2009-08-05

Using plain old classpath with Maven and Surefire

Because of the particular nature of our product, I have some big restrictions on class loading. Specifically, since I have my own specialized system class loader, the surefire isolated class loader is a non-starter.

Diagnosing the following problem took way too long: the documented useManifestOnlyJar property of the surefire plugin does not work in surefire 2.4.2, which is what my install of Maven2 picked up by by default.

Since there are some potentially confusing interaction between surefire configuration properties, I include here the entire block you will need in your pom.xml to use "plain old Java class paths":
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.3</version>
<configuration>
<forkMode>once</forkMode>
<useManifestOnlyJar>false</useManifestOnlyJar>
<useSystemClassLoader>true</useSystemClassLoader>
</configuration>
</plugin>
...
</plugins>
<build>

In this case, I ran maven with maximal verbosity (mvn -X test [yadda yadda]) and eventually noticed:
[DEBUG]   (f) useSystemClassLoader = true
...but no line talking about useManifestOnlyJar. Did I spell it wrong? Is it not recognized? Is it a surefire bug? (yes.)

Now that I have the classpath working, the next step is to figure out why useSystemClassLoader is being ignored. What?
System.out.println("CL: " + this.getClass().getClassLoader().toString());
says:
Running com.kabira.iotest.AddressTest
CL: org.apache.maven.surefire.booter.IsolatedClassLoader@7051630a

Sigh.

Documentation for surefire plugin is here.

2008-10-27

Java: Do 'final' fields really exist?

A while back, I was bit by an interesting Java optimization. Most likely (oh who am I kidding, 'certainly') it is only interesting to me, but I'll share anyway.

Consider the following class.
class T
{
public final int i = 3;
public int getFinal()
{
return i;
}
}
Ignoring the rest of the class, the implementation of getFinal seems predictable enough - a "getfield" and an "ireturn", right?

When we examine the bytecodes for getFinal, however:

% javap -verbose T
...
public int getFinal();
Code:
Stack=1, Locals=1, Args_size=1
0: iconst_3
1: ireturn
LineNumberTable:
line 6: 0


Notice that there is no getfield. The compiler has skipped the field definition altogether and replaced "i" with the compile-time constant value.

This is one of those rare cases that the Java compiler, rather than the VM, does the optimization.

Is this optimization just a property of the class where the final field was defined? Let's see:

class Q
{
public int getFinalFromT(T t)
{
return t.i;
}
}


which compiles to:

public int getFinalFromT(T);
Code:
Stack=1, Locals=2, Args_size=2
0: aload_1
1: invokevirtual #13; //Method java/lang/Object.getClass:()Ljava/lang/Class;
4: pop
5: iconst_3
6: ireturn
LineNumberTable:
line 5: 0


Now this is where it gets really interesting! The call to getClass() will cause T to be loaded and resolved, if it wasn't already. But the result of the load will be thrown away (pop at position 4), and the constant value (which is known whether the class can be resolved or not) is returned immediately. This is my favorite kind of code archaeology: you can see evidence of the VM spec being carefully followed even when the functional consequences are very subtle.

By the way, the optimization is guaranteed to provide the correct result, given the semantics of a 'final' field and its initialization. So, no complaints there.

Unless...say, you were replacing getfield instructions at class load time. You might be surprised to find that the "final" fields avoid your getfield filter. The transformation is a lossy, or at least "non-reversible" optimization: the class loader cannot determine from the byte stream that the compiler skipped the field access. The following two implementations of T.getFinal() generate indistinguishable byte streams:
final int i = 3;
public int getFinal()
{
return this.i;
}
// results in the same bytecodes as...
public int getFinal()
{
return 3;
}

Can this optimization be avoided? Yep. Consider:
class R
{
final int i;
R()
{
i = 4;
}

public int getFinal()
{
return i;
}
}


...which results in the following, instrumentation-compatible code:

public int getFinal();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: getfield #12; //Field i:I
4: ireturn
LineNumberTable:
line 11: 0