EJB 2.0: The Local Client API

EJB 2.0 introduces the concept of local component interfaces, which are intended to provide different semantics and a different execution context for enterprise beans that work together within the same EJB container system.

When two or more enterprise beans interact, they are usually co-located; that is, they are deployed in the same EJB container system and execute within the same Java Virtual Machine. Co-located beans do not need to use the network to communicate. Since they are in the same JVM, they can communicate more directly by avoiding the overhead of Java RMI-IIOP. However, the EJB 1.1 specification required that even co-located beans utilize Java RMI-IIOP semantics for communication. This specification did not require that a network protocol be used, but it did require that Java RMI-IIOP types be used and that all objects be copied, rather then referenced, when passed as arguments to methods.

EJB 1.1 vendors, determined to squeeze every ounce of performance out of their servers, optimized co-located beans. The optimizations required that the EJB container interpose on invocations from one EJB to another but allowed vendors to avoid the overhead of the network; arguments and returned values were processed within the JVM by the container and not serialized over the network. However, arguments still had to be copied, rather then passed by reference, which slowed invocations down slightly. Many, if not most, vendors offered a proprietary switch that allowed deployers to turn off the copy semantics of co-located beans, so that objects passed from one enterprise bean to another in the same container system could be passed by reference rather then copied, which improved performance.

Optimizations of co-located beans that included switches for toggling argument copying eventually became so pervasive across vendors that Sun Microsystems decided to make this option part of the specification. This is why local component interfaces, which make up the Local Client API, were introduced.

Session and entity beans can choose to implement either remote or local component interfaces, or both. Any type of enterprise bean (entity, session, or message-driven) can become a co-located client of a session or entity bean: for example, a message-driven bean can call methods on co-located entity beans using its local component interfaces. The Local Client API is similar in many respects to the Remote Client API, but it is less complicated. The Local Client API is composed of two interfaces, the local and local home interfaces, which are similar to the remote and remote home interfaces discussed earlier in this chapter.

While explaining the local and local home interfaces and how they are used, we will create these interfaces for the Cabin EJB we developed in Chapter 4.

The Local Interface

The local interface, like the remote interface, defines the enterprise bean’s business methods that can be invoked by other co-located beans (co-located clients). These business methods must match the signatures of business methods defined in the bean class. For example, the CabinLocal interface is the local interface defined for the Cabin EJB:

package com.titan.cabin;

import javax.ejb.EJBException;

public interface CabinLocal extends javax.ejb.EJBLocalObject {
    public String getName() throws EJBException;
    public void setName(String str) throws EJBException;
    public int getDeckLevel() throws EJBException;
    public void setDeckLevel(int level) throws EJBException;
    public int getShipId() throws EJBException;
    public void setShipId(int sp) throws EJBException;
    public int getBedCount() throws EJBException;
    public void setBedCount(int bc) throws EJBException; 
}

The CabinLocal interface is basically the same as the CabinRemote interface we developed in Chapter 4, with a couple of key differences. Most importantly, the CabinLocal interface extends the javax.ejb.EJBLocalObject interface and its methods do not throw the java.rmi.RemoteException.

Local interfaces must extend the javax.ejb.EJBLocalObject interface, while remote interfaces must extend the javax.ejb.EJBObject interface:

package javax.ejb;
 
import javax.ejb.EJBException;
import javax.ejb.RemoteException;
 
public interface EJBLocalObject {
    public EJBLocalHome getEJBLocalHome() throws EJBException;
    public Object getPrimaryKey() throws EJBException;
    public boolean isIdentical(EJBLocalObject obj) throws EJBException;
    public void remove() throws RemoveException, throws EJBException;
}

The EJBLocalObject interface defines several methods that should be familiar to you from the previous sections. The getEJBLocalHome() method returns a local home object; the getPrimaryKey() method returns the primary key (entity beans only); the isIdentical() method compares two local EJB objects; and the remove() method removes the enterprise bean. These methods work just like their corresponding methods in the javax.ejb.EJBObject interface. It is also important to point out that the EJBLocalObject, unlike the EJBObject, does not extend the java.rmi.Remote interface, because it is not a remote object.

You may have noticed that the EJBLocalObject, unlike the EJBObject, does not define a getHandle() method. EJB local interfaces do not define a getHandle() method because a javax.ejb.Handle is not relevant when the client and enterprise beans are located in the same EJB container system. The Handle is a serializable reference that makes it easier for a remote client to obtain a reference to an enterprise bean on a remote node on a network. Since co-located beans are located in the same container system, not across a network, the Handle object is not necessary.

