Sunday, September 27, 2009

Deployment Plan Patch Contribution for Clownfish

I admit I've been out of action since the last release of Clownfish v0.1.1-SNAPSHOT, not to mention my original Clownfish blog had been removed by Blogger.com for unknown reason - can't be bothered to chase this up. :D

During my absence, I've spent a little bit of time looking at Android+ZXing early this year and MyFaces Orchestra recently. (Still trying to get my hands dirty with Google App Engine - ported Hibernate Annotations to JPA, still have a long way to go.)



Yesterday, I went back to have a look at my plugin, with much surprise a "Good Samaritan" offered a patch code that would enable Clownfish to deploy through a deployment plan, unit tests included! This is one of the functionality that I left off that I find I hardly use. I'm glad that someone had tried using Clownfish as a Maven2 Glassfish deployer!

Looking forward this coming long weekend to review and hopefully merge this patch back to Clownfish codebase.

Burned by Public Constants

We define two classes, one class exposes a public constant while the other class consumes the constant value.

Const.java:
1:  public class Const {  
2: public static final int VALUE = 600;
3: public void test() {
4: int a = VALUE;
5: System.out.println("a=" + a);
6: }
7: public static void main(String[] args) {
8: new Const().test();
9: }
10: }


ConstClient.java:
1:  public class ConstClient {  
2: public void test() {
3: int b = Const.VALUE;
4: System.out.println("b=" + b);
5: }
6: public static void main(String[] args) {
7: new ConstClient().test();
8: }
9: }


Compile and run both classes:
Const.java displays:
 a=600  


ConstClient.java displays:
 b=600  


So far so good, this is what we expected.

Let's try disassembling them with "javap -c Const"
1:  Compiled from "Const.java"  
2: public class Const extends java.lang.Object{
3: public static final int VALUE;
4: public Const();
5: Code:
6: 0: aload_0
7: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
8: 4: return
9: public void test();
10: Code:
11: 0: sipush 600
12: 3: istore_1
13: 4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
14: 7: new #3; //class java/lang/StringBuilder
15: 10: dup
16: 11: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
17: 14: ldc #5; //String a=
18: 16: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: 19: iload_1
20: 20: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
21: 23: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: 26: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: 29: return
24: public static void main(java.lang.String[]);
25: Code:
26: 0: new #10; //class Const
27: 3: dup
28: 4: invokespecial #11; //Method "<init>":()V
29: 7: invokevirtual #12; //Method test:()V
30: 10: return
31: }


and "javap -c ConstClient"
1:  Compiled from "ConstClient.java"  
2: public class ConstClient extends java.lang.Object{
3: public ConstClient();
4: Code:
5: 0: aload_0
6: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
7: 4: return
8: public void test();
9: Code:
10: 0: sipush 600
11: 3: istore_1
12: 4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
13: 7: new #3; //class java/lang/StringBuilder
14: 10: dup
15: 11: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
16: 14: ldc #5; //String b=
17: 16: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: 19: iload_1
19: 20: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
20: 23: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
21: 26: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: 29: return
23: public static void main(java.lang.String[]);
24: Code:
25: 0: new #10; //class ConstClient
26: 3: dup
27: 4: invokespecial #11; //Method "<init>":()V
28: 7: invokevirtual #12; //Method test:()V
29: 10: return
30: }


We see that from Const.class line 11 and ConstClient.class line 10, the constant value 600 is pushed onto the stack memory at slot #1.

Let's try changing Const.java constant value from 600 to 500.
1:  public class Const {  
2: public static final int VALUE = 500;
3: public void test() {
4: int a = VALUE;
5: System.out.println("a=" + a);
6: }
7: public static void main(String[] args) {
8: new Const().test();
9: }
10: }


Compile Const.java and run produces:
 a=500  


Rerun ConstClient without recompilation produces strange result:
 b=600  


Let's take a peek by disassembling Const.class:
1:  Compiled from "Const.java"  
2: public class Const extends java.lang.Object{
3: public static final int VALUE;
4: public Const();
5: Code:
6: 0: aload_0
7: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
8: 4: return
9: public void test();
10: Code:
11: 0: sipush 500
12: 3: istore_1
13: 4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
14: 7: new #3; //class java/lang/StringBuilder
15: 10: dup
16: 11: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
17: 14: ldc #5; //String a=
18: 16: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: 19: iload_1
20: 20: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
21: 23: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: 26: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: 29: return
24: public static void main(java.lang.String[]);
25: Code:
26: 0: new #10; //class Const
27: 3: dup
28: 4: invokespecial #11; //Method "<init>":()V
29: 7: invokevirtual #12; //Method test:()V
30: 10: return
31: }


