Book Home Java Enterprise in a Nutshell Search this book

7.4. Implementing a Basic EJB Object

Now it's time to start talking about actually implementing an Enterprise JavaBeans component. No matter whether you are creating an entity bean or a session bean, there are three Java interfaces/classes you need to provide:

Home interface

The home interface is accessed directly by clients and used to create and/or find EJB objects of a specific type.

Remote bean interface

The remote interface for the bean is also used directly by clients. When a client creates or finds an EJB object through a home interface, it is given a reference to a stub that implements the remote interface for the bean. The remote interface defines the methods the EJB object exports to remote clients.

Bean implementation

The EJB object implementation itself must implement all the remote methods defined in its remote interface, provide methods that correspond to the methods on its home interface for creating and/or finding the bean, and also implement the methods used by the EJB container to manage the bean.

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 stateless session 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, etc. You might see a profile server running behind an online information service, allowing users to personalize the content and appearance of the site when they enter. After we've gone through this general example of writing a bean, we'll look more closely at the differences between implementing session beans and entity beans.

Table 7-1 shows how the methods of the home interface, the remote interface, and the bean implementation are related to each other.

Table 7-1. Related Methods

Home Interface Method

Remote Interface Method

EJB Object Method

remote-type create(args) throws CreateException, RemoteException;

N/A

public void ejbCreate(args); // Session beans publicprimary-key-type ejbCreate(args); // Entity beans

remote-type or Enumeration findBymethod(args) throws FinderException, RemoteException; // Entity beans only

N/A

publicprimary-key-type or Enumeration ejbFindBymethod(args); // Entity beans only

public void remove();

public void remove();

public void ejbRemove();

N/A

Business method (must throw RemoteException)

Business method, same signature (must throw RemoteException)

7.4.1. Home Interface

The client needs a way to create a local reference to a profile server, so we have to provide a home interface for our bean, as shown in Example 7-1. This home interface provides a single create() method that takes no arguments and returns the bean's remote interface type, ProfileServer.

Example 7-1. Home Interface for the Profile Server Bean

import javax.ejb.*;
import java.rmi.RemoteException;

public interface ProfileServerHome extends EJBHome {
  public ProfileServer create() throws CreateException, RemoteException;
}

The home interface for an EJB object extends the javax.ejb.EJBHome interface. The 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 for the bean to be created. The create() method returns a reference to our bean's remote interface (ProfileServer, in this case).

As shown in Table 7-1, for each create() method on the home interface, the EJB object implementation must have a matching ejbCreate() method that takes the same arguments. The create() method on the home interface has to declare that it throws java.rmi.RemoteException, since the home interface is an RMI remote interface. It also has to throw javax.ejb.CreateException, in case some error occurs during the EJB creation process (as opposed to some general RMI-related problem). 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. In this example, the bean's ejbCreate() method doesn't throw any exceptions, so we don't need to add any additional exceptions here.

Home interfaces for entity beans can also include finder methods, used to find existing persistent entity beans that were previously created. We'll discuss them in detail a bit later, when we talk about entity beans.

7.4.2. Remote Interface

You usually start putting together an EJB by defining its remote interface. This interface contains declarations of the methods that are available to remote clients, so it really points to the heart of the EJB object. The remote interface for our ProfileServer is shown in Example 7-2. A remote EJB interface must extend the javax.ejb.EJBObject interface. EJBObject in turn extends the java.rmi.Remote interface, which makes the EJB remote interface an RMI remote interface as well.

Example 7-2. Remote Interface for the Profile Server Bean

import javax.ejb.*;
import java.rmi.RemoteException;
import java.rmi.Remote;

public interface ProfileServer extends EJBObject {
  public Profile getProfile(String name)
    throws NoSuchPersonException, RemoteException;
}

The ProfileServer interface defines a single remote method, getProfile(), that accepts a username as its only argument. It returns a Profile object, containing the profile information for the person named. If the person's profile cannot be found on the server, a NoSuchPersonException is thrown. This is an application-specific exception whose implementation isn't shown here. Since the ProfileServer 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, and/or they need to be exportable RMI objects themselves. Our getProfile() method returns a Profile object, which we'll implement as an RMI-exportable object. The remote interface for Profile is shown in Example 7-3. The interface has only two remote methods, one to set profile entry values and one to get those values. Note: the implementation of the Profile remote interface isn't shown in this chapter.

Example 7-3. RMI Remote Interface for a Profile Object

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Profile extends Remote {
  public String getProfileEntry(String name) throws RemoteException;
  public void setProfileEntry(String name, String value)
    throws RemoteException;
}

