Friday 25 June 2010

Problems with Eclipse, Eclipse-CS and JDK 1.5

I've just spent 2 days reinstalling my machine, with the worst part being the following problem. Since I didn't find any good Google hits, I thought I'd recount the problem and fix for others to follow.

My main goal was the upgrade of my OpenGamma work Windows 7 PC from a hard disk to an SSD. As part of this, I was joining a Windows Domain. As such, I ended up reinstalling Windows 7 from scratch and recreating my entire work environment. The PC in question is 64 bit, which was key to the problems.

After the fresh install of Windows 7, I reinstalled JDK 1.5, then JDK 1.6. Only then did I install Eclipse (3.5). When I tried starting Eclipse, it failed immediately, displaying its command line arguments.

To fix this strange error (which I'd not seen before) I changed the eclipse.ini OSGI -Dosgi.requiredJavaVersion=1.5 line to 1.6. This change got Eclipse up and running.

I installed all the Eclipse plugins I needed including Eclipse-CS for Checkstyle.

When I then imported our source code I got an UnsupportedClassVersionError referencing a number of seemingly random files. The problem marker referred to the whole file with a lovely Eclipse red cross. The only reference to checkstyle was the problem type of 'Checkstyle problem'.

By uninstalling all the checkstyle setup, I got everything to compile. So it was definitely something to do with Eclipse-CS or Checkstyle.

Some Googling did reveal that the cause for the random files was that those files contained a Javadoc @throws clause referring to a custom exception class (not a JDK exception class). So, there was something in Eclipse-CS or Checkstyle that couldn't cope with this, and it had something to do with an invalid Java version. (UnsupportedClassVersionError refers to a class that was compiled under one version of the JDK and is then being run under an earlier version)

But, I had no idea what that issue was, or how to solve it. Or why it was different on this new install as opposed to my old install.

This morning I decided to go back and think about the first problem that I'd worked around - the failure of Eclipse to startup normally. I did java -version on each of the four Java installs, JDK 1.5 and 1.6 on each of the old and new disks. On the old hard disk, both were 64 bit JDK installs. On the new SSD, JDK 1.6 was 64 bit, but JDK 1.5 was 32 bit.

So, I uninstalled the two SSD JDKs, and reinstalled them, both as 64 bit. I also needed to install the browser JRE separately as that needed a 32 bit JRE.

Eclipse now started correctly (with either the OSGI 1.5 or 1.6 setting) and the Eclipse-CS/Checkstyle problem went away.

I've still no idea why the difference between a 32 and 64 bit JVM should cause an UnsupportedClassVersionError though. I've always thought that the bytecode format was independent of 32 vs 64 bit, yet the error would appear to indicate otherwise.

At least it is sorted now. The newly installed SSD drive is going to have to be super-quick for the two day loss of productivity though!

Thursday 17 June 2010

Exception transparency and Lone-Throws

The Project Lambda mailing list has been considering exception transparency recently. My fear with the proposal in this area is that the current proposal goes beyond what Java's complexity budget will allow. So, I proposed an alternative.

Exception transparency

Exception transparency is all about checked exceptions and how to handle them around a closure/lambda.

Firstly, its important to note that closures are a common feature in other programming languages. As such, it would be a standard approach to look elsewhere to see how this is handled. However, checked exceptions are a uniquely Java feature, so this approach doesn't help.

Within Neal Gafter's BGGA and CFJ proposals, and referenced by the original FCM proposal is the concept and solution for exception transparency. First lets look at the problem:

Consider a method that takes a closure and a list, and processes each item in the list using the closure. For out example, we have a conversion library method (often called map) that transforms an input list to an output list:

  // library method
  public static <I, O> List<O> convert(List<I> list, #O(I) block) {
    List<O> out = new ArrayList<O>();
    for (I in : list) {
      O converted = block.(in);
      out.add(converted);
    }
    return out;
  }
  // user code
  List<File> files = ...
  #String(File) block = #(File file) {
    return file.getCanonicalPath();
  };
  List<String> paths = convert(list, block);

However, this code won't work as expected unless we specially handle it in closures. This is because the method getCanonicalPath can throw an IOException.

The problem of exception transparency is how to transparently pass the exception, thrown by the user supplied closure, back to the surrounding user code. In other words, we don't want the library method to absorb the IOException, or wrap it in a RuntimeException.

Project Lambda approach

