As Murphy said – you cannot do just one thing. Thanks to JDK 1.4 entering end-of-life support period (and 1.3 being unsupported for some time), we need to move few applications happily running on these antique Java platforms to at least Java 5 and gain few more years of uninterrupted Sun-supported life.
Upgrading the applications was as expected, piece of cake – as long as you have solid build environment with build scripts and everything – which we certainly do. Ant runs on Java5 exactly as well as on 1.4 (maybe faster, but I did not measure), Tomcat was the same no-brainer thing and switching the Eclipse required only selecting different JRE in preferences window. Codewise, the only change required inside the application code was renaming few identifiers to something else than enum, which happens to be keyword in Java 5.
So far so good. What was not so easy were third party libraries. Our applications are using Jakarta Commons, Struts, Torque and few other great pieces of open source software. I like to compile everything from source, so I tried to rebuild 3rd party jar’s. No luck – about one third did not compile (I have to remind you that these were versions from late 2001/early 2002). Unlike with our own code, it would make little sense try to change the 3rd party code – certainly not when there are many newer versions for each of the components already available anyway. Therefore – upgrading components is way to go. And here the fun begins.
The idea behind components such as Jakarta Commons is that they should be used in other components. And indeed – they are used. With little luck and enough components, you will end up with one jar being used/required by two, three or more other jars and over time, nice Web of inter-dependencies will somehow grow inside your little app. Nothing wrong with that. The trouble is that as the libraries mature and evolve, new versions are added, which add new features. In this process, sometimes the component’s APIs are changed, some features are deprecated and eventually removed – and backwards compatibility slowly disappears. Because the different open source projects are evolving with various speed, you will eventually end up with requirements of two different and incompatible versions of the same component at the same time. This is usually not an issue when you are developing new project, because the time span is too short so that the differences in required versions are not very far apart. It does however cause problems when you are upgrading and working with enough components over a time span of several years.
There are two possible strategies: many small steps or one big jump. The small steps means that you try to upgrade as little as possible, keeping the changes in API minimal – just to achieve the goal (compilable with Java 5 in our case). The problem with this approach is that it is quite time consuming process, because of the cascading updates. Imagine e.g, that upgrading A1 to A2 requires upgrade of B1 to B2 (because A depends on B), but because B depends on C, you need also upgrade C1 to C2. And because the X also depends on B, after upgrading to B2 (unless you are lucky) you may be forced to upgrade X as well . It iterative “snowball” process and easily can take much longer than expected. Another problem is that after spending all that time, you have application that is still running on out-of-date components. Whether this is a problem or not, depends on number and size of business changes required.
The big jump strategy is to get the latest versions of everything. Chances are very high that it will compile and run OK – as long as all components are still active projects. The typical catch is that you have just updated the B1 to B7 and this is fine with all components except X, which was kind of dormant inactive project for last 3 years and still requires B3 as dependency. If the API of B3 and B7 differ enough, you may not be able simply use B7 without diving into unknown code and fixing it. The other catch is that your own application may be incompatible with latest components API of B7 and needs changes. This is better situation because you a) know the code b) it may be better code (if you did a good job) than the code of component X – but in both cases you may need to spend time on something that has no direct link to what you wanted to do in a first place: doing just that one thing 🙂