When you deploy your EJB object, you can specify who is allowed to call each method on your bean through the remote interface. In this case, we might want only certain clients to be able to query for user profiles, so we might want to limit access to the getProfile() method on our ProfileServer bean. We discuss the access control features of bean deployment descriptors later in the chapter.

7.4.3. The Bean Implementation

Now that we have a home interface that lets clients create EJB references and a remote interface that describes the EJB methods, we need to actually implement the EJB object itself. Our ProfileServerBean is shown in Example 7-4. If you are familiar with RMI, this should look like an RMI server object, with some extra methods included. These extra methods are the hooks the EJB container uses to manage the bean as a component. At the end of the class is the implementation of the getProfile() method from the remote interface. The ejbCreate() method is also included here, to match the create() method on the home interface.

Example 7-4. The ProfileServerBean Implementation

import javax.ejb.*;
import java.rmi.RemoteException;

public class ProfileServerBean implements SessionBean {
  private SessionContext mContext = null;
  
  // Session bean methods
  public void ejbPassivate() {
    System.out.println("ProfileServerBean passivated.");
  }

  public void ejbActivate() {
    System.out.println("ProfileServerBean activated.");
  }

  public void ejbCreate() {
    System.out.println("ProfileServerBean created.");
  }

  public void ejbRemove() {
    System.out.println("ProfileServerBean removed.");
  }

 // Get session context from container
  public void setSessionContext(SessionContext context) {
    System.out.println("ProfileServerBean context set.");
    mContext = context;
  }

 // Business methods
  public Profile getProfile(String name) throws NoSuchPersonException {
    // Here, we just create a ProfileImpl and return it.
    ProfileImpl profile = null;
    try {
      profile = new ProfileImpl(name);
    }
    catch (RemoteException re) {
      System.out.println("Failed creating profile for " + name);
      re.printStackTrace();
      throw new NoSuchPersonException();
    }

    return profile;
  }
}

The class for an EJB object must implement the javax.ejb.EnterpriseBean interface. This is usually done indirectly, through either the javax.ejb.SessionBean interface or the javax.ejb.EntityBean interface. In our example, we're defining a session bean, so the ProfileServerBean class implements the SessionBean interface.

The EJB 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 can optionally implement the bean's remote interface, but this isn't strictly required. In our case, we haven't implemented the bean's remote ProfileServer interface in the ProfileServerBean class. 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.[4]

[4] Depending on the server implementation and how it chooses to generate these classes, it may be useful for you to provide an EJB class that directly implements the remote interface, since it might eliminate one level of method indirection. This is server-dependent, however, so there's no guarantee that this will help (or hurt).

In fact, for practical reasons, you probably don't want your EJB implementation to implement the remote interface for your bean. The remote interface has to extend the EJBObject interface, which includes a set of abstract methods that clients can use to retrieve the bean's home interface, get the primary key for entity beans, etc. When you deploy your bean, you'll use tools provided by the EJB container tools to generate stub and skeleton classes for the remote interface that implement these methods from EJBObject. If you implement the remote interface with your bean implementation class, you have to provide implementations for the EJBObject methods as well.

All Enterprise JavaBean objects, whether they are session beans or entity beans, must implement the following methods:

public void ejbActivate()

Called by the container when the bean has been deserialized from passive storage on the server. Allows the bean to reclaim any resources freed during passivation (e.g., file handles, network connections) or restore any other state not explicitly saved when the bean was serialized.

public void ejbPassivate()

Called by the container just before the bean is to be serialized and stored in passive storage (e.g., disk, database) on the server. Allows the bean to release any non-serializable resources (e.g., open files, network connections).

public void ejbCreate(. . .)

Called after the client invokes one of the create() methods on the bean's home interface. The bean and its home interface usually provide at least one create()/ejbCreate() pair to allow the client to create new beans. Session beans are required to provide at least one create method, but create methods are optional on entity beans, since entity beans can also be acquired using finder methods. The container creates the bean object using one of its standard constructors and might create several beans of the same type at server startup to act as a pool for future client requests. The ejbCreate() method indicates that a client is ready to use the bean; the arguments indicate the identity or starting state of the bean. For entity beans, the return type of an ejbCreate() method should be the bean's primary key type (see the later section "Implementing Entity Beans" for more details).

public void ejbRemove()

Called by the container just before the bean is to be removed from the container and made eligible for garbage collection. The container may call this when all remote and local references to the bean have been removed.

These methods are used by the bean's container to notify the bean of various changes in its runtime state. In our example, the ProfileServerBean doesn't need to perform any actions in these methods, so they are included as empty methods that simply print messages to standard output, indicating that they have been called.



Library Navigation Links

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