Thursday, October 15, 2009

EJB 2.x maximum performances and flexibility: abstract from Remote vs Local

I think the following should be a best practice whenever you should implement EJB 2.x specifications as this will allow to transparently switch between local and remote interfaces without any change.

In EJB 2.x you need to write the following classes/interfaces to support both remote and local deployment:
  • public class MyComponentBean implements javax.ejb.SessionBean
  • public interface MyComponentRemoteHome extends javax.ejb.EJBRemoteHome
  • public interface MyComponentRemote extends javax.ejb.EJBObject
  • public interface MyComponentLocalHome extends javax.ejb. EJBLocalHome
  • public interface MyComponentLocal extends javax.ejb.EJBLocalObject
If you want to be able to switch between local and remote deployment you need to change every place where you retrieve the EJB, usually this action is concentrated in a ServiceLocator implementation like:

public class MyComponentServiceLocator {
public final static String MY_COMPONENT_LOCATION = "ejb/myComponent";

public static MyComponentLocal getLocal(Properties properties) throws Exception {
       InitialContext context = new InitialContext(properties);
       MyComponentLocalHome home = (MyComponentRemoteHome)context.lookup(MY_COMPONENT_LOCATION + "/local");
       return home.create();
   }
   public static MyComponentRemote getRemote(Properties properties) throws Exception {
       InitialContext context = new InitialContext(properties);
       MyComponentRemoteHome home = (MyComponentRemoteHome)context.lookup(MY_COMPONENT_LOCATION + "/remote");
       return home.create();
   }
}


With this approach you can switch from local to remote just switching from MyComponentServiceLocator.getLocal(...) to MyComponentServiceLocator.getRemote(...) on every place you need to switch, but in addition you need to switch the type you declared for the variable to which you are going to assign the MyComponentServiceLocator call result: from MyComponentLocal to MyComponentRemote.

In addition you need to manually track down all interfaces are exposing the same methods.
Wouldn't it easier if we can have some sort of automatic check and avoid the need to switch the code? Couldn't be possible to switch between local and remote at deployment time without any change at compile time?

Well, the answer is in the following structure:
  • public interface MyComponent
    declares all shared functional methods, each method will throws java.rmi.RemoteException in addition to any exception it should normally throw
  • public interface MyComponentHome
    declares all shared creation methods throwing both java.rmi.RemoteException and javax.ejb.CreateException

  • public class MyComponentBean implements javax.ejb.SessionBean, MyComponentRemote, MyComponentLocal
    the MyComponentRemote and MyComponentLocal interfaces have been added here to ensure the implementation class provides code for all the declared methods: be careful, all methods must not throw java.rmiRemoteException
  • public interface MyComponentRemoteHome extends javax.ejb.EJBRemoteHome, MyComponentHome
    the MyComponentHome interface has been added here to ensure all shared creation methods are supported by the remote home: if all the remote creation methods are shared than this interface will be completely empty!
  • public interface MyComponentRemote extends javax.ejb.EJBObject, MyComponent
    the MyComponent interface has been added here to ensure all the shared methods are supported through the remote interface: if all the remote methods are shared than this interface will be completely empty!
  • public interface MyComponentLocalHome extends javax.ejb. EJBLocalHome, MyComponentHome
    the MyComponentHome interface has been added here to ensure all shared creation methods are supported by the local home: all methods defined in the MyComponentHome interface must be overriden here to remove the java.rmi.RemoteException declaration; if you forget this step your application server should warn you when you deploy this EJB.
  • public interface MyComponentLocal extends javax.ejb.EJBLocalObject, MyComponent
    the MyComponent interface has been added here to ensure all shared methods are supported through the local interface: all methods defined in the MyComponent interface must be overriden here to remove the java.rmi.RemoteException declaration; if you forget this step your application server should warn you when you deploy this EJB.
This is harder to say than to put in practice and the advantages are:
  • one place for shared creation methods: if you add a method to MyComponentHome interface you automatically get it on the remote home and if you forget to override it in the local home (to remove the java.rmi.RemoteException) your application server will warn you on your first deployment;
  • one place for shared functional methods: if you add a method to MyComponent interface you automatically get it on the remote interface and if you forget to override it in the local interface (to remove the java.rmi.RemoteException) your application server will warn you on your first deployment;
  • your clients will no more have to deal with remote or local differences as they will use the MyComponent interface (unless they need some methods not available on both interfaces)
  • you can still produce different interfaces for local and remote deployments;
  • your implementation will always implement the required methods;
  • you can switch between local and remote deployment using the ejb-ref directive (in your web.xml or in your ejb.xml)
  • you can have a ServiceLocator like the following one which completely masks the remote vs local

