HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Last month, in EJB Home, I covered the business advantage of Enterprise JavaBeans' portability from a high level. First I discussed the various types of portability. Then I covered (1) the portability goals the creators of EJB had in mind when developing the specification and (2) how your business can achieve a competitive edge through EJB. This month I'll finish up the discussion of EJB portability from a developer's perspective.

Avoiding Portability Pitfalls in EJB
While the EJB specification 1.0/1 has laid the foundation for building portable enterprise beans, there's much left to be specified before true interoperability will be a reality. The EJB specification is vague in a number of areas important to EJB portability, including a container provider's responsibilities, multiple-vendor EJB server integration, security, distributed/asynchronous event notification and mappings for COM integration.

I'm not going to bash EJB for its lack of portability this month. Besides, if you want to read articles that downplay EJB, you're reading the wrong magazine (and the wrong author)! I will, however, offer a number of tricks, traps and tips that I and my colleagues have encountered when building portable EJBs. Add these techniques to your armament and you'll shield yourself from many pitfalls in EJB portability to date.

Dos and Don'ts of EJB Portability
When you were a child, your mother or father probably separated your world into dos and don'ts, such as "Do your homework!" and "Don't put gum in your sister's hair!!" This is a nice model for describing good and bad software development techniques as well; thus, for your enjoyment, I've made a list of dos and don'ts on the topic of EJB portability. The list is definitely not complete and I'd love feedback on "gotchas" that you've encountered in your experience no matter the EJB server. Maybe a second article listing reader encounters could be compiled in the future.

If you don't recall last month's coverage of the wrapper design pattern, you'll want to dust off your October issue of JDJ and reread Part 1 on the topic of EJB portability. For the rest of you...hold onto your "wrapper." Not only is a wrapper a better place for your gum than your sister's hair but, as you'll see, the wrapper design pattern will play a key role in the portability of your enterprise beans!

Don'ts
Proprietary Value-Add Features
Don't use hard-coded references to a particular application server's proprietary features. J2EE is aimed at providing the APIs necessary for you to build portable, distributed applications to solve diverse business problems. However, it was created after many application server vendors had already implemented their own value-add features into their products. The problem inherent in these features is vendor lock-in, a problem EJB (and J2EE) looks to solve! Following is a list of value-add features to be wary of.

  • Proprietary event models: Examples of proprietary event models include WebLogic Events and the Novera Integration Server's Event Components. The EJB specification says nothing about asynchronous event notification (EJB is a transactional component model, while events are usually considered nontransactional). Because certain business problems require asynchronous communications along with publish and subscribe mechanisms, event models are an often implemented proprietary feature.
  • Nested transactions: To provide backward compatibility with existing transaction monitors and middleware, the EJB specification supports only flat transactions. Some EJB servers (I won't mention names, but the color blue comes to mind) may tout their value-add "nested transaction" support. Buyer, beware!
  • System management facilities that aren't Java Management API (JMAPI) compliant: Coding special management interfaces into an enterprise bean to monitor performance, or otherwise, will be disastrous when moving the bean to another EJB server.

  • Shared services: EJB has no concept of shared services that hold state and are accessible by all clients. If this feature is necessary in your application, you'll have to look beyond EJB, possibly to value-add features in your application server, such as Novera's Registered Objects and Daemons.
  • Proprietary application server "login" techniques: Often an application server requires a client to make a connection to it via a proprietary login object or JavaBean. The login not only establishes a connection to the application server, but performs necessary security checks on the user as well. Coding logins to a particular application server will tie you to this vendor and hinder an easy move to another application server if the future demands it. Thus the code in your enterprise beans needs to be portable, and you also have to think about the portability of your client code when designing for minimal impact from one application server to another.
Java Native Interface
This is an easy one. When building a portable EJB, don't implement JNI code in your beans. Although it's a necessary feature for Java to make inroads into Wintel applications, JNI hinders platform portability for your EJB.

Bean-Managed Persistence
If you're concerned about persistence portability, don't use bean-managed persistence in your entity beans. The August column of EJB Home (JDJ Vol. 4, issue 8) warned about the disadvantages of bean-managed entity beans. When coding an entity bean with bean-managed persistence, you have the potential of tying yourself not only to a particular database, but also to a particular EJB server by utilizing the EJB server's connection management features.

Bean-Managed Transactions
While we're on the subject of bean management....Don't develop enterprise beans that use bean-managed transactions (e.g., TX_BEANMANAGED) unless absolutely necessary. Once a bean is coded to use such transactions, you're stuck with this decision.

You won't be able to switch to another transactional property during deployment! An enterprise bean with a transactional property of TX_BEANMANAGED may also unknowingly tie itself to an EJB server's implementation of a transaction service. As a reminder, if you're using bean-managed transactions, don't nest transactions in your Enterprise JavaBeans!

From the last two "don'ts" you can see there's a trend here -bean-managed is generally a portability liability and requires complex implementations. On the other hand, the EJB component model for development and deployment of server-side components promotes portability while sheltering fledgling developers from the nuances of transaction management or object storage.

Dos
Proprietary Value-Add Features

Instead of using an application server's proprietary features, use the Java Platform for the Enterprise APIs (J2EE) offered by your application server. While too new to be standard across all application servers, these APIs will be offered in most servers in the near future. If you must use an application server's proprietary features, use a wrapper that maps to the API as best you can to encapsulate calls to these features. Wrappers help to localize proprietary code, so when your EJB server becomes J2EE compliant, you'll have minimized your changes to a few classes and methods.

Note: Even some Java Enterprise APIs may need to be wrapped to be truly portable across application server vendors! For instance, to access an enterprise bean, you go through the bean's home interface, which acts as a factory to give you a handle to your EJB. However, you first have to locate an enterprise bean's home interface by calling through the Java Naming Directory Interface (JNDI). The JNDI requires you to specify the InitialContextFactory to be used to perform a lookup on your enterprise bean's home. The parameter you pass for the InitialContextFactory corresponds to the EJB server that the enterprise bean is currently deployed in; thus it is inevitably proprietary to your current EJB server.

To avoid references to a particular vendor's InitialContextFactory and ease your pain when moving to a different EJB server, wrap calls to the EJB server's InitialContextFactory within your own class, such as the class in Listing 1.

This wrapper is fairly basic, but does the job of a wrapper by localizing calls to an EJB server vendor's InitialContextFactory. When getContext() is called, the wrapper uses its current values for user, password, url and contextfactory to return a valid InitialContext object. The InitialContext, of course, is used to perform a lookup of your enterprise bean's EJBHome interface. If your product contains thousands of JNDI lookups, you can see how the EJBContextWrapper would minimize your struggle to migrate to another EJB server.

To use the context wrapper, import it into your Java IDE of choice and simply modify the JNDIConf.properties -specifically, the name of the EJB vendor's InitialContextFactory in question. In the following code you can see that I have set the url and contextfactory properties to point to the BEA WebLogic application server where my enterprise beans are deployed.

url=t3://localhost:7001
contextfactory=weblogic.jndi.TengahInitialContextFactory
user=
password=

I've left the user and password empty as they're not required values to get an InitialContext returned. Following is the classic example of the AccountBean (getting as old as hello, world, wouldn't you agree?). This code gets a handle to the AccountHome through the Context object returned via the EJBContextWrapper.

Context ctx = EJBContextWrapper.getContext();

// Contact the AccountHome through JNDI.
AccountHome home = (AccountHome) ctx.lookup("AccountJNDIName");

While serving our purpose for this article, the EJBContextWrapper could be enhanced in a number of ways. For instance, you could include more overrides for added user flexibility. Also, to support disparate EJB server vendors, you could add a dynamically loaded hashtable that contains each deployed enterprise bean's EJB server context information keyed by the bean's JNDIHome name. This would enable you to find the correct EJB server in the enterprise environment hosting the enterprise bean in question -for instance, by passing a java.lang.String argument into getContext() (i.e., getContext(String JNDIName)). This is used to look up the correct settings from the hashtable. This technique is needed only if you have multiple EJB servers running under different naming services in your enterprise.

Wrap Native Code
You want your business logic to remain portable! To do this, separate your business logic from native code calls by making a separate portable EJB that wraps a legacy/JNI EJB. This gives anyone needing access to the legacy code the benefits of EJB -namely, location independence, transaction support and lifecycle management -while keeping the business logic in a portable enterprise bean.

Container-Managed Persistence
Instead of using bean-managed persistence, offload persistence responsibilities to the EJB container by utilizing container-managed persistence for everything possible. In some cases a query may get too complex to be handled through the EJB server's container and custom database code is thus unavoidable. However, for a large percentage of your persistence needs you should be able to take full advantage of EJB's component model and map your entity beans at deployment time to their respective storage mechanism(s).

Container-Managed Transactions
Instead of writing transaction code in your bean, concentrate on its business logic and leave transaction management up to the EJB container. Determine the transactional behavior of your EJBs at deployment time and keep your enterprise beans flexible.

Lowest Common Denominator
There's no silver bullet solution to EJB portability. The answer lies in the Lowest Common Denominator approach, which says, "Do not use a feature that is not widely available among vendors or an industry recognized standard." To use the LCD approach when building portable enterprise beans, use only those features designated mandatory in the current EJB specification. This will ensure that all compliant EJB servers will offer your enterprise bean the services it needs to execute properly. The LCD approach applies to other APIs besides the EJB specification. It assumes you wouldn't incorporate proprietary features from your application server vendor into your application either.

Summary
While EJB builds on Java's claim to fame of platform portability, there are a number of nuances to be aware of when building portable enterprise beans. To ensure that the next enterprise neans you develop are portable and add business value to your users, make sound architectural decisions, utilize design patterns like the "wrapper" as portability enablers, and follow the tips listed above.

As mentioned, this list of EJB portability tricks isn't exhaustive by any means. I look forward to hearing from everyone who'd like to share their experiences.

Author Bio
Jason Westra is a managing partner with Verge Technologies Group, Inc., a Java consulting firm specializing in Enterprise JavaBeans solutions.
He can be reached at: [email protected]

	

Listing 1: EJBContextWrapper
 
import javax.naming.InitialContext; 
import javax.naming.Context; 
import java.util.Properties; 
import java.io.FileInputStream; 
  

// EJBContextWrapper is a utility class to service requests 
// for a JNDI context.  It loads JNDI lookup properties from 
// a properties file, or uses defaults when none are preset. 
// It also allows the properties to be bypassed by calling 
// overrides of getContext() 
  

public class EJBContextWrapper extends java.lang.Object 
{ 

        private static String url = ""; 
        private static String factory = ""; 
        private static String password = ""; 
        private static String user = ""; 

        static { 
         try { 
          // load in the JNDI settings from configuration file 
          FileInputStream in = new FileInputStream("JNDIConf.properties"); 
          Properties props = new Properties(); 
          props.load(in); 
          in.close(); 
  

          url = props.getProperty("url"); 
          factory = props.getProperty("contextfactory"); 
          user = props.getProperty("user"); 
          password = props.getProperty("password"); 

         } catch (Exception ex) { 
          // defaults 
          url = "t3://localhost:7001"; 
          factory = "weblogic.jndi.TengahInitialContextFactory"; 
          user = ""; 
          password = ""; 

          System.out.println("Failed to initialize EJBContextWrapper "+ 
                             "properties. Using defaults."); 
          System.out.println("url =  "+url 
                             +" factory = "+factory 
                             +" user = "+user 
                             +" password = "+password); 
        } 
       } 

  // constructor, do not let this get instantiated 

  public EJBContextWrapper () 
  {} 

  // getContext() override that uses loaded values to get a 
  // context in JNDI 

  public static InitialContext getContext() throws Exception 
  { 
     return getContext(url, factory, user, password); 
  } 

  // getContext() override that uses specified user/pwd and 
  // loaded url/factory to get a context in JNDI 

  public static InitialContext getContext(String aUser, String 
  aPwd) throws Exception 
  { 
    return getContext(url, factory, aUser, aPwd); 
  } 

  // getContext() override that uses specified parameters to 
  // get a context in JNDI 

  public static InitialContext getContext(String aUrl, String 
  aFactory, String aUser, String aPwd) throws Exception 
  { 
   Properties p = new Properties(); 
   p.put(Context.INITIAL_CONTEXT_FACTORY, aFactory); 
   p.put(Context.PROVIDER_URL, aUrl); 

   if (user != null || user != "") { 
    p.put(Context.SECURITY_PRINCIPAL, aUser); 

   if (password == null) 
    password = ""; 

   p.put(Context.SECURITY_CREDENTIALS, aPwd); 
  } 

  return new InitialContext(p); 
 } 

} 
  



      
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.