This book discourages implementing the remote interface in the bean class, even though 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.
There should be no difference, other than
the missing java.rmi.RemoteException
, between the
business methods defined in the ShipBean
class and
their corresponding business methods defined in the
ShipRemote
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.ShipRemote
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 reasons. First, the remote
interface is actually an extension of the
javax.ejb.EJBObject
interface, which you learned
about in Chapter 5. 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 shown
here:
public class ShipBean implements ShipRemote {
When you recompile the ShipBean
, you should have
five errors stating that the ShipBean
must be
declared abstract
because it does not implement
the methods from the javax.ejb.EJBObject
interface. 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 should not 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.[60] In normal Java programming, an
object passes a reference to itself using the
this
keyword. In EJB,
however, clients—even bean clients—are allowed to
interact only 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 reference instead. The fact
that a bean class does not 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
will not compile if you attempt to use this
as a
remote reference. For example, assume that the
ShipBean
needs to call
someMethod(ShipRemote
ship)
. It
cannot simply call someMethod(this)
, because
ShipBean
does not implement
ShipRemote
. If, however, the bean instance
implements the remote interface, you could mistakenly pass the bean
instance reference using the this
keyword to
another bean.
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. }
In EJB 2.0, the
bean class should not implement the local
interface for the exact same reasons that it should not implement the
remote interface. Specifically, the bean instance would have to
support the methods of the
javax.ejb.EJBLocalObject
interface, which are not
germane to the bean class.
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 the business 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
ShipRemote
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 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 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 ShipRemote 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 this case, we chose 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 for you 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.
[60] This is frequently done in loopbacks, where the invokee needs information about the invoker. Loopbacks are discouraged in EJB because they require reentrant programming, which should be avoided.