Share

From this page you can share Making the Most of Java 5.0: Enum Example to a social bookmarking site or email a link to the page.
Social WebE-mail
Enter multiple addresses on separate lines or separate them with commas.
Making the Most of Java 5.0: Enum Example
(Your Name) has forwarded a page to you from Ajaxonomy
(Your Name) thought you would like to see this page from the Ajaxonomy web site.

Making the Most of Java 5.0: Enum Example

Tagged:  

"Hello World!" examples are great for general introductions to a concept, but often tend to lack much depth. So, as a follow-up to the original article on enums in Java 5.0, this article goes into a little more depth with a real-world example.

The Container Enum

Figure 1 below illustrates the enumeration of possible application servers to which an application can deploy. Why is this useful? Many organizations have more than one Java application server in different environments, and an EAR or WAR may need to be redeployed to different servers; or, you may be developing a common framework that will be used in different environments. It is often helpful in these situations to abstract many of the (static) variances between containers so that redeployment to a different environment can be done with a minimum of effort.

Figure 1: The Container enum.

public enum Container {
   WEBSPHERE, WEBLOGIC, JBOSS, NONE;
}

Now, let's say the application being designed requires an application-controlled JTA TransactionManager (if, for example, the standard way of doing XA transactions cannot be used). But how one is acquired varies pretty widely, depending on which environment the code is running. The variances here can be handled with an abstract method added to the enum, as in Figure 2.

Figure 2: The Container enum with factory methods.

public enum Container 
{
     WEBSPHERE() {
        private String[] factoryNames =
           {"com.ibm.ws.Transaction.TransactionManagerFactory",
            "com.ibm.ejs.jts.jta.TransactionManagerFactory",
            "com.ibm.ejs.jts.jta.JTSXA"};
        @Override
	public TransactionManager getTransactionManager() 
            throws Exception {
	     for (int i = 0; i < factoryNames.length; i++)  {
	         Method getTxMgr = null;
		 try {
		   //attempt to get class and method
		   Class<?> txMgrFactory = Class.forName(factoryNames[i]);
		   getTxMgr = txMgrFactory
                      .getMethod("getTransactionManager", null);	
		  } catch (Exception e) { 
		       continue;
		  }
		  //attempt to invoke method (let exception throw...)
                  return (TransactionManager) getTxMgr.invoke(null, null);
	     }
	     //if all else fails
	     return null;   //or throw exception
	}
     },

     WEBLOGIC() {
        @Override
	public TransactionManager getTransactionManager() 
          throws Exception {
           InitialContext ctx = null;
           try {
             ctx = new InitialContext();
             return (TransactionManager) ctx
               .lookup("java:comp/UserTransaction");
           } finally {
              if(ctx!=null) {
                 try { ctx.close(); } catch (NamingException e) {}
              }
           }
        }
     },

     JBOSS() {
      @Override
	public TransactionManager getTransactionManager() 
          throws Exception {
           InitialContext ctx = null;
           try {
             ctx = new InitialContext();
             return (TransactionManager) ctx
               .lookup("java:/TransactionManager");
           } finally {
              if(ctx!=null) {
                 try { ctx.close(); } catch (NamingException e) {}
              }
           }
        }
     },

    // Standalone environ or server w/o TM
    NONE() {
      @Override
	public TransactionManager getTransactionManager() 
          throws Exception {
	   //Use reflection to avoid necessary dependency
	   Class<?> clazz = Class.forName("org.objectweb.jotm.Jotm");
	   Constructor<?> constr = clazz.getConstructor(boolean.class, 
            boolean.class);
	   Object jotm = constr.newInstance(true, false);
	   Method getTxMgr = clazz.getMethod("getTransactionManager", 
            (Class[])null);
	   return TransactionManager.class.cast(getTxMgr
             .invoke(jotm, (Object[])null));
        }
    };

    // abstract method
    public abstract TransactionManager getTransactionManager() 
         throws Exception;
}

Here we are defining an abstract method getTransactionManager() as a factory method for getting or creating a JTA TransactionManager in a manner similar to the Spring Framework's JtaTransactionManager. Each enumeration instance must implement this method. In most cases, a JEE-compliant server will have a JNDI lookup that can be used. For WebSphere, there is no API for gettng a TransactionManager directly, but instead a time-honored backdoor for creating one. In the case of NONE (either a stand-alone environment or a server that has no TransactionManager), JOTM is used. Of course, the enum would be a lot shorter if the calls were delegated to factory classes, but that's an implementation detail.

Since the JMX default domain for most major Java application servers is self-describing, something like the following snippet of code can be used to "auto-sniff" the container environment:

     public Container lookupContainer() {
          String defaultDomain = ManagementFactory
              .getPlatformMBeanServer()
              .getDefaultDomain();
          try {
               return Container.valueOf(defaultDomain.toUpperCase());
          } catch (Exception e) { /*...assume none....*/ }
          return Container.NONE;   // or null if no default behavior
     }

After all of that, the client code to use it is a simple line:

lookupContainer().getTransactionManager():

Conclusion

The Container enum is a good illustration of the power of enumerations in Java compared to their C/C++ counterparts. By associating behavior with each enum instance, a smooth way of dispatching getTransactionManager() to the appropriate implementation has been created. This points out another fact: each enum instance (WEBSPHERE, WEBLOGIC, etc.) is implicitly a subclass of the enum class Container itself. This comes in handy since it means that many other object-oriented design patterns (Abstract Factory, Command, Visitor, and Strategy, to name a few) can be implemented using enums. Those, of course, are left as an exercise to the reader.