Book Home Enterprise JavaBeans Search this book

9.5. Implementing a Common Interface

This book discourages implementing the remote interface in the bean class. This makes it a little more difficult to enforce consistency between the business methods defined in the remote interface and the corresponding methods on the bean class. There are good reasons for not implementing the remote interface in the bean class, but there is also a need for a common interface to ensure that the bean class and remote interface define the same business methods. This section describes a design alternative that allows you to use a common interface to ensure consistency between the bean class and the remote interface.

9.5.1. Why the Bean Class Shouldn't Implement the Remote Interface

There should be no difference, other than the missing java.rmi.RemoteException, between the business methods defined in the ShipBean and their corresponding business methods defined in the Ship interface. EJB requires you to match the method signatures so that the remote interface can accurately represent the bean class on the client. Why not implement the remote interface com.titan.Ship in the ShipBean class to ensure that these methods are matched correctly?

EJB allows a bean class to implement its remote interface, but this practice is discouraged for a couple of very good reasons. First, the remote interface is actually an extension of the javax.ejb.EJBObject interface, which you learned about in Chapter 5, "The Client View". This interface defines several methods that are implemented by the EJB container when the bean is deployed. Here is the definition of the javax.ejb.EJBObject interface:

public interface javax.ejb.EJBObject extends java.rmi.Remote {
    public abstract EJBHome getEJBHome();
    public abstract Handle getHandle();
    public abstract Object getPrimaryKey();
    public abstract boolean isIdentical(EJBObject obj);
    public abstract void remove();
}

The methods defined here are implemented and supported by the EJB object for use by client software and are not implemented by the javax.ejb.EntityBean class. In other words, these methods are intended for the remote interface's implementation, not the bean instance's. The bean instance implements the business methods defined in the remote interface, but it does so indirectly. The EJB object receives all the method invocations made on the remote interface; those that are business methods (like the getName or setCapacity methods in Ship) are delegated to the bean instance. The other methods, defined by the EJBObject, are handled by the container and are never delegated to the bean instance.

Just for kicks, change the ShipBean definition so that it implements the Ship interface as show here:

public class ShipBean implements Ship {

When you recompile the ShipBean, you should have five errors stating that the ShipBean must be declared abstract because it doesn't implement the methods from the javax.ejb.EJBObject. EJB allows you to implement the remote interface, but in so doing you clutter the bean class's definition with a bunch of methods that have nothing to do with its functionality. You can hide these methods in an adapter class; however, using an adapter for methods that have empty implementations is one thing, but using an adapter for methods that shouldn't be in the class at all is decidedly bad practice.

Another reason that beans should not implement the remote interface is that a client can be an application on a remote computer or it can be another bean. Beans as clients are very common. When calling a method on an object, the caller sometimes passes itself as one of the parameters.[1] In normal Java programming, an object passes a reference to itself using the this keyword. In EJB, however, clients, even bean clients, are only allowed to interact with the remote interfaces of beans. When one bean calls a method on another bean, it is not allowed to pass the this reference; it must obtain its own remote reference from its context and pass that instead. The fact that a bean class doesn't implement its remote interface prevents you from passing the this reference and forces you to get a reference to the interface from the context. The bean class won't compile if you attempt to use this as a remote reference. For example, assume that the ShipBean needs to call someMethod(Shipship). It can't simply call someMethod(this) because ShipBean doesn't implement Ship. If, however, the bean instance implements the remote interface, you could mistakenly pass the bean instance reference using the this keyword to another bean.

[1] This is frequently done in loopbacks where the invokee will need information about the invoker. Loopbacks are discouraged in EJB because they require reentrant programming, which should be avoided.

Beans should always interact with the remote references of other beans so that method invocations are intercepted by the EJB objects. Remember that the EJB objects apply security, transaction, concurrency, and other system-level constraints to method calls before they are delegated to the bean instance; the EJB object works with the container to manage the bean at runtime.

The proper way to obtain a bean's remote reference, within the bean class, is to use the EJBContext. Here is an example of how this works:

public class HypotheticalBean extends EntityBean {
    public EntityContext ejbContext;
    public void someMethod() throws RemoteException {
          
          Hypothetical mySelf = (Hypothetical) ejbContext.getEJBObject();
          
          // Do something interesting with the remote reference.
    }
    // More methods follow.
}

9.5.2. The Business Interface Alternative

Although it is undesirable for the bean class to implement its remote interface, we can define an intermediate interface that is used by both the bean class and the remote interface to ensure consistent business method definitions. We will call this intermediate interface thebusiness interface.

The following code contains an example of a business interface defined for the Ship bean, called ShipBusiness. All the business methods formerly defined in the Ship interface are now defined in the ShipBusiness interface. The business interface defines all the business methods, including every exception that will be thrown from the remote interface when used at runtime:

package com.titan.ship;
import java.rmi.RemoteException;

public interface ShipBusiness {
    public String getName() throws RemoteException;
    public void setName(String name) throws RemoteException;
    public void setCapacity(int cap) throws RemoteException;
    public int getCapacity() throws RemoteException;
    public double getTonnage() throws RemoteException;
    public void setTonnage(double tons) 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 int capacity;
    public double tonnage;

    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }
    public void setCapacity(int cap) {
        capacity = cap;
    }
    public int getCapacity() {
        return capacity;
    }
    public double getTonnage() {
        return tonnage;
    }
    public void setTonnage(double tons) {
        tonnage = tons;
    }

    // More methods follow...
}

In the case of the ShipBean class, we choose not to throw the RemoteException. Classes that implement interfaces can choose not to throw exceptions defined in the interface. They cannot, however, add exceptions. This is why the business interface must declare that its methods throw the RemoteException and all application exceptions. The remote interface should not modify the business interface definition. The bean class can choose not to throw the RemoteException, but it must throw all the application-specific exceptions.

The business interface is an easily implemented design strategy that will make it easier to develop beans. This book recommends that you use the business interface strategy in your own implementations. Remember not to pass the business interface in method calls; always use the bean's remote interface in method parameters and as return types.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.