public class MyComponentServiceLocator {
   public final static String MY_COMPONENT_LOCATION = "ejb/myComponent";
   public static MyComponent get(Properties properties) throws NamingException, CreateException, RemoteException {
        InitialContext context = new InitialContext(properties);
       MyComponentHome home = (MyComponentHome)context.lookup("java:comp/env/" + MY_COMPONENT_LOCATION);
       return home.create();
   }
}


If you don't want to deal with the ejb-ref at all you can consider the following ServiceLocator implementation which allows any deployment combination and automatically uses the local interface if available (with lesser performances as two JNDI lookups are performed in the worst case)

public class MyComponentServiceLocator {
   public final static String MY_COMPONENT_LOCATION = "ejb/myComponent";
   public static MyComponent get(Properties properties) throws NamingException, CreateException, RemoteException {
       try {
            return MyComponentServiceLocator.getLocal(properties);
       } catch (Exception e) {
            return MyComponentServiceLocator.getRemote(properties);
       }
   }
   public static MyComponentLocal getLocal(Properties properties) throws NamingException, CreateException {
       InitialContext context = new InitialContext(properties);
       MyComponentLocalHome home = (MyComponentRemoteHome)context.lookup(MY_COMPONENT_LOCATION + "/local");
       return home.create();
   }
   public static MyComponentRemote getRemote(Properties properties) throws NamingException, CreateException, RemoteException {
       InitialContext context = new InitialContext(properties);
       MyComponentRemoteHome home = (MyComponentRemoteHome)context.lookup(MY_COMPONENT_LOCATION + "/remote");
       return home.create();
   }
}

Be careful, the last solution can produce unwanted exception traces in your application server when the local lookup fails: those exceptions are normal unless produced by the remote lookup. Those unwanted exceptions can bring you mad when you try to understand why your EJB is not working.

Tuesday, October 6, 2009

JBoss Production Environment

In my humble opinion a JBoss production environment should be something like the one depicted in the following diagram



I recommend to use mod_proxy, mod_proxy_balancer and mod_proxy_ajp apache modules both for load balancing and request forwarding with directives like:

ProxyRequests Off
<Proxy balancer://webapp-cluster>
Order deny,allow
Allow from all
BalancerMember ajp://instance1:8009/webapp-name loadfactor=1
BalancerMember ajp://instance2:8009/webapp-name loadfactor=1
ProxySet lbmethod=bytraffic
</Proxy>
ProxyPass /webapp-name balancer://webapp-cluster
ProxyPassReverse /webapp-name balancer://webapp-cluster
System dimensions and load can vary the numbers, but the architecture should be sufficient and enough scalable for many situations.

Monday, October 5, 2009

Java Serialization & final class attributes

Today I had to face a problem with Java Serialization and here is the report of what I've achieved.
The SmartWeb BusinessObject class defines a protected attribute named logger carrying the logger for subclasses. The BusinessObject class implements Serializable thus it needs to define the logger attribute as transient because Commons Logging loggers are non serializable.

The problem arises whenever you deserialize a BusinessObject subclass because the logger attribute will not be deserialized (it has not be serialized at all!) and this makes all your logging statements producing NullPointerExceptions! BTW, those errors are very difficult to understand for two reasons:
  1. you always consider that attribute valid and you will hardly consider tha logger attribute to be null
  2. every logging statement you try to add to your code to understand what's going wrong will fail on it's own
Well, the solution to the problem is re initialize the logger attribute upon object deserialization implementing a custom readObject method as stated in the Serializable interface documentation:
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
The preceeding code is not going to work in my specific case because the logger attribute has been declared as final to avoid unwanted replacements and potential errors. The first option I took in consideration was "ok, I've no exit, let's make that attribute non final" but the idea was suddenly replaced by "but standard Java Serialization is normally able to deserialize final fields... how?" and I googled and digged a little bit into the problem ending to the following solution:
<br />	/**<br />	 * Custom deserialization. We need to re-initialize a logger instance as loggers<br />	 * can't be serialized.<br />	 */<br />	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {<br />		try {<br />			Class type = BusinessObject.class;<br />			// use getDeclaredField as the field is non public<br />			Field logger = type.getDeclaredField("logger");<br />			// make the field non final<br />			logger.setAccessible(true);<br />			logger.set(this, LogFactory.getLog(type));<br />			// make the field final again<br />			logger.setAccessible(false);<br />		} catch (Exception e) {<br />			LogFactory.getLog(this.getClass())<br />				.warn("unable to recover the logger after deserialization: logging statements will cause null pointer exceptions", e);<br />		}<br />		in.defaultReadObject();<br />	}<br />