We see that line 11 pushes our new constant value 500 onto the stack.

Let's take another peek at ConstClient.class:
1:  Compiled from "ConstClient.java"  
2: public class ConstClient extends java.lang.Object{
3: public ConstClient();
4: Code:
5: 0: aload_0
6: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
7: 4: return
8: public void test();
9: Code:
10: 0: sipush 600
11: 3: istore_1
12: 4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
13: 7: new #3; //class java/lang/StringBuilder
14: 10: dup
15: 11: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
16: 14: ldc #5; //String b=
17: 16: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: 19: iload_1
19: 20: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
20: 23: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
21: 26: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: 29: return
23: public static void main(java.lang.String[]);
24: Code:
25: 0: new #10; //class ConstClient
26: 3: dup
27: 4: invokespecial #11; //Method "<init>":()V
28: 7: invokevirtual #12; //Method test:()V
29: 10: return
30: }


Line 10 tells us that for some reason our program pushes 600 onto the stack not 500 - our constant value is still 600! It seems like javac burned the constant value into ConstClient.class instead of JVM resolving it through Const.class!

- "Extra care should be taken when hot-deploying classes with public constants as they could potentially break your code during runtime."

Saturday, September 26, 2009

The Curious Case of Enums and Autoboxing

Prior to the release of JDK 5, the expression in a switch statement must be either a char, byte, short, or int; otherwise, compilation error occurs.

 switch (expression) { 
...
}


However as of JDK 5, Sun introduced enumeration type and auto-boxing and because of this, the switch statement also supports these two concepts.

Below is sample code for JDK 5 and later:

1:  public class CaseEnum {  
2: enum ResultEnum { OK, CANCEL };
3: public void test1() {
4: ResultEnum result = ResultEnum.OK;
5: switch (result) {
6: case OK:
7: case CANCEL:
8: }
9: }
10: public void test2() {
11: switch (new Integer(2)) {
12: case 1:
13: case 2:
14: }
15: }
16: public void test3() {
17: switch (new Byte((byte) 3)) {
18: case 1:
19: case 2:
20: case 3:
21: }
22: }
23: }


Compiling with JDK6 and disassembling this class reveals:

1:  Compiled from "CaseEnum.java"  
2: public class CaseEnum extends java.lang.Object{
3: public CaseEnum();
4: Code:
5: 0: aload_0
6: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
7: 4: return
8: public void test1();
9: Code:
10: 0: getstatic #2; //Field CaseEnum$ResultEnum.OK:LCaseEnum$ResultEnum;
11: 3: astore_1
12: 4: getstatic #3; //Field CaseEnum$1.$SwitchMap$CaseEnum$ResultEnum:[I
13: 7: aload_1
14: 8: invokevirtual #4; //Method CaseEnum$ResultEnum.ordinal:()I
15: 11: iaload
16: 12: lookupswitch{ //2
17: 1: 40;
18: 2: 40;
19: default: 40 }
20: 40: return
21: public void test2();
22: Code:
23: 0: new #5; //class java/lang/Integer
24: 3: dup
25: 4: iconst_2
26: 5: invokespecial #6; //Method java/lang/Integer."<init>":(I)V
27: 8: invokevirtual #7; //Method java/lang/Integer.intValue:()I
28: 11: lookupswitch{ //2
29: 1: 36;
30: 2: 36;
31: default: 36 }
32: 36: return
33: public void test3();
34: Code:
35: 0: new #8; //class java/lang/Byte
36: 3: dup
37: 4: iconst_3
38: 5: invokespecial #9; //Method java/lang/Byte."<init>":(B)V
39: 8: invokevirtual #10; //Method java/lang/Byte.byteValue:()B
40: 11: tableswitch{ //1 to 3
41: 1: 36;
42: 2: 36;
43: 3: 36;
44: default: 36 }
45: 36: return
46: }


From line 14, javac adds an extra command to turn the enum into an integer value by invoking the enum "ordinal" method: CaseEnum$ResultEnum.ordinal:()I

From line 27, javac adds an extra command to unbox Integer to int by invoking the "intValue" method:
java/lang/Integer.intValue:()I

From line 39, again javac adds another extra command to unbox Byte to byte by invoking the "byteValue" method:
java/lang/Byte.byteValue:()B

Java String, StringBuilder and StringBuffer

I had a phone conversation with someone yesterday afternoon about Java's String and StringBuffer and how they work. After 24 hours had elapsed my thoughts on String still comes back to bug me - as if I missed out something.

