Site hosted by Angelfire.com: Build your free website today!

Enterprise JavaBeans

What are Enterprise JavaBeans and why do you need them?

If you haven't yet, you should take a look at the book, Enterprise JavaBeans by Richard Monson-Haefel - it is one of the best EJB books available in my opinion.

Have you ever looked at an EJB Bean class and wondered why it doesn't implement it's remote interface? Or how it even works without implementing it's remote interface? Unlike Java RMI, the implementation class (the EJB Bean class) does not need to implement it's remote interface because the remote method calls are automatically delegated from the remote interface to the EJB Bean class through the EJB container. And actually, it is not recommended to implement the remote interface because the bean will have have to implement that many more callback methods in the Bean class that are not necessary or will not even get used.

In the Enterprise JavaBeans book in Chapter 9 Design Strategies, Richard talks about having an Adapter class for your EJB Bean class to extend so all of the callback methods that you don't care about aren't cluttering your EJB. This Adapter class could also just be a base class that all of your EJBs extend from anyway, so it really doesn't need to be designated as an "Adapter" class.

Another good design strategy mentioned in this chapter is creating a "common interface" that contains the remote interfaces business methods and is a stand alone interface. This is a good way to keep your remote interface and your EJB Bean implementation class in sync, from a compiling standpoint. This way, the remote interface extends the "common interface" and the EJBObject interface, and the EJB Bean class implements the "common interface" and the SessionBean or EntityBean.
Doing this will force the compiler to check method signatures between the remote interface and the EJB Bean class that would otherwise not get caught until you were packaging the EJBs into a deployable .jar file, after all of the files have already been compiled, and successfully (and incorrectly) passed compilation.

Best Practice: Create a common interface for the remote interface:
package com.titan.ship;
import java.rmi.RemoteException;

public interface ShipBusiness {
    public String getName() throws RemoteException;
    public void setName(String name) throws RemoteException;
}

Once the business interface is defined, it can be extended by the remote interface. The remote interface extends both the ShipBusiness and the EJBObject interfaces, giving it all the business methods and the EJBObject methods that the container will implement at deployment time:

package com.titan.ship;
import javax.ejb.EJBObject;

public interface Ship extends ShipBusiness, javax.ejb.EJBObject {
}

Finally, we can implement the business interface in the bean class as we would any other interface:

public class ShipBean implements ShipBusiness, javax.ejb.EntityBean {
    public int id;
    public String name;

    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }
}


Best Practice: Use a Factory to retrieve EJB Home interfaces:
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();
      }

      public static EJBHomeFactory getInstance() throws NamingException {
          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;
      }
}
Best Practice: Use EJB 2.0 Local interfaces instead of Remote:
Reference: Local interfaces how to
ejb-jar.xml
<ejb-jar> <enterprise-beans> <session> <ejb-name>CommonService</ejb-name> <home>com.app.services.commonserviceejb.CommonServiceHome</home> <remote>com.app.services.commonserviceejb.CommonService</remote> <ejb-class>com.app.services.commonserviceejb.CommonServiceEJB</ejb-class> <session-type>Stateless</session-type> <transaction-type>Bean</transaction-type> <ejb-local-ref> <ejb-ref-name>PlayerEntityLocal</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <local-home>com.app.services.playerentityejb.PlayerEntityHome</local-home> <local>com.app.services.playerentityejb.PlayerEntity</local> <ejb-link>PlayerEntity</ejb-link> </ejb-local-ref> </session> <entity> <ejb-name>PlayerEntity</ejb-name> <local-home>com.app.services.playerentityejb.PlayerEntityHome</local-home> <local>com.app.services.playerentityejb.PlayerEntity</local> <ejb-class>com.app.services.playerentityejb.PlayerEntityBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>java.lang.String</prim-key-class> <reentrant>False</reentrant> </entity> </enterprise-beans> </ejb-jar>

The CORRECT way to retrieve the Local EJB object:
As this is how the EJB spec requires from code that is calling local ejbs.

Context ctx = new InitialContext();
      PlayerEntityHome playerEntityHome =
           (PlayerEntityHome)ctx.lookup("java:comp/env/PlayerEntityLocal");


The INCORRECT way to retrieve the Local EJB object:
This code snippet would not be transferrable across app. servers.

Context ctx = new InitialContext();
      PlayerEntityHome playerEntityHome =
           (PlayerEntityHome)ctx.lookup("PlayerEntityLocal");


If you are retrieving the Local EJB from a Servlet you need to add this code
bit to your web.xml file, so you will be able to get the local interface of 
your entity bean:

web.xml
<ejb-local-ref> <ejb-ref-name>PlayerEntityLocal</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <local-home>com.app.services.playerentityejb.PlayerEntityHome</local-home> <local>com.app.services.playerentityejb.PlayerEntity</local> <ejb-link>PlayerEntity</ejb-link> </ejb-local-ref> If you are not going to lookup the Local EJB from a Servlet, then you don't need to define your Local EJB in the web.xml. If you are going to lookup the Local EJB from an object that was created by a Session Bean (DAO), or the Session Bean itself, then defining it only in the ejb-jar.xml will be enough.

Published EJB Articles
Do you really need EJBs?
Optimizing EJBs
7 Rules for Optimizing EJBs
12 tips for better EJB performance!