6.3. EJB TutorialNow let's look at the generic steps that you take to implement an Enterprise JavaBeans component. In all of the examples that follow, we're showing you the "traditional" approach to writing EJBs: you create the client and home interfaces (where needed), the implementation classes, and the deployment descriptor by hand. A number of tools are available to simplify EJB development by creating these elements for you. Chapter 21 discusses XDoclet annotations that can be used to automatically generate interfaces and deployment descriptors for EJBs using special Javadoc comments in your code. At the end of this chapter we also give a preview of similar features being integrated into the EJB 3.0 specification. But even with current and upcoming development aids, it's important to understand the underlying EJB programming model so that you can apply the tools effectively. As we discussed earlier, every type of EJB (session, entity, message-driven) must include a bean implementation class. This class houses the bean's business logicit's the heart of the component. All the other trappings of the EJB either give clients ways to access the bean or help the container manage the bean. If you are creating either an entity or a session bean, you also need to provide:
The EJB object implementation must implement all the methods defined in its remote and local interfaces, provide methods that correspond to the methods on its home interface for creating or finding the bean, and also implement a set of required methods used by the EJB container to manage the bean. Message-driven beans require only the bean implementation class. They don't have home interfaces or client interfaces since the client interface is implemented using JMS message passing (we'll give more details on creating and using message-driven beans later in the chapter). To demonstrate the various components that make up an Enterprise JavaBeans object, we'll look at a simple example: a profile server. The profile server is a bean that provides profile information for named users. This profile information consists of name/value pairs that might represent preferences in an application, historical usage patterns, and the like. You might see a profile server running behind an online information service, personalizing the content and appearance of the site. After we've gone through this general example of writing a bean, we'll look more closely at the differences between implementing session and entity beans. 6.3.1. Client InterfacesAlthough the bean implementation is central to the functions of the EJB component, it's typical to start putting together an EJB by defining its client interface(s). Client interfaces contain declarations of the methods that are available to clients. 6.3.1.1. Remote client interfacesA remote client interface for our ProfileManager is shown in Example 6-1. A remote EJB client interface must extend the javax.ejb.EJBObject interface. EJBObject in turn extends the java.rmi.Remote interface, which makes the remote client interface an RMI remote interface as well. Example 6-1. Remote interface for the profile manager beanimport javax.ejb.*; import java.rmi.RemoteException; public interface ProfileManager extends EJBObject { public ProfileBean getProfile(String acctName) throws NoSuchPersonException, RemoteException; } The ProfileManager interface defines a single method, getProfile( ), that accepts an account name as its only argument. It returns a ProfileBean object containing the profile information for the person named. If the person's profile can't be found on the server, a NoSuchPersonException is thrown. This is an application-specific exception whose implementation isn't discussed in this chapter. Since the ProfileManager interface is an RMI remote interface, its methods must throw RemoteException in case some RMI communication problem occurs during a method call. Also, the arguments and return values for the methods have to be Serializable, or they need to be exportable RMI objects themselves. Our getProfile( ) method returns a ProfileBean object, which we'll implement as a Serializable object, as shown below in Example 6-2. The ProfileBean, not shown here but included in the example source code download for the book, is a fairly straightforward Java bean: it simply has methods that allow you to get and set the profile entry values. 6.3.1.2. Local client interfacesA local client interface for an EJB is defined in a similar way, but the rules and usage of the interface differ from those of its remote counterpart. As we've mentioned, local interfaces are used by clients that reside in the same JVM as the EJB itself, so they follow the same argument-passing and return-value rules as normal, nonremote Java classes: objects are passed by reference, and basic data types are passed by value. No remote operations are involved in the use of a local interface, so there's no need to ensure that method arguments and return values are Remote or Serializable object types. And methods in a local interface aren't required to throw RemoteException. Local interfaces extend the EJBLocalObject interface, which is a simplified, nonremote interface. A local interface for our profile server is shown in Example 6-2. Example 6-2. Local interface for the profile serverimport javax.ejb.*; public interface ProfileManagerLocal extends EJBLocalObject { public ProfileBean getProfile(String acctName) throws NoSuchPersonException; } An EJB implementation class needs to provide implementations for all the methods exposed in its remote and local interfaces. 6.3.2. Home InterfacesThe client needs a way to create a reference to a profile server, so we have to provide a home interface for our bean. Since clients use home interfaces directly, and since clients can be either remote or local, an EJB can also have remote and local home interfaces. Example 6-3 shows a remote home interface for our EJB. It provides a single create( ) method that takes no arguments and returns the bean's remote interface type, ProfileManager. Example 6-3. Remote home interface for the profile server beanimport javax.ejb.*; import java.rmi.RemoteException; public interface ProfileManagerHome extends EJBHome { public ProfileManager create( ) throws CreateException, RemoteException; } A remote home interface for an EJB object extends the javax.ejb.EJBHome interface. The remote home interface is also an RMI remote interface since EJBHome extends java.rmi.Remote. The home interface can contain multiple create( ) methods that take various initialization arguments to create the bean. A corresponding local home interface is shown in Example 6-4. Example 6-4. Local home interface for the profile server beanimport javax.ejb.*; public interface ProfileManagerLocalHome extends EJBLocalHome { public ProfileManagerLocal create( ) throws CreateException; } For each create( ) method on a home interface, the EJB object implementation must have a matching ejbCreate( ) method that takes the same arguments. In either remote or local home interfaces, create( ) methods are required to throw javax.ejb.CreateException, in case some error occurs during the EJB creation process. Create methods (and all other methods) on remote home interfaces must also throw java.rmi.RemoteException (or one of its parent exceptions) since the home interface is an RMI remote interface and some sort of network/communication error could occur between the client and the home implementation on the server. If the corresponding ejbCreate( ) method on the bean implementation throws any other exceptions, the create( ) method has to include these in its throws clause as well. As we'll see in our bean implementation in the next section, the bean's ejbCreate( ) method doesn't throw any special exceptions, so we don't need to add any additional exceptions in the home interfaces. Home interfaces for entity beans can also include finder methods, used to find previously created persistent entity beans. We'll discuss them when we talk about entity beans in detail. 6.3.3. The Bean ImplementationNow that we have a home interface that lets clients create EJB references and interfaces that describe what the EJB can do for the client, we need to actually implement the EJB object itself. Every bean implementation class can be divided into three sets of methods: business method implementations, container callback methods, and internal utility methods used by the bean class itself. A complete implementation class for the ProfileManager EJB can be found in the downloadable source code for the book; we'll just look at the highlights here in terms of these three categories of methods. The business method implementations must match the business methods that are exposed in the EJB's client interfaces. For our ProfileManagerBean, we have only one business method in the client interfaces: getProfile( ). Our implementation of this business method is simple enough: public Profile getProfile(String name) throws NoSuchPersonException { // First validate the provided name, throw a NoSuchPersonException // if it's not valid if (!validateName(name)) { throw new NoSuchPersonException("No user found matching name \"" + name + "\""); } // If the name is valid, find/create the profile. Here, we simply // create a new profile Java bean (e.g., no persistence is provided) Profile profile = new Profile(name); return profile; } We validate the name argument and create a new Profile JavaBean object for the named user. Note that this highly simplified implementation provides no persistence featuresevery time a username is given, a new, empty profile is created and returned. We'll see later in the tutorial how to integrate persistence into the bean implementation. The validateName( ) method is an internal utility method: it's private, and it's not included in any of the client interfaces, so it's not available for clients to invoke. Every implementation class for any EJB object must implement the javax.ejb.EnterpriseBean interface. This is typically done indirectly, through the SessionBean, EntityBean, or MessageDrivenBean interface. These interfaces include the required callback/notification methods that the EJB container needs in order to effectively manage the lifecycle of the EJB at runtime. Our sample EJB implementation is a session bean, so the ProfileManagerBean class implements the SessionBean interface. The implementation class must follow a few other rules. The class must be declared as public to allow the container to introspect the class when generating the classes that hook the bean to the container and to allow the container to invoke methods on the bean directly where necessary. The bean class doesn't implement the bean's remote or local interface. This may seem a bit strange at first, especially if you're familiar with remote object systems like RMI and CORBA, since the purpose of the bean is to provide a concrete implementation of the EJB's interfaces, and in these other contexts this is done through direct inheritance. But in an EJB context, the EJB container always mediates between a client method request and the actual call on the corresponding method on a bean instance. As long as the container is explicitly told the EJB's various interfaces and its implementation class (and we'll see how to do that with deployment descriptors in a later section), it has all the information it needs to make this connection. When the EJB server generates the classes that bridge the bean to the container, it also provides a class that implements the remote interface and acts as a proxy to the EJB class itself. The EJB container callbacks actually make up the bulk of the implementation class. These methods serve as the hooks the EJB container uses to manage the bean as a component. They also implement some of the functionality provided to the client through the client home and stub interfaces. For example, since the home interfaces for this EJB include a no-argument create( ) method, we must provide an equivalent ejbCreate( ) method that will be invoked by the container on a bean instance when the create( ) method is invoked by a client on a home interface. The ejbCreate( ) method is a container callbackit gives the EJB implementation object a chance to initialize any resources it might need to operate properly. In our case, resources must be set up (because our implementation is so simple), but we still need to provide a method implementation for the container: public void ejbCreate( ) { System.out.println("ProfileManagerBean created."); } The other remaining methods on the implementation class, ejbActivate( ), ejbRemove( ), ejbPassivate( ), and setSessionContext( ), are also container callbacks, invoked by the container to manage the EJB instance during its lifetime. All session and entity bean implementations have to provide the following container callback methods, which are used in the following ways:
In our example, the ProfileManagerBean doesn't need to perform any actions in these methods, so they are implemented as empty methods that simply print message to standard output, indicating that they have been called. |