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.
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. 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 EJBHomegetEJBHome
(); public abstract HandlegetHandle
(); public abstract ObjectgetPrimaryKey
(); public abstract booleanisIdentical
(EJBObject obj); public abstract voidremove
(); }
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.[43] 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(Ship
ship)
. 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.
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. }
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 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.
[43] 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.