7/15/2010

Oh, this Inner classes!

Hi! There was a long time since my last post. Actually, there is a lot of work with my current project now. And today I would like to tell you some interesting fact I encountered yesterday in our java source-code.

So, I were in the middle of my battle with our JUnit test (hope I'll find time to write about it) when saw this thing. Imagine that you have the following code (this is not a real piece of code, ye, ye - NDA, you know):

public class BrokenSingleton {
// ---- Thread Safe Singleton pattern -----
static class InstanceHolder {
static final BrokenSingleton instance = new BrokenSingleton();
}

public static BrokenSingleton getInstance() {
return InstanceHolder.instance;
}

private BrokenSingleton() {
// Do your initialization here
}
}

despite of the fact that this is not only 'Thread Safe' but also 'Lazy Init' Singleton, the strange thing is... Actually, what do you think, how many *.classes files there should be? Yes, you right, TWO:

denlion@denlion-laptop:~/workspace/For Test/bin$ ls -al
total 16
drwxr-xr-x 2 denlion denlion 4096 2010-07-15 22:58 .
drwxr-xr-x 5 denlion denlion 4096 2010-06-29 22:06 ..
-rw-r--r-- 1 denlion denlion 544 2010-07-15 22:57 BrokenSingleton.class
-rw-r--r-- 1 denlion denlion 524 2010-07-15 22:57 BrokenSingleton$InstanceHolder.class
denlion@denlion-laptop:~/workspace/For Test/bin$

if you are using the Eclipse compiler. Otherwise, if you use something else:

denlion@denlion-laptop:~/workspace/For Test/bin$ java -version
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8) (6b18-1.8-0ubuntu1)
OpenJDK Server VM (build 14.0-b16, mixed mode)
denlion@denlion-laptop:~/workspace/For Test/bin$

you'll see something different:

denlion@denlion-laptop:~/workspace/For Test/src$ javac BrokenSingleton.java
denlion@denlion-laptop:~/workspace/For Test/src$ ls -al
total 24
drwxr-xr-x 2 denlion denlion 4096 2010-07-15 23:22 .
drwxr-xr-x 5 denlion denlion 4096 2010-06-29 22:06 ..
-rw-r--r-- 1 denlion denlion 196 2010-07-15 23:22 BrokenSingleton$1.class
-rw-r--r-- 1 denlion denlion 518 2010-07-15 23:22 BrokenSingleton.class
-rw-r--r-- 1 denlion denlion 467 2010-07-15 23:22 BrokenSingleton$InstanceHolder.class
-rw-r--r-- 1 denlion denlion 331 2010-07-15 22:57 BrokenSingleton.java
denlion@denlion-laptop:~/workspace/For Test/src$

Yes! THREE files. I saw the same. Checked twice, checked my eyes, and again - THREE! But what is this BrokenSingleton$1.class? So, together with my colleagues, we armed with 'javap' and started our investigation:

denlion@denlion-laptop:~/workspace/For Test/src$ javap BrokenSingleton\$1
Compiled from "BrokenSingleton.java"
class BrokenSingleton$1 extends java.lang.Object{
}

What is this? What is going on? What is this empty class for? So, decompilation of the rest lets us see that there is a very interesting thing inside the BrokenSingleton.class:

denlion@denlion-laptop:~/workspace/For Test/src$ javap BrokenSingleton
Compiled from "BrokenSingleton.java"
public class BrokenSingleton extends java.lang.Object{
public static BrokenSingleton getInstance();
BrokenSingleton(BrokenSingleton$1);
}

But there is NO such construnctor inside BrokenSingleton, right? I think we are wrong :) With the strong suspicion let's write a small test using java reflection api:


public class BrokenSingletonTest {

/**
* @param args
*/
public static void main(String[] args) {
Constructor[] constr = BrokenSingleton.getInstance().getClass().getDeclaredConstructors();
try {
System.out.println(BrokenSingleton.getInstance() == BrokenSingleton.getInstance());
System.out.println(constr[1].newInstance((BrokenSingleton) null) == BrokenSingleton.getInstance());
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

System.out.println(constr[1]);
}

}

And output for this test is:

denlion@denlion-laptop:~/workspace/For Test/src$ java BrokenSingletonTest
true
false
BrokenSingleton(BrokenSingleton$1)
denlion@denlion-laptop:~/workspace/For Test/src$

That's it! We have broken the Singleton and made a small hole in the Java security :) So, as you must have noticed, the Open Jdk compiler has not allowed me to write 'newInstance((BrokenSingleton$1) null)', showing error that it cannot find the symbol (BrokenSingleton$1). But as we can see, there IS the constructor with 'BrokenSingleton$1' and package default access modificator. And by using it, you can easily create as many instances of your 'Single'-pattern as you wish. Honestly, we have made a couple of experiments (Eclipse compiler, Sun Hot Spot v1.6 under Windows, JRockit) they all use the similar technique with slight differences. But Sun Hot Spot allowed us to compile code with 'BrokenSingleton$1' cast on Windows at work.

Here we invented the theory, that Inner classes (actually JVM does not have instructions for inner classes, this is only a syntactic sugar as far as I understand) actually DO NOT have (because this is not a JVM construction) access to the private methods of outher class. And for screwing this, compiler generates such code. This unique constructor with 'BrokenSingleton$1' parameter and with package access allows it to create an instance of our Singleton.

You may think: "How about the private methods of outher class?" And you will be absolutely right - there is almost the same technique (generation of additional method with name like 'access$100' and package default access). You can easily check it yourself :)

So, be aware, my friend! If you are using inner classes, you can not be sure anymore that your private method is not accessible outside the class.
Good luck!

PS: Yes, elimination of inner class solves this problem perfectly :)