Skip to main content

EJB best practices: Industrial-strength JNDI optimization

Use caching and a generic factory class to automate JNDI lookups

Brett McLaughlin (brett@newInstance.com), Author and Editor, O'Reilly Media Inc.
Photo of Brett McLaughlin
Brett McLaughlin has been working in computers since the Logo days (remember the little triangle?). He currently specializes in building application infrastructure using Java and Java-related technologies. He has spent the last several years implementing these infrastructures at Nextel Communications and Allegiance Telecom, Inc. Brett is one of the co-founders of the Java Apache project, Turbine, which builds a reusable component architecture for Web application development using Java servlets. He is also a contributor of the EJBoss project, an open source EJB application server, and Cocoon, an open source XML Web-publishing engine.

Summary:  Brett McLaughlin continues his EJB best practices with an examination of JNDI lookups, which are an essential and frequent part of almost all EJB interactions. Unfortunately, JNDI operations almost always exact a performance toll. In this tip, Brett shows you how a home-interface factory can reduce the overhead of JNDI lookups in your EJB applications.

View more content in this series

Date:  01 Sep 2002
Level:  Intermediate
Activity:  4697 views

Every kind of EJB component (session, entity, and message driven) has a home interface. The home interface is a bean's base of operations; once you've found it, you have access to that bean's functionality. EJB applications rely on JNDI lookups to access their beans' home interfaces. Because EJB apps tend to run multiple beans, and because JNDI lookups are often present in many components, much of an application's performance overhead can be spent on these lookups.

In this tip, we'll look at some of the most common JNDI optimizations. In particular, I'll show you how to combine caching and a generic helper class to create a factory-style solution to JNDI overhead.

Reducing context instances

Listing 1 shows a typical piece of EJB code, requiring multiple JNDI lookups. Study the code for a moment, and then we'll work on optimizing it for better performance.

public boolean buyItems(PaymentInfo paymentInfo, String storeName,
List items) {
      // Load up the initial context
      Context ctx = new InitialContext();

      // Look up a bean's home interface
      Object obj = ctx.lookup("java:comp/env/ejb/PurchaseHome");
      PurchaseHome purchaseHome =
       (PurchaseHome)PortableRemoteObject.narrow(obj, PurchaseHome.class);
      Purchase purchase = purchaseHome.create(paymentInfo);

      // Work on the bean
      for (Iterator i = items.iterator(); i.hasNext(); ) {
          purchase.addItem((Item)i.next());
      }

      // Look up another bean
      Object obj = ctx.lookup("java:comp/env/ejb/InventoryHome");
      InventoryHome inventoryHome =
       (InventoryHome)PortableRemoteObject.narrow(obj, InventoryHome.class);
      Inventory inventory = inventoryHome.findByStoreName(storeName);

      // Work on the bean
      for (Iterator i = items.iterator(); i.hasNext(); )
          inventory.markAsSold((Item)i.next());
      }

      // Do some other stuff
}

While this example is somewhat contrived, it does reveal some of the most glaring problems with using JNDI. For starters, you might ask yourself if the new InitialContext object is necessary. It's likely that this context has already been loaded elsewhere in the application code, yet we've created a new one here. Caching the InitialContext instances would result in an immediate performance boost, as shown in Listing 2:

public static Context getInitialContext() {
      if (initialContext == null) {
          initialContext = new InitialContext();
      }

      return initialContext;
}

By using a helper class with the getInitialContext() instead of instantiating a new InitialContext for every operation, we've cut down the number of contexts floating around in our application to one.

Uh oh -- what about threading?

If you're worried about the effects of threading on the solution proposed here, don't be. It is absolutely possible that two threads could go to work on at the same time (thus creating two contexts at once) but this type of error would happen only on the first invocation of the method. Because the problem won't come up more than once, synchronization is unnecessary, and would in fact introduce more complexities than it would resolve.

Optimizing lookups

Caching the context instances is a step in the right direction, but we're not done optimizing yet. Every time we call the lookup() method it will perform a new lookup, and return a new instance of a bean's home interface. At least, that's the way JNDI lookups are usually coded. But wouldn't it be better to have just one home-interface per bean, shared across components?

Rather than looking up the home interface for PurchaseHome or InventoryHome again and again, we could cache each individual bean reference; that's one solution. But what we really want is a more general mechanism for caching home interfaces in our EJB applications.