The urge to scratch this itch finally made me fire up “vi” from my terminal. Yes I know what you are thinking, I'm no "vi" expert, but do I really need Eclipse to write 15 lines of code? :)

Here is the source:

1:  public class Str {  
2: public void sayHelloWorld() {
3: String str1 = "hello " + "world ";
4: String str2 = "hello world again";
5: }
6: public void sayGoodbyeWorld() {
7: String str3 = "goodbye world " + 3;
8: }
9: public void sayYouSayMe() {
10: String str4 = "";
11: for (int i = 0; i < 3; i++) {
12: str4 += "say you say me ";
13: }
14: }
15: }


Compiling the source with JDK 6 followed by
“javap -c Str" gives us:

1:  Compiled from "Str.java"  
2: public class Str extends java.lang.Object{
3: public Str();
4: Code:
5: 0: aload_0
6: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
7: 4: return
8: public void sayHelloWorld();
9: Code:
10: 0: ldc #2; //String hello world
11: 2: astore_1
12: 3: ldc #3; //String hello world again
13: 5: astore_2
14: 6: return
15: public void sayGoodbyeWorld();
16: Code:
17: 0: ldc #4; //String goodbye world 3
18: 2: astore_1
19: 3: return
20: public void sayYouSayMe();
21: Code:
22: 0: ldc #5; //String
23: 2: astore_1
24: 3: iconst_0
25: 4: istore_2
26: 5: iload_2
27: 6: iconst_3
28: 7: if_icmpge 36
29: 10: new #6; //class java/lang/StringBuilder
30: 13: dup
31: 14: invokespecial #7; //Method java/lang/StringBuilder."<init>":()V
32: 17: aload_1
33: 18: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: 21: ldc #9; //String say you say me
35: 23: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: 26: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: 29: astore_1
38: 30: iinc 2, 1
39: 33: goto 5
40: 36: return
41: }


The disassembled JDK 6 code from above reveals quite a bit about this simple class.
  • Line 2: the compiler automatically extends our class from java.lang.Object.

  • Line 3-7: the compiler automatically inserts a default Str constructor that invokes java.lang.Object constructor

  • Line 10: ldc #2 suggests that “hello world” string is stored as the 2nd item from constant pool table, where “hello " and "world” is automagically concatenated for you by the compiler

  • Line 12: ldc #3 suggests that “hello world again” string is stored as the 3rd item from constant pool table

  • Line 17: ldc #4 suggests that “goodbye world 3” is stored as the 4th item from constant pool table, where the compiler again automagically concatenated integer “3” to "goodbye world "

  • Line 22: ldc #5 suggests that our empty string is stored as the 5th item from constant pool table

  • Line 29-36: tells us that javac inserts StringBuilder to concatenate "say you say me " strings with "say you say me " stored as the 9th item from constant pool table

  • Line 36: StringBuilder object invokes the toString method and assigns it back to “str4”





Let's try compiling the same source with JDK 1.4 by running “javac -source 1.4 -target 1.4 Str.java” then followed by “javap -c Str” which gives us:

1:  Compiled from "Str.java"  
2: public class Str extends java.lang.Object{
3: public Str();
4: Code:
5: 0: aload_0
6: 1: invokespecial #1; //Method java/lang/Object."<init>":()V
7: 4: return
8: public void sayHelloWorld();
9: Code:
10: 0: ldc #2; //String hello world
11: 2: astore_1
12: 3: ldc #3; //String hello world again
13: 5: astore_2
14: 6: return
15: public void sayGoodbyeWorld();
16: Code:
17: 0: ldc #4; //String goodbye world 3
18: 2: astore_1
19: 3: return
20: public void sayYouSayMe();
21: Code:
22: 0: ldc #5; //String
23: 2: astore_1
24: 3: iconst_0
25: 4: istore_2
26: 5: iload_2
27: 6: iconst_3
28: 7: if_icmpge 36
29: 10: new #6; //class java/lang/StringBuffer
30: 13: dup
31: 14: invokespecial #7; //Method java/lang/StringBuffer."<init>":()V
32: 17: aload_1
33: 18: invokevirtual #8; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
34: 21: ldc #9; //String say you say me
35: 23: invokevirtual #8; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
36: 26: invokevirtual #10; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
37: 29: astore_1
38: 30: iinc 2, 1
39: 33: goto 5
40: 36: return
41: }



The disassembled code for 1.4 and 1.6 looks the same except that 1.4 uses StringBuffer instead of StringBuilder as StringBuilder was introduced as part of JDK 5 and later. So it looks like a little bit of String optimisation is done on the Javac level not on the JVM level.