The approach of Project Lambda is modelled on Neal Gafter's work. This approach adds addition type information to the closure to specify what checked exceptions can be thrown:

  // library method
  public static <I, O, throws E> List<O> convert(List<I> list, #O(I)(throws E) block) throws E {
    List<O> out = new ArrayList<O>();
    for (I in : list) {
      O converted = block.(in);
      out.add(converted);
    }
    return out;
  }
  // user code
  List<File> files = ...
  #String(File)(throws IOException) block = #(File file) {
    return file.getCanonicalPath();
  };
  List<String> paths = convert(list, block);

Notice how more generic type information was added - throws E. In the library method, this is specified at least three time - once in the generic declaration, once in the function type of the block and once on the method itself. In short, throws E says "throws zero-to-many exceptions where checked exceptions must follow standard rules".

However, the user code also changed. We had to add the (throws IOException) clause to the function type. This actually locks in the exception that will be thrown, and allows checked exceptions to continue to work. This creates the mouthful #String(File)(throws IOException).

It has recently been noted that syntax doesn't matter yet in Project Lambda. However, here is a case where there is effectively a minimum syntax pain. No matter how you rearrange the elements, and what symbols you use, the IOException element needs to be present.

On the Project Lambda mailing list I have argued that the syntax pain here is inevitable and unavoidable with this approach to exception transparency. And I've gone further to argue that this syntax goes beyond what Java can handle. (Imagine some of these declarations with more than one block passed to the library method, or with wildcards!!!)

Lone throws approach

As a result of the difficulties above, I have proposed an alternative - lone-throws.

The lone-throws approach has three elements:

  1. Any method may have a throws keyword without specifying the types that are thrown ("lone-throws"). This indicates that any exception, checked or unchecked may be thrown. Once thrown in this manner, any checked exception flows up the stack in an unchecked manner.
  2. Any catch clause may have a throws keyword after the catch. This indicates that any exception may be caught, even if the exception isn't known to be thrown by the try block.
  3. All closures are implicitly declared with lone throws. Thus, all closures can throw checked and unchecked exceptions without declaring the checked ones.

Here is the same example from above:

  // library method
  public static <I, O> List<O> convert(List<I> list, #O(I) block) {
    List<O> out = new ArrayList<O>();
    for (I in : list) {
      O converted = block.(in);
      out.add(converted);
    }
    return out;
  }
  // user code
  List<File> files = ...
  #String(File) block = #(File file) {
    return file.getCanonicalPath();
  };
  List<String> paths = convert(list, block);

If you compare this example to the very first one, it can be seen that it is identical. Personally, I'd describe that as true exception transparency (as opposed to the multiple declarations of generics required in the Project Lambda approach.)

It works, because the closure block automatically declares the lone-throws. This allows all exceptions, checked or unchecked to escape. These flow freely through the library method and back to the user code. (Checked exceptions only exist in the compiler, so this has no impact on the JVM)

The user may choose to catch the IOException, however they won't be forced to. In this sense, the IOException has become equivalent to a runtime exception because it was wrapped in a closure. The code to catch it is as follows:

  try {
    paths = convert(list, block);  // might throw IOException via lone-throws
  } catch throws (IOException ex) {
    // handle as normal - if you throw it, it is checked again
  }

The simplicity of the approach in syntax terms should be clear - it just works. However, the downside is the impact on checked exceptions.

Checked exceptions have both supporters and detractors in the Java community. However, all must accept that given projects like Spring avoiding checked exceptions, their role has been reduced. It is also widely known that other newer programming languages are not adopting the concept of checked exceptions.

In essence, this proposal provides a means for the new reality where checked exceptions are less important to be accepted. Any developer may use the lone-throws concept to convert checked exceptions to unchecked ones. They may also use the catch-throws concept to catch the exceptions that would otherwise be uncatchable.

This may seem radical, however with the growing integration of non-Java JVM languages, the problem of being unable to catch checked exceptions is fast approaching. (Many of those languages throw Java checked exceptions in an unchecked manner.) As such, the catch-throws clause is a useful language change on its own.

Finally, I spent a couple of hours tonight implementing the lone-throws and catch-throws parts. It took less than 2 hours - this is an easy change to specify and implement.

Summary

Overall, this is a tale of two approaches to a common problem - passing checked exceptions transparently from inside to outside a closure. The Project Lambda approach preserves full type-information and safeguards checked exceptions at the cost of horribly verbose and complex syntax. The lone-throws approach side-steps the problem by converting checked exceptions to unchecked, with less type-information as a result, but far simpler syntax. (The mailing list has discussed other possible alternatives, however these two are the best developed options.)

Can Java really stand the excess syntax of the Project Lambda approach?
Or is the lone-throws approach too radical around checked exceptions?
Which is the lesser evil?

Feedback welcome!