tags:

views:

196

answers:

5

Our application needs to support 1.5 and 1.6 JVMs. The 1.5 support needs to stay clear of any 1.6 JRE dependencies, whereas the 1.6 support needs to exploit 1.6-only features.

When we change our Eclipse project to use a 1.5 JRE, we get all the dependencies flagged as errors. This is useful to see where our dependencies are, but is not useful for plain development. Committing source with such compile errors also feels wrong.

What are the best practices for this kind of multi JRE version support?

In C land, we had #ifdef compiler directives to solve such things fairly cleanly. What's the cleanest Java equivalent?

+3  A: 

If your software must run on both JRE 1.5 and 1.6, then why don't you just develop for 1.5 only? Is there a reason why you absolutely need to use features that are only available in Java 6? Are there no third-party libraries that run on Java 1.5 that contain equivalents for the 1.6-only features that you want to use?

Maintaining two code bases, keeping them in sync etc. is a lot of work and is probably not worth the effort compared to what you gain.

Java ofcourse has no preprocessor, so you can't (easily) do conditional compilation like you can do in C with preprocessor directives.

It depends ofcourse on how big your project is, but I'd say: don't do it, use Java 5 features only, and if there are Java 6 specific things you think you need, then look for third-party libraries that run on Java 5 that implement those things (or even write it yourself - in the long run, that might be less work than trying to maintain two code bases).

Jesper
Exactly what I was going to say. Just develop against 1.5 and don't use any deprecated APIs or features.
cletus
Nothing useful was introduced in 1.6???
Tom Hawtin - tackline
Tom that's a separate question, isn't it? If their application isn't using anything specific to 1.6 then they can save a lot of time by just targetting 1.5.
matt b
+1  A: 

There are a few approaches you could use:

  • Compile against 1.6 and use testing to ensure functionality degrades gracefully; this is a process I've worked with on commercial products (1.4 target with 1.3 compatibility)
  • Use version-specific plugins and use the runtime to determine which to load; this requires some sort of plugin framework
  • Compile against 1.5 and use reflection to invoke 1.6 functionality; I would avoid this because of the added complexity over the first approach at reduced performance

In all cases, you'll want to isolate the functionality and ensure the generated class files have a version of 49.0 (compile with a 1.5 target). You can use reflection to determine method/feature availability when initializing your façade classes.

McDowell
+1  A: 

Compile most of your code as 1.5. Have a separate source directory for 1.6-specific code. The 1.6 source should depend upon the 1.5, but not vice-versa. Interfacing to the 1.6 code should be done by subtyping types from the 1.5 code. The 1.5 code may have an alternative implementation rather than checking for null everywhere.

Use a single piece of reflection once to attempt to load an instance of a root 1.6 class. The root class should check that it is running on 1.6 before allowing an instance to be created (I suggest both using -target 1.6` and using a 1.6 only method in a static initialiser).

Tom Hawtin - tackline
A: 

You could use your source control to help you a little if it does branching easily (git, svn or Perforce would work great). You could have two branches of code, the 1.5 only branch and then a 1.6 branch that branches off of the 1.5 line.

With this you can develop in 1.5 on the 1.5 branch and then merge your changes/bugfixes into the 1.6 branch as needed and then do any code upgrades for specific 1.6 needs.

When you need to release code you build it from whichever branch is required.

For your Eclipse, you can either maintain two workspaces, one for each branch or you can just have two sets of projects, one for each branch, though you will need to have different project names which can be a pain. I would recommend the workspaces approach (though it has its own pains).

You can then specify the required JVM version for each project/workspace as needed.

Hope this helps.

(Added: this would also make for an easy transition at such time when you no longer need the 1.5 support, you just close down that branch and start working only in the 1.6 branch)

cjstehno
A: 

One option would be to break the code into 3 projects.

One project would contain common stuff which would work on either version of java.

One project would contain the java6 implementations and would depend on the common project.

One project would contain the java5 implementations and would depend on the common project.

Breaking things into interfaces with implementations that implement those interfaces, you could eliminate any build dependencies. You would almost certainly need dependency injection of one kind or another to help wire your concrete classes together.

Working in Eclipse, you could set the java6 project to target java6, and the other 2 projects to target java5. By selecting the JRE on a project by project basis, you'd show up any dependencies you missed.

By getting a little clever with your build files, you could build the common bit both ways, and depend on the correct version, for deployment - though I'm not sure this would bring much benefit.

You would end up with two separate versions of your application - one for java6, one for java5.

Bill Michell