What not to do at an Airport ?
Easy… arrive 5 hours after the plane takes off on New Years Eve… in Sydney… bound for London with no flights going out :). That’s what I did, oops. My family, who were with me were not that happy, but the consolation was we saw the fireworks on Sydney Harbour Bridge.
How did it happen ? An SMS was sent from the carrier (Singapore Air) telling me the flight time had changed, what it didn’t tell me was that this was the connecting flight from Singapore to London, and I didn’t check. Perhaps they should think through their UX a bit more and the impact of a small screen on the information they are trying to deliver. I will check next time.
What happened next? Well I was expecting a complete nightmare, but although this was entirely my fault (so I had no right to rant, and didn’t), and there were no seats available until 12 days later, a very nice bloke at the Sydney Singapore Air office (Davin he said his name was) got us all onto a flight 24 hours later, and we didn’t have to wait on standby at the airport. I guess some lucky person got bumped up to business to make space for us. 24 hours later, having done some work, watched a few movies and one embedded Linux reboot from the onboard video system (A380’s have 1 CPU with a full IP stack and Ethernet port per seat running a Linux kerne and every seat has 110v, if only I had a network cable) we arrived in London, tired, cold but very grateful to Singapore Air. The only shock was the temperature difference. 30C.
Apple Mail Sync Hang
Sometimes when offline I have noticed Apple Mail hang, especially on slow connections with lots of latency. It appears that some ADSL providers in Australia route their US bound traffic via Japan, giving high packet latencies over the Pacific (400ms), so IMAP to GMail is slower than normal. If you have been working offline for some time, Apple Mail will store your operations in an offline cache in ~/Library/Mail/IMAP-XXXX/.OfflineCache where the XXXX is the account name. In this folder are normally lots numbered files, and an operations file. The numbered files are emails messages, and the operations file is a redo log written in binary format referencing the numbered file. When Mail comes back online it will replay the operations file.
So, if you have a slow connection, and you see that an account (via the Activity Window) is blocking with some operation, then if everything else is OK (ie the connection) you can clear this operation by selectively removing the numbered file causing the block.
Obviously this is dangerous advice, as I can give no guarantees that you will be able to work out which file you should delete or if the operation that you are invalidating will be the right one, or what will happen next :), but it worked for me with an unintentional operation on a huge email with masses of attachments that would not allow the account to go online on a 6KB/s upstream link. So use with caution as a last resort when all else fails and you are prepared for all disasters ![]()
Adding a Branch to Git
One of the benefits of Git is that its easier to manage and merge in multiple branches. You can do this with a svn git repo by pulling the svn branches into the git repo to perform merge operations locally, before committing back to the svn branch. Normally you might have pulled the whole tree from svn including the branches and tags with git svn clone http://myrepo.com/svn -T trunk -b branches -t tags but what do you do if you only took out the trunk and want to add the branch in.
In the git repo there is a file .git/config that contains a list of repote repos including the remote svn repos. git svn manages its operation by having remote branches in the git repo, you can see them with git branch -a but whatever you do dont touch any of the svn branches in your git repo.
So we have a git repo and we want to add a single branch in to be able to merge into that branch. Here is how.
1. Edit the .git/config file to add another remote repo
I am using the Apache Shindig repo, which I just did a branch on…. which is why I need to get it into my git repo.
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [svn-remote "svn"] url = https://svn.apache.org/repos/asf/incubator/shindig/trunk fetch = :refs/remotes/git-svn
I then add the new branch so my .git/config looks like this:
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [svn-remote "svn"] url = https://svn.apache.org/repos/asf/incubator/shindig/trunk fetch = :refs/remotes/git-svn [svn-remote "svn081x"] url = https://svn.apache.org/repos/asf/incubator/shindig/branches/0.8.1-x fetch = :refs/remotes/git-svn-0.8.1-x
This adds the branch svn081x to my git repo, as a svn-remote and points it to the svn branch 0.8.1-x. It also gives it a fetch reference that is unique. At this point the git repo doesnt have anything in that branch so we need to fetch the branch, but we need to give git a bit of help at this point. We need to give it a revision on the branch so it can walk back down the branch to find the branch point. The revision of branch would also be Ok. svn log –stop-on-copy https://svn.apache.org/repos/asf/incubator/shindig/branches/0.8.1-x is going to help here as this will list all the commits to svn on the branch.
2. Fetch the branch
git svn fetch svn081x -r719438
svn081x is the name of the remote branch in the .git/config and the revision is on the branch and close to the branch point. If you chose a revision that is not on the branch, you will get a url not found error from svn. If this works you will see the files in the branch being added to your local repo.
At this point you can see the branches you have with a
x43543:trunk ieb$ git branch -a * master git-svn git-svn-0.8.1-x
Which shows the remote branches and the master which is active. Dont start working on the git-svn-0.8.1-x branch as its a remote branch that you should not touch. We need to create a git branch that is based on the remote first.
3. Create a branch from the remote branch
git checkout git-svn-0.8.1-x git checkout -b master-0.8.1-x
The first switched to the git-svn-0.8.1.x branch and the second creates a new branch from the current branch called master-0.8.1-x. So we now have a working branch called master-0.8.1-x on which to work.
x43543:trunk ieb$ git branch master * master-0.8.1-x
git checkout master will switch the branch back to the trunk, and git checkout master-0.8.1-x will switch back to the branch. Now you can merge to your hearts content and git svn dcommit back to the svn repo when you are happy with the results.
Unit Test and Constructor IoC
I might have said it before but, Constructor IoC has 2 big advantages over setter and getters.
- The IoC all happens in one shot, so there are no issues about a class being half configured, its always thread safe from the start.
- When doing writing Unit tests is really clear what is needed to make a class function
- Less code.
That was 3… oops… bloat ![]()
Increasing Code Coverage
One of the problems for developers, is that the honest ones want to increase their code coverage and unit tests, but dont really know how much of their code isnt tested. There are solutions to this. You can add a coverage report to maven and build a site, or you can use an eclipse plugin, which is more interactive. http://www.eclemma.org/ works well inside eclipse and gives good information about the lines being covered. The temptation with this evidence that code is covered or not, is to chase the % coverage up, by devising unreal ways of exercising the code. This should be resisted at all costs, as it will give a false sense of security, but it would be get to the point where the last 5% of coverage is all there is to worry about.
Performin a Mvn Release (Notes)
Maven perorms a release by editing the pom files and replacing the version numbers, it then commits the edited files after a build. Having doen that it re-edits the poms to the new trunk, and commits again.
The standard release plugin configuration does a mvn clean verify, which can fail if you have plugins that depend on artifacts that should have been added to the local repo. You can change this by either doing a mvn clean install after the failed mvn release:prepare, or re-configure the goals for the release plugin. (see below).
mvn release:perform only checks out the tag, and runs the perform goals usually deploy site-deploy, so you can do this step manually with a checkout and a mvn deploy.
The other thing that the mvn release:prepare does is edit all the svn urls to match the new destination of the tag, so when doing a branch its probably best to use mvn release:branch -DbranchName=kernel-1.0.x to perform the branch as this will keep all the svn urls in place.
The final thing on the release, where no SVN urls are specified in the pom, the fielsystem path from the last pom that did have the svn urls specified, MUST match the the path in SVN otherwise the tagging/branching/committing will get lost and muck it all up. I find it easier just to spevify the location in svn in each pom.
So, here is how to re-config the goals used to verify a tag, I have added install because I have an assembly project in the build that needs things in the local repo.
<build> <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <configuration> <preparationGoals>clean install verify</preparationGoals> </configuration> </plugin>
</plugins> </build>
OSGi version resolution
OSGi version resolution looks great, but there is a problem that I am encountering. I can deploy more than one version of a jar as seperate OSGi bundles, but unless the consuming bundle was explicity written to specify a range of versions that bundle will not start.
Bundle A was built with a dependency on package org.apache.commons.useful;version=1.0.2, which means any version > 1.0.2.
I deploy bundle B with org.apache.commons.useful version 1.0.4, great no problem.
Bundle C depends on org.apache.commons.usefull;version=1.0.8,
So I deploy bundle D with 1.0.8, great for Bundle C, but now Bundle A can see 1.0.4 and 1.0.8 and wont start, due to a constraint violation.(at least in Felix).
To make this work I have to go back to bundle A, and rebuild it with version=”[1.0.2,1.0.8)” meaning “I depend on any version >= 1.0.2 and < 1.0.8|. Thats all fine, but, now every time I add a second version of a jar to OSGi, I have to go back and repackage all the bundles that dont have precise enough version numbers.
You might argue 1.0.4 and 1.0.8 are equivalent so dont deploy bundle B anymore, but the same problem happens when you try and deploy bundles container 2.0.6 and 2.5.5 when 2.5.5 is not fully backwards compatable.
Worse, this also means if I took any 3rd party bundles without precise version ranges, I will have to work out how to rebuild them. Not exactly and inviting task when you have > 100 bundles in a container. Makes me wonder if its worth all the hassle.
Classloader Magic
OSGi has classloader magic, although its not really that much magic. Once into a runtime of a JVM a class is an instance of the class information bound to the classloader that instanced that. So when binding to the class, its both the classloader and the class that identifies this instance. All those ClassCastExceptions that everyone shouts out… “you stupid program .. those are the same class ” … only to realise some time later that you were the stupid one. Perhapse ClassCastExceptions have the wrong name since they really talk about the class instance rather than class type although that may just be becuase many developers think of class as type.
So there is no classloader magic, all that OSGi classloaders do it to work together though a shared registry of resolver instances of specific versions of class types. Having don’t that, the class instance becomes available.
The shared space that is normally used by the typical parent classloader as seen in tomcat applications becomes a slightly more intelligent classloader that maintains a virtual shared space of exported packages. The interesting part witll be to see if its possible to use this same type of classloader as the basis for a webapplication context, then the shared space will become a window into a group of OSGi classloaders each managing their own set of internal class instances and exported class instances.
How Shindig Works.
Here is a great discription of the internals of Shindig, http://sites.google.com/site/opensocialarticles/Home/shindig-rest-java. Well worth a read for the technically minded.
Linear Classloaders and OSGi
OSGi does not remove all classloader problems as can be seen from http://www.osgi.org/blog/2008/07/qvo-vadimus.html and http://www.osgi.org/blog/2007/06/osgi-and-hibernate.html where the Peter Kriens notes that
“Hibernate manipulates the classpath, and programs like that usually do not work well together with OSGi based systems. The reason is that in many systems the class visibility between modules is more or less unrestricted. In OSGi frameworks, the classpath is well defined and restricted. This gives us a lot of good features but it also gives us pain when we want to use a library that has aspirations to become a classloader when it grows up.”