EJB Environment Handling

Declaring and accessing a Enterprise Java Bean's component environment properties (i.e. environment entries, ejb references, resource references) is complicated and error-prone. It's a source for typo's which remain undiscovered until the EJB is deployed and run in the EJB Container. Moreover, the EJB Bean Provider has to write the same lookup code over and over again.

The Service Locator Pattern tries to solve the problem with a central object for the lookups which does also some caching. A disadvantage of this pattern is that each bean must use the same naming for it's environment properties. This may be the source of very strange behaviour when two bean's use the same name for different things. A possible solution is to use the physical JNDI names instead of the names inside the java:comp/env namespace. It works but it's at the border of the EJB specification because the beans do no longer declare proper ejb and resource references.

XDoclet makes it now a lot easier to declare and access environment properties and it's fully compliant to the ejb spec!. It's still possible that each bean uses it's own naming. This is done by expanding the current tags for declaring environment properties (i.e. @ejb.env-entry, @ejb.ejb-ref) to field and method level. The lookup of the properties is delegated to the technical bean class generated with the session, mdb, entitybmp or entitycmp subtask. This means that the only thing you have to do is to declare a field or a abstract method with a XDoclet tag. The deploymentdescriptor entries and the lookup from the JNDI tree is generated by XDoclet! Your bean class has not a single line of JNDI lookup code.

A simple example

Let's have a look at a simple example. This example declares a field-level environment entry and a method-level ejb reference. Please note that this is not a complete example: there are many tags missing, only the ones related to the new environment handling are presented here.

package example.ejb;

import javax.ejb.SessionBean;

/**
 * This is a environment handling example for XDoclet.
 *
 * @ejb.bean
 *   type="Stateless"
 *   name="Customer"
 *   view-type="local"
 *
 */
public abstract class MySessionEJB implements SessionBean {

    /**
     * @ejb.env-entry
     *   value="1234"
     */
    protected int aFieldLevelEnvEntry();
    
    /**
     * @ejb.ejb-ref
     *   ejb-name="Customer"
     */
    protected abstract CustomerLocal aMethodLevelEjbRef();

}

The field-level property must be declared protected to ensure that it can be set from the XDoclet-generated subclass. For the method-level property, the method must be declared protected and abstract so that it can be implemented by the XDoclet-generated subclass.

Let's see what the deploymentdescriptor subtask generated. Here is a snipplet of the generated ejb-jar.xml.


         <env-entry>
            <env-entry-name>aFieldLevelEnvEntry</env-entry-name>
            <env-entry-type>java.lang.Integer</env-entry-type>
            <env-entry-value>1234</env-entry-value>
         </env-entry>
         <ejb-local-ref>
            <ejb-ref-name>aMethodLevelEjbRef</ejb-ref-name>
            <ejb-ref-type>Session</ejb-ref-type>
            <local-home>example.ejb.CustomerLocalHome</local-home>
            <local>example.ejb.CustomerLocal</local>
         </ejb-local-ref>                
         
            

A env-entry element and a ejb-local-ref element are generated for the declared environment properties. The names are set to the field- and method-name. It is possible to customize the names with the name and ref-name parameters of the @ejb.env-entry resp. @ejb.ejb-ref tag.

Now comes the intersting part. The XDoclet-generated technical bean class. This class is generated with the session subtask.

/*
 * Generated by XDoclet - Do not edit!
 */
package example.ejb;

/**
 * Session layer for MySession.
 */
public class MySessionSession
   extends ejb.env.MySessionEJB
   implements javax.ejb.SessionBean
{
   public void setSessionContext(javax.ejb.SessionContext ctx) 
   {
      javax.naming.Context namingCtx = null;
      try 
      {
         namingCtx = new javax.naming.InitialContext();
         aFieldLevelEnvEntry = ((java.lang.Integer) namingCtx.lookup("java:comp/env/aFieldLevelEnvEntry")).intValue();       
         aMethodLevelEjbRef = (example.ejb.CustomerLocalHome) namingCtx.lookup("java:comp/env/aMethodLevelEjbRef");       
      } 
      catch(javax.naming.NamingException e) 
      {
	     throw new javax.ejb.EJBException("lookup failed", e);
      }
      finally {
         if (namingCtx != null) 
         {
            try 
            {
               namingCtx.close(); 
            }
            catch(javax.naming.NamingException e) 
            {
               e.printStackTrace();
            }			
         }
      }
   }

   private  example.ejb.CustomerLocalHome aMethodLevelEjbRef;
   protected example.ejb.CustomerLocal aMethodLevelEjbRef()
   {
      try 
      {
          return aMethodLevelEjbRef.create();
      } 
      catch(javax.ejb.CreateException e) 
      {
          throw new javax.ejb.EJBException("create failed", e);
      }
   }

}

The XDoclet-generated class performs the lookup of the declared environment properties. The value is directly assigned to field-level properties and cached for method-level properties. For the method-level ejb-reference which has the component interface as return-type, a call to the create() method of the home interface was generated!

EJB references

As you could see in the previous example, it's very easy to make a reference to another EJB by adding a abstract method which returns the referenced EJB's component interface. The XDoclet-generated technical bean class will do the lookup and call the create() method.

This makes it very easy to have a reference to a stateless session bean. And it's also the recommended way for this because the home interface of a stateless session bean is most of the time only used for calling the create() method. But it's not a good idea to use this feature for references to entity beans where the home-interface plays a bigger role. For those beans, it's recommended that the abstract method returns the home interface instead of the component interface.

Caching

For method-level properties, the values are cached in a private memeber of the XDoclet-generated technical bean class. Therefore, the developer is not required to care about caching.

Stateful Session Bean Passivation

When a stateful session bean is passivated, the environment properties of the following types are not passivated. They are set to null in ejbPassivate() and re-fetched from the JNDI tree in ejbActivate().

  • @ejb.resource-ref
  • @ejb.resource-env-ref
  • @ejb.destination-ref
  • @ejb.ejb-service-ref

Environment properties of the follwing types are passivated.

  • @ejb.env-entry
  • @ejb.ejb-ref
  • @ejb.ejb-external-ref