The answer is to create a generic helper class to both obtain the initial context and look up the home interface for every bean in the application. In addition, this class should be able to manage each bean's context for various application components. The generic helper class shown in Listing 3 will act as a factory for EJB home interfaces:

package com.ibm.ejb;

import java.util.Map;
import javax.ejb.EJBHome;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class EJBHomeFactory {

      private static EJBHomeFactory instance;

      private Map homeInterfaces;
      private Context context;

      // This is private, and can't be instantiated directly
      private EJBHomeFactory() throws NamingException {
          homeInterfaces = new HashMap();

          // Get the context for caching purposes
          context = new InitialContext();

          /**
           * In non-J2EE applications, you might need to load up
           *   a properties file and get this context manually. I've
           *   kept this simple for demonstration purposes.
           */
      }

      public static EJBHomeFactory getInstance() throws NamingException {
          // Not completely thread-safe, but good enough 
          // (see note in article)
          if (instance == null) {
              instance = new EJBHomeFactory();
          }
          return instance;
      }

      public EJBHome lookup(String jndiName, Class homeInterfaceClass) 
            throws NamingException {

          // See if we already have this interface cached
          EJBHome homeInterface = 
            (EJBHome)homeInterfaces.get(homeInterfaceClass);
          // If not, look up with the supplied JNDI name
          if (homeInterface == null) {
              Object obj = context.lookup(jndiName);
              homeInterface =
               (EJBHome)PortableRemoteObject.narrow(obj, homeInterfaceClass);

              // If this is a new ref, save for caching purposes
              homeInterfaces.put(homeInterfaceClass, homeInterface);
          }
          return homeInterface;
      }
}


Inside the EJBHomeFactory class

The key to the home-interface factory is in the homeInterfaces map. The map stores each bean's home interface for use; as such, one home-interface instance can be used over and over again. You should also note that the key in the map is not the JNDI name passed into the lookup() method. It's quite common to have the same home interface bound to different JNDI names, but doing so can result in duplicates in your map. By relying on the class itself, you ensure that you won't end up with multiple home interfaces for the same bean.

Inserting the new home-interface factory class into the original code from Listing 1 will result in the optimized EJB lookup shown in Listing 4:

public boolean buyItems(PaymentInfo paymentInfo, String storeName,
List items) {

      EJBHomeFactory f = EJBHomeFactory.getInstance();

      PurchaseHome purchaseHome =
          (PurchaseHome)f.lookup("java:comp/env/ejb/PurchaseHome", 
          PurchaseHome.class);
      Purchase purchase = purchaseHome.create(paymentInfo);

      // Work on the bean
      for (Iterator i = items.iterator(); i.hasNext(); ) {
          purchase.addItem((Item)i.next());
      }

      InventoryHome inventoryHome =
          (InventoryHome)f.lookup("java:comp/env/ejb/InventoryHome", 
          InventoryHome.class);
      Inventory inventory = inventoryHome.findByStoreName(storeName);

      // Work on the bean
      for (Iterator i = items.iterator(); i.hasNext(); ) {
          inventory.markAsSold((Item)i.next());
      }

      // Do some other stuff
}

In addition to being more clear (at least in my opinion) the factory-optimized EJB lookup above will perform much faster over time. The first time you use the new class, you'll incur all the usual lookup penalties (assuming another portion of the application hasn't already paid them) but all future JNDI lookups should hum right along. It's also worth pointing out that the home-interface factory will not interfere with your container's bean management. Containers manage bean instances, not the home interfaces of those instances. Your container will still be in charge of instance swapping, as well as any other optimizations you want it to perform.

In the next installment of EJB best practices, I'll show you how you can enable administrative access to entity beans, without directly exposing them to your application's Web tier. Until then, I'll see you online.


Resources

About the author

Photo of Brett McLaughlin

Brett McLaughlin has been working in computers since the Logo days (remember the little triangle?). He currently specializes in building application infrastructure using Java and Java-related technologies. He has spent the last several years implementing these infrastructures at Nextel Communications and Allegiance Telecom, Inc. Brett is one of the co-founders of the Java Apache project, Turbine, which builds a reusable component architecture for Web application development using Java servlets. He is also a contributor of the EJBoss project, an open source EJB application server, and Cocoon, an open source XML Web-publishing engine.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10704
ArticleTitle=EJB best practices: Industrial-strength JNDI optimization
publish-date=09012002
author1-email=brett@newInstance.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers