Friday 17 January 2014

Class Loader, Class and Object


Recently, there are two occasions that make me feel that it is worth talking about the Class Loading mechanism in Java. On the first occasion, my friend asked me how to create new object of the nested class. Simply put, it is like this:

class OuterClass {
 
 private String outerVariable;
 private static String outerStaticVariable;
 
 class InnerClass {
  public String toString() {
   return outerVariable.toString();
  }
 }

 static class StaticInnerClass {
  public String toString() {
   return outerStaticVariable.toString();
  }
 }

 public static void main(String[] args) {
  OuterClass outerObject = new OuterClass();
  outerObject.outerVariable = "outerVariable";
  outerStaticVariable = "outerStaticVariable";
  
  OuterClass.StaticInnerClass nestedObject = new OuterClass.StaticInnerClass();
  OuterClass.InnerClass innerObject = outerObject.new InnerClass();

  System.out.println(nestedObject.toString());
  System.out.println(innerObject.toString());
 }
}


It is very simple to know why if you understand the class loader mechanism in Java well enough. As it is perfectly explained in http://zeroturnaround.com/rebellabs/reloading-objects-classes-classloaders/


When you create inner class, you need to have an outer class object to bind your self to before manage to create any object.

The hierarchy of innerObject should be:
Class Loader → OuterClass → outerObject → InnerClass → innerObject.
In constrast, the hierarchy of nestedObject should be:
Class Loader → OuterClass → InnerClass → innerObject
That explain the different among 3 constructors in the above example:

OuterClass outerObject = new OuterClass();
OuterClass.StaticInnerClass nestedObject = new OuterClass.StaticInnerClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();


If you read up to this point, you will wonder when to use inner class, when to use static inner class. For me, static inner class is naturally an independent class that being hide inside another class declaration. It still fully function as a normal class but invisible from IDE autocomplete. It gives you the flexibility of reusing the same name for inner class. Some sample of using it is the mapper class to be used in JDBC if you find the mapper useful to be reused.

Most of the time, you will just need to use static inner class and it is recommended to do so as JVM will only keep track of one class declaration for your inner class. If you are still unclear about this benefit, the class itself is also one object in JVM. That why java allow you to access class object using reflection. The only case that you use non-static inner class is when you need access to outer class non-static variable.

It is also worth taking note that the class object is not unique in JVM. To be more accurate, it is unique per class loader. However, depend on how you create your application, you may have more than one class loader in your JVM. For example, if you run the above code using java OuterClass from terminal, you will only have 1 class loader in your JVM. If you put the class above inside one Java web application then your JVM will have more than one class loader. It is because most of the Java web container use dedicated class loader to load web application for security reason (you do not want the class from other web application to interfere with your application right?).

In this case, if you deploy 2 web application to the same container that both contain log4j, you actually have 2 log4j class in your JVM. However, only one log4j class is visible to each webapp. Still, it may cause some issues if you have 2 appenders that attempt to write to the same log file. If you choose the other strategy by putting log4j class to web container library then you will have unique appender per web container and you force all the webapps to share log4j configuration. It is highly recommended that you do not put any class that you need access to its static variable to web container library.

 Up to this point, I have not mentioned the second occasion yet. Yep, it happened when we deployed two applications with this log4j configuration to Tomcat:


<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.xml</param-value>
</context-param>
<context-param>
<param-name>log4jExposeWebAppRoot</param-name>
<param-value>false</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>


Kindly notice the highlighted param above. This is quite tricky and I see many developers suffered from this problem. webAppRoot is a bonus feature that allow you to use webapp root inside log4j config file. However, if you put log4j config file to a share folder in Tomcat, you manage to create conflict here as log4j fail to identify the unique webapp root. Depend of your deployment order, it is likely that the first webapp deployment will pass and the second one will fail. In this case, you got no choice but turn off this feature.

3 comments: