Is your project ready for Java 7?

Java 7 has been released last year, but how many of you use it in production or even in development environment? Have you even tried it at home? Why not?

Well, I’ll leave all this questions to the reader as in this post I would like to discuss issues that one might encounter when trying to upgrade existing project to Java 7.

Here is the list of questions I want you to ask yourself about your project:

  • Will it compile?
  • Will it run?
  • Will it execute with the same runtime semantics, i.e. will it run as before?

Some of the questions might sound silly but all they come from my attempt to upgrade existing project from Java 6 to Java 7.

Oracle has released interesting document Java SE 7 and JDK 7 Compatibility. This document defines Binary, Source and Behavioral compatibility policies as well as list exceptions to defined compatibility rules. What is interesting is that it defines incompatibilities between Java SE 7 and Java SE 6 as such:

Java SE 7 is strongly compatible with previous versions of the Java platform. Almost all existing programs should run on Java SE 7 without modification. However, there are some minor potential source and binary incompatibilities in the JRE and JDK that involve rare circumstances and “corner cases” that are documented here for completeness.

Compiling it

Back to our questions, so how can project not compile with Java 7 compiler (here I assume using -source=1.7 -target=1.7 compiler options)? The answer is source incompatibility which with Java 7 can have surprising incarnations. For example consider the following class:

class MessageService {
  String getMessage(String msgKey, Object...args);
  String getMessage(String msgKey, boolean includeKey, Object...args);
}

And it’s usage by some client class:

  messageService.getMessage("MSG108", false, arg1, arg2, arg3);

Here client attempts to invoke second method which allows him control whether or not message will have message prefix added. In this particular example client explicitly requests that he wants only actual message with prefix.

This code is perfectly OK for Java 6, but does not compile on Java 7. When I first saw it I went as far as to create bug report for Oracle (see 7115209), but as was expected Oracle closed the issue as “not a bug”. They provided explanation that this behavior is according to JLS and pointed me to the Java SE 7 and JDK 7 Compatibility document for explanation. And indeed this document contains issue named “Changes in Most Specific Varargs Method Selection” which describes in great detail the rationale behind the changes and use-cases.

So how can we fix code so it compiles. There are 2 possible solutions:

  1. Fix MessageService class definition so that client code compiles
  2. Change MessageService API and all clients that are using it (for example by creating two different methods getMessage and getMessageWithoutKey)

Which is the best approach depends on whether or not you have access to client code. In my case I didn’t have such luxury so I had to go with solution 1 which resulted in the following definition of the MessageService class:

class MessageService {
  String getMessage(String msgKey, Object...args);
  String getMessage(String msgKey, Boolean includeKey, Object...args);
}

As you can see the only different from the original declaration is that parameter includeKey is declared as Boolean instead of boolean. This is pretty cheap fix and will leave users of your class happy as they can continue using old API!

Running it

OK, so what about second question: “Will it run?”. The answer to this question basically depends on whether or not the libraries used in the project are ready for Java 7. This is especially true for libraries that manipulate bytecode. As described in the document in the section “Verification of Version 51.0 Class Files”:

Classfiles with version number 51 are exclusively verified using the type-checking verifier, and thus the methods must have StackMapTable attributes when appropriate. For classfiles with version 50, the Hotspot JVM would (and continues to) failover to the type-inferencing verifier if the stackmaps in the file were missing or incorrect. This failover behavior does not occur for classfiles with version 51 (the default version for Java SE 7).
Any tool that modifies bytecode in a version 51 classfile must be sure to update the stackmap information to be consistent with the bytecode in order to pass verification.

And that exactly what I observed with AspectJ library version 1.6.11 which failed at startup with the “java.lang.VerifyError: Expecting a stackmap frame at branch target 26 in method …“.

However luckily for me upgrade to version 1.6.12 solved the issue. The moral here is that you should check libraries that are in use within your project! 😉

Checking results

Last but not least question about runtime semantics. Surprisingly I came across an issue when running unit tests as some of the tests started to fail on Java 7 but are fine on Java 6. The answer to this behavior could also found in the document, section “Order of Methods returned by Class.get Methods can Vary”:

In JDK 7, build 129, the following reflective operations in java.lang.Class changed the fixed order in which they return the methods and constructors of a class:

getMethods
getDeclaredMethods
getDeclaredConstructors

This may cause issues for code that assumes (contrary to the specification) a particular order for methods or constructors.

So with this change the order of tests execution changed and as the result showed actual problem with my code – i.e. dependencies between tests. Which is btw an anti-pattern as defined in the book by Gerard Meszaros xUnit Test Patterns: Refactoring Test Code. Thus I was alble to fix actual problem looking at the symptoms! 🙂

Moral of this story: be ready to spend some effort when upgrading to Java 7. It is better to start early and prepare infrastructure without conducting actual update. For instance I started using Java 7 on the CI server which actually showed all the issues listed above and now I’m pretty certain that actual switch from Java 6 to Java 7 will be smooth.

I hope this information will be useful for some of you. And I’m waiting for your feedback!


Updated 2012-07-18:

Beware that JDK 7 uses new version of JAXB. As of this writing latest JDK version from Oracle is 1.7.0_05 which is shipped with the JAXB 2.2.4u1.

This version of JAXB contains a bug JAXB-871 which leads to broken serialization of the objects (if base class property is overridden by any single class, it won’t be delivered to all other sub-classes of the base class sub-classes)!!!

The workaround for this issue is to use standalone version of JAXB that does not have it. For example one can use version 2.2.3u2. Just add jars into classpath of your application!

Note: In order to find out which version of the JAXB and JAX-WS is in your JDK installation run the following commands:

jdk_home/bin/wsgen -version
jdk_home/bin/xjc -version

Here is sample output:

c:\Program Files\Java\jdk1.7.0_05\bin>wsgen -version
JAX-WS RI 2.2.4-b01

c:\Program Files\Java\jdk1.7.0_05\bin>xjc -version
xjc 2.2.4


4 responses to “Is your project ready for Java 7?

  • dgesino

    Hi Dimitri,

    this article was interesting and pragmatic.
    I have been playing around with Java 7 for a while to test the new features, but we’ve never made the “big jump”.
    In the language I like the Autocloesable feature, and the new File API: just a 10 minutes play is productive, you can write something in a text file without opening 100000 XXXXXXOutputStreams.
    Then I’ve read a bit about the “invokeDynamic” and is seems a promising feature for the implementation of dynamic languages on the JVM: I’ve seen JRuby 1.7 is going to use it extensively Java 7 in a kind of pluggabile way,and the performance tests look promising.
    I think that in Java 7 there’s more than we would expect.
    Actually in the main project I work on we’ve just jumped to Java6, as a product we depend on has bugs on Java 6, so we were stuck to the “End of life” Java 6.

  • Ouch…multi-catch « Ideas Not For Sale

    […] my previous post on Java 7 I’ve outlined some problems one might face when converting project to use it. Here […]

  • Manuel

    Hello Dmitry,
    interesting article,
    we are upgrading our application to Java 7,
    we’re currently using Java 1.5, it looks like it’s going to be a big effort in order to achieve the upgrade, what worries me the most is that the libraries/tools used in our project are very old versions…
    like jaxb, we’re currently using version 1.3
    hope we don’t find to many incompatibilities

    thnaks for the info!
    Regards

Leave a reply to Manuel Cancel reply

Latency Tip Of The Day

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)

Psychosomatic, Lobotomy, Saw

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)

Blog-City.com

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)

Mechanical Sympathy

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)