The EJBLocalObject and the local interfaces that extend it do not throw a java.rmi.RemoteException. These interfaces are used for co-located beans in the same JVM, not for accessing remote enterprise beans, so the RemoteException--which is used to report network or partial failures of a remote system—is not relevant. Instead, the local interfaces and EJBLocalObject throw the EJBException . The EJBException is thrown from local interface methods automatically by the container when some kind of container system error occurs or when a transaction errors occurs that causes the bean instance to be discarded.

The EJBException is a subtype of the java.lang.RuntimeException and is therefore an unchecked exception. Unchecked exceptions do not have to be declared in the throws clause of the local component interfaces and do not require the client to explicitly handle them using try/catch blocks. However, we choose to declare the EJBExeption in the methods signatures of the CabinLocal interface to better communicate to the client application that this type of exception is possible.

The Local Home Interface

The local home interface, like the remote home interface, defines the enterprise bean’s life-cycle methods that can be invoked by other co-located beans (co-located clients). The life-cycle methods of the local home interface include find, create, and remove methods similar to those of the remote home interface. The CabinHomeLocal, the local home interface of the Cabin EJB, is shown in the following listing:

package com.titan.cabin;

import javax.ejb.EJBException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface CabinHomeLocal extends javax.ejb.EJBLocalHome {

    public CabinLocal create(Integer id)
        throws CreateException, EJBException;

    public CabinLocal findByPrimaryKey(Integer pk)
        throws FinderException, EJBException;
}

The CabinHomeLocal interface is very similar to its counterpart, CabinHomeRemote, which we developed in Chapter 4. However, the CabinHomeLocal extends the javax.ejb.EJBLocalHome and does not throw the RemoteException from its create and find methods. You may also have noticed that the type returned from the create() and findByPrimaryKey() methods is the CabinLocal interface, not the remote interface of the Cabin EJB. The create and find methods of local home interfaces will always return EJB objects that implement the local interface of that enterprise bean.

Local interfaces must always extend the EJBLocalHome interface, which is much simpler than its remote counterpart, EJBHome:

package javax.ejb;

import javax.ejb.RemoveException;
import javax.ejb.EJBException;

public interface EJBLocalHome {
    public void remove(Object primaryKey) 
        throws RemoveException, EJBException;
}

Unlike the EJBHome, the EJBLocalHome does not provide EJBMetaData and HomeHandle accessors. The EJBMetaData, which is primarily used by visual development tools, is not needed for co-located beans. In addition, the HomeHandle is not relevant to co-located client beans any more than the Handle was, because co-located beans do not need special network references. The EJBLocalHome does define a remove() method that takes the primary key as its argument; this method works the same as its corresponding method in the remote EJBObject interface.

Deployment Descriptor

When an enterprise bean uses local component interfaces, they must be declared in the XML deployment descriptor. This is a trivial matter. The changes are highlighted below:

<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise
JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

<ejb-jar>
    <enterprise-beans>
        <entity>
            <ejb-name>CabinEJB</ejb-name>
            <home>com.titan.cabin.CabinHomeRemote</home>
            <remote>com.titan.cabin.CabinRemote</remote>            <local-home>com.titan.cabin.CabinHomeLocal</local-home>
            <local>com.titan.cabin.CabinLocal</local>
            <ejb-class>com.titan.cabin.CabinBean</ejb-class>

In addition to adding the <local-home> and <local> elements, the <ejb-ref> element is changed to an <ejb-local-ref> element, indicating that a local EJB object is being used instead of a remote one:

<ejb-local-ref>
    <ejb-ref-name>ejb/CabinHomeLocal</ejb-ref-name>
    <ejb-ref-type>Entity</ejb-ref-type>
    <local-home>com.titan.cabin.CabinHomeLocal</local-home>
    <local>com.titan.cabin.CabinLocal</local>
</ejb-local-ref>

Using the Local Client API

We can easily redesign the TravelAgent EJB developed in Chapter 4 so that it uses the Cabin EJB’s local component interfaces instead of the remote component interfaces:

public String [] listCabins(int shipID, int bedCount) {

    try {
        javax.naming.Context jndiContext = new InitialContext();        CabinHomeLocal home = (CabinHomeLocal)
            jndiContext.lookup("java:comp/env/ejb/CabinHomeLocal");
    
        Vector vect = new Vector();
        for (int i = 1; ; i++) {
            Integer pk = new Integer(i);            CabinLocal cabin;
            try {
                cabin = home.findByPrimaryKey(pk);
            } catch(javax.ejb.FinderException fe) {
                break;
            }
            // Check to see if the bed count and ship ID match.
            if (cabin.getShipId() == shipID && 
                cabin.getBedCount() == bedCount) {
                String details = 
                i+","+cabin.getName()+","+cabin.getDeckLevel();
                vect.addElement(details);
            }
        }
        
        String [] list = new String[vect.size()];
        vect.copyInto(list);
        return list;
       
    } catch(NamingException ne) {
         throw new EJBException(ne);
    }    
}

The bold code text shows the three small changes that were needed. The most important change is using local component interfaces for the Cabin EJB instead of remote interfaces. In addition, we do not need to use the PortableRemoteObject.narrow() method when obtaining the Cabin EJB’s home object from the JNDI ENC. This is because we are not accessing the home across the network; we are accessing it from within the same JVM, so we know that the client is a Java client and that no network protocol is in use. The elimination of this method makes the code much easier to look at. We also changed the try/catch block so that it will catch the javax.naming.NamingException and not the EJBException thrown by any of the local component interface methods. In this case it is easier to allow those exception to propagate directly to the container, where they can be handled better. Chapter 14 covers exception handling in detail.

Using the Local Client APIPlease refer to Workbook Exercise 5.3, The Local Component Interfaces. This workbook is available free, in PDF format, at http://www.oreilly.com/catalog/entjbeans3/workbooks.

When to Use Local Component Interfaces

Entity and session beans can use either local or remote component interfaces, or they may use both so that the bean is accessible from remote and local clients. Any time you are going to have enterprise beans accessing each other from within the same container system you should seriously consider using local component interfaces, as their performance is likely to be better than that of remote component interfaces. With the Local Client API, no network infrastructure is involved and there is no argument copying.

Because local clients are in the same JVM, there is no need for network comminations. When a co-located client bean invokes a method on a local EJB object, the container intervenes to apply transaction and security management, but then delegates the invocation directly to the target bean instance. No network. Very little overhead. The complete absence of a network can increase the speed of an RMI loop by a full magnitude, so it is very desirable. However, relying on the Local Client API eliminates the location transparency of enterprise bean references. In other words, you cannot move the bean to a different server on the network, because it must be co-located. With the Remote Client API, you can move enterprise beans from one server to another without impacting the bean code. In other words, the Remote Client API provides better location transparency: you are not dependent on the location of the enterprise bean in order to invoke its methods.

The Local Client API also passes object arguments by reference from one bean to another, as illustrated in Figure 5-5. This means that an object passed from enterprise bean A to enterprise bean B is referenced by both beans, so if B changes its values A will see those changes.

Passing by reference with the Local Client API

Figure 5-5. Passing by reference with the Local Client API

With the Remote Client API, objects’ arguments (parameters or return values) are always copied, so changes made to one copy are not reflected in the other (see Figure 5-1).

Passing by reference can create some pretty dangerous situations if the enterprise beans that share the object reference are not coded carefully. In most cases, it is best to pass immutable objects without copying them first. Chapter 16 discusses using immutable dependent objects, which are also good candidates for passing by reference. However, if you are going to pass an object that is not immutable, the object should be copied before it is passed. That’s the rule of thumb, unless the passed objects are not modified by any beans other than the enterprise beans from which they came.

Are Local Component Interfaces Necessary?

Some vendors have argued that local component interfaces were never needed in the first place and only added more complexity and no real functionality to the EJB platform. This is a reasonable argument, considering that most EJB 1.1 vendors were already optimizing the Remote Client API for co-located beans. As we discussed earlier, these vendors did not use the network for co-located bean invocations and provided argument-copy switches.

So why was the Local Client API needed at all? An alternative would have been to amend the specification of the Remote Client API to account for co-located container optimizations, making those optimizations standard configurable attributes in the deployment descriptor. The only problem with that solution is semantics. The remote interfaces extend java.rmi.Remote, which is specifically intended for remote objects. In addition, all subtypes of the java.rmi.Remote interface are required to throw java.rmi.RemoteException types from methods. It may have been difficult for developers to distinguish between a co-located EJB object and a remote EJB object, which is important if one is passing objects by reference while the other passes them by copy.

However, it is also going to be difficult for EJB developers to get used to local component interfaces and to use them effectively. With local component interfaces, you are locked into a single JVM, and you cannot move beans from one container to the next at will. The arguments for and against the local component interfaces both have their merits. Whether you agree with the need for the Local Client API or not, local interfaces are here to stay, and you should learn to use them appropriately.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset