Serialization – Compatible and Incompatible modifications to classes

April 15, 2010

Hello,
Java Serialization allows us to write an Object into byte stream and again re-construct its state whenever needed.
In between Serialization and De-serialization the class definition might get changed. Some modifications in
the class definition would result in java.io.InvalidClassException while De-serializing.
It is very important to understand these constraints. especially while dealing with Enterprise distributed Applications. As Distributed applications would always exchange the objects over the network stream using Serialization techniques.

Modifications those keep the Class compatible with Serialized version

  1. Add Fields
  2. Change Field from Static to Non-Static.
  3. Change Field from Transient to Non-Transient
  4. Add Class to the Object Tree


Modifications those make the Class incompatible with Serialized version

  1. Delete Field
  2. Change Field from Non-Static to Static
  3. Change Field from Non-Transient to Transient
  4. Changing the Class hierarchy
  5. Changing type of a Primitive Field

There are others changes as well those can be added to above list. They can be found here.
If you look at the above list the gist is “Addition of Field is permitted while Deletion cause Exception“.
Changing Field from Static to Non-static for transient to non-transient is kind of adding field to existing class.
While making non-static field to static and data-type of the field is like deleting the field which is incompatible.

This seems quite obvious from the Java Documentation but a beginner level would find this helpful, which is the reason
behind this post.

Cheers !

Amit

Advertisements

Decoupling Java applications and config/properties files

March 11, 2010

Hello,
I have been developing a stand-alone Java application packaged as an executable JAR file.
The application had,

  1. Database interaction using iBatis APIs.
  2. Log the various activities in a log file using log4j API.

I had packaged the “my-db.properties” file that feeds the driver/username/password information to “SqlMapConfig.xml(iBatis configuration file)” within the JAR itself.Also the “log4j-myapp.properties” file was placed inside the JAR.

This introduced strong coupling/dependency between these files and JAR. Whenever I need to change database or log4j configuration, I had to re-package the JAR.

I was searching for a solution for this and based on google searches and interpretation of Java documentation for Class.getResourceAsStream() addressed this problem in the following way.

Solution:

  1. Consider that we are generating this executable jar (myapp.jar) in  “myapp/dist” directory.
  2. To resolve dependencies on iBatis and log4j, put respective JARs in the same i.e “myapp/dist” directory.
  3. Add Class-Path attribute in “Manifest.mf” file that lists these JAR files.
  4. This can be done by providing the custom manifest file while generating the JAR file. #2 and #3 are widely used in various enterprise as well as standalone applications so doing this is not an issue.

  5. Create a directory named “config” in “myapp/dist” where we will keep properties file. Of course this can be any name you like.
  6. Add the entry “config/” to the Class-Path attribute in “Manifest.mf” file. This brings “config” directory in the classpath while executing the application.
  7. Put the “log4j-myapp.properties” and “my-db.properties” in “config” directory.
  8. Your application(may be constructor of your main class) can load the log4j configuration as shown below –
  9. 	/*Initialize log4j */
    	Properties props = new Properties();
    	props.load(this.getClass().getResourceAsStream("/log4j-myapp.properties"));
    	props.list(System.out);
    	PropertyConfigurator.configure(props);
    
  10. SqlMapConfig.xml” can point to the resource properties file as shown below. This does not change much as compared to log4j.
  11. <sqlMapConfig>
       <!-- This is where we specify the properties file where DB configuration is specified -->
       <properties resource="my-db.properties"/>
       <transactionManager type="JDBC" commitRequired="false">
       <dataSource type="SIMPLE">
         <property name="JDBC.Driver" value="${driver}"/>
         <property name="JDBC.ConnectionURL" value="${url}"/>
         <property name="JDBC.Username" value="${username}"/>
         <property name="JDBC.Password" value="${password}"/>
       </dataSource>
      </transactionManager>
      <sqlMap resource="com/amit/test/xyz.xml"/>
    </sqlMapConfig>
    
    
  12. Now when you run your application (java -jar myapp.jar), the properties files will be picked up from the config directory.

This ensures you do not need to change the JAR file for making Database and log4j configuration changes.

The Directory structure for “dist” looks as shown below –

Directory Strcture for "dist": myapp |----dist | |----myapp.jar | |----ibatis-2.3.4.726.jar |----log4j-1.2.14.jar |----ojdbc14_g.jar |----config | |---log4j-myapp.properties |---my-db.properties

Manifest.mf classpath entry as below –

Class-Path: config/ log4j-1.2.14.jar ibatis-2.3.4.726.jar ojdbc14_g.jar

Why this works?
If you see the line that loads log4j configuration file, it has “/” at the start of filename.

props.load(this.getClass().getResourceAsStream("/log4j-myapp.properties"));

As per the Java documentation(see below) for the method, we have to give “/” at the start and then only the resource will be located using absolute name calculated as mentioned.

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)

I was facing issues earlier because,

  1. I was only giving “log4j-myapp.properties” while loading the log4j configuration in my program.
  2. In the Manifest.mf, I was not appending “/” at the end of “config” directory. Without this the resource was not getting located.

Of course this is not just limited to iBatis and/or log4j, but any properties file that needs to be controlled outside the realm of the JAR file especially in stand-alone application can be configured this way.
Java API documentation to the rescue once again. Many thanks to them.

Cheers !!
Amit


Using Java Enums to replace conditional (IF) statements.

February 16, 2010

Hello,
Recently I was using Java Enumerations for addressing some requirements and while doing that I realized that one can write a much better and maintainable code using Enums.
I got rid of lot of conditional statements using Enum attributes in my code. It looks clean and easily understandable.
Here is how I utilized Java Enums. Hope you find this useful.

Scenario:
Consider that we have a list of Strings that represents days of the week.
Now if we want to print out the index of the day instead of its name then we might do something like below ,

Note that System.out.println() is just a an example function used to illustrate the example. We can use retrieved variable in whatever the business logic may be.


Solution I:

This is the naive way of handling this scenario.


if(day.equals("Monday")) {
 System.out.println("Index is " + 1);
} else if(day.equals("Tuesday")) {
 System.out.println("Index is " + 2);
} ...

...

and so on

Solution II:
Somebody might want to create a Map<String,Integer) that contains String “day” as key and “index” as value and easily get the index using the code snippet below


Map<String,Integer> daysofweek = new HashMap<String,Integer>();
daysofweek.put("Monday",1);
daysofweek.put("Tuesday",2);
..
..
int index = map.get("Monday");
System.out.println("Index is" + index);

The second solution seems to be more elegant than the Solution I, which relies on lot of if/else conditional statements to fulfill the requirement.
Instead of using a Map we can do the same thing using an Enumeration in Java 5.

Solution III:
We construct an Enum as below that has an attribute “index”.


public class EnumTest {

 enum daysofweek {
  MONDAY(1),
  TUESDAY(2),
  WEDNESDAY(3); //Not all the days are shown here

  private int index;

  daysofweek(int index) {
   this.index = index;
  }

  public int getIndex() {
   return this.index;
  }
 }

 public static void main(String args[]) {
   //Assume that day is passed as a argument
   String day = args[0];
   System.out.println("Entered valye of day is - " + day);
   System.out.println("Index of the day is - " + daysofweek.valueOf(day.toUpperCase()).getIndex());
 }
}

This is very similar to using the MAP but I think this prove more advantageous when we have more properties OR attributes for each enumerated element (in this case day).
Additionally see the use of “valueOf()” function. We can easily convert the input value to corresponding Enumerated constant.

Let’s say that we want to designate each day to represent task done on that day.
For e.g: Monday is SchoolDay, Tuesday is StoryTimeDay, Wednesday is SwimmingDay etc.
The enumeration above can be easily extended to accomodate this and ready to use instead of conditional statement in your code to handle this.

Modified Enum will look as below:


public class EnumTest {
 enum daysofweek {
  MONDAY(1,"Schoolday"),
  TUESDAY(2,"StoryTimeday"),
  WEDNESDAY(3,"Swimmingday"); //Not all the days are shown here

  private int index;
  private String activity;

  daysofweek(int index,String activity) {
    this.index = index;
    this.activity = activity;
  }

  public int getIndex() {
   return this.index;
  }

  public String activity() {
   return this.activity;
  }
 }

 public static void main(String args[]) {
  //Assume that day is passed as a argument
  String day = args[0];
  System.out.println("Entered valye of day is - " + day);
  System.out.println("Index of the day is - " + daysofweek.valueOf(day).getIndex());
  System.out.println("Activity for the day is - " + daysofweek.valueOf(day).activity());
 }
}

Do let me know your comments and suggestions about this one.

Cheers !!

Amit