What's in an EJB?

So far, you have been presented with a “black box” view of an EJB; it provides business functionality via an RMI remote interface, and it cooperates with its container to perform its duties. To understand, use, and ultimately write EJBs, you will need to know more in concrete terms about the Java programming artefacts that make up an EJB. In other words, what's in one?

The Business Interface

The primary purpose of an EJB is to deliver business or application logic. To this end, the bean developer will define or derive the business operations required of the bean and will formalize them in an RMI remote interface. This is referred to as the bean's business or remote interface as opposed to the home interface you will look at in a moment.

Note

You may see references to local EJB interfaces and wonder how these relate to the current discussion. Don't worry about local interfaces for the moment; they are covered on Day 6 when you examine entity EJBs.


The actual methods defined on the remote interface will depend on the purpose of the bean, but there are certain general rules concerning the interface:

  • As with any RMI-based interface, each method must be declared as throwing java.rmi.RemoteException in addition to any business-oriented exceptions. This allows the RMI subsystem to signal network-related errors to the client.

  • RMI rules also apply to parameters and return values, so any types used must either be primitive, Serializable, or Remote.

  • The interface must declare that it extends the javax.ejb.EJBObject interface. This provides a handful of basic methods that you will encounter as you progress.

Caution

Failure to conform to the rules about extending javax.ejb.EJBObject and throwing RemoteException will cause the interface to be rejected by tools that manipulate EJBs. Additionally, if you use parameter or return types that do not conform to the rules, your bean will compile and even deploy, but will fail with runtime errors.


The issue regarding object parameters and return values is worth considering for a moment. When you pass a parameter into a local method call, a reference to the original object is provided to be used within the method. Any changes to the state of the object are seen by all users of that object because they are sharing the same object. Also, there is no need to create a copy of the object—only a reference is passed.

On the other hand, when using RMI remote methods, objects that are serializable (implement the Serializable interface) are passed by value, whereas objects that are remote (that is, EJBs) are passed by reference. Pass by value means that a copy of the object is sent. This has several implications. First, users of a serializable object passed across a remote interface will no longer share the same object. Also, there may now be some performance costs associated with invoking a method through a bean's remote interface. Not only is there the cost of the network call, but also there is the cost of making a copy of the object so that it can be sent across the network. Most of the time, it will be serializable objects that are passed.

You can see an example of an EJB remote interface in Listing 4.1—in this case, the one for the Agency EJB used in the case study.

Listing 4.1. Remote Interface for the Agency EJB
package agency;

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

public interface Agency extends EJBObject
{
    String getAgencyName() throws RemoteException;

    Collection findAllApplicants()
      throws RemoteException;
    void createApplicant(String login, String name, String email)
      throws RemoteException, DuplicateException, CreateException;
    void deleteApplicant (String login)
      throws RemoteException, NotFoundException;

    Collection findAllCustomers() throws RemoteException;
    void createCustomer(String login, String name, String email)
      throws RemoteException, DuplicateException, CreateException;
    void deleteCustomer (String login)
      throws RemoteException, NotFoundException;

    Collection getLocations()
      throws RemoteException;
    void addLocation(String name)
      throws RemoteException, DuplicateException;
    void removeLocation(String code)
      throws RemoteException, NotFoundException;

    Collection getSkills()
      throws RemoteException;
    void addSkill(String name)
      throws RemoteException, DuplicateException;
    void removeSkill(String name)
      throws RemoteException, NotFoundException;

    List select(String table)
      throws RemoteException;
}
						

The interface lives in a package called agency, which will be common to all the classes that comprise the EJB. The definition imports java.rmi.* and javax.ejb.* for RemoteException and EJBObject, respectively. The rest of the interface is much as you would expect from any remote Java interface—in this case, passing Strings and returning serializable Collections.

Notice that all the methods must be declared as throwing RemoteException. This means that the client will have to handle potential exceptions that may arise from the underlying distribution mechanism. However, your application will probably want to employ exceptions itself to indicate application-level errors. These exceptions should be declared as part of the remote interface, as shown by the use of NotFoundException and DuplicateException in the Agency interface.

The Business Logic

After an interface is defined, there is the none-too-trivial task of implementing the business logic behind it. The business logic for an EJB will live in a class referred to as the bean. The bean consists of two parts:

  • The business logic itself, including implementations of the methods defined in the remote interface

  • A set of methods that allow the container to manage the bean's lifecycle.

Note

Although the bean itself must contain these elements, note that it is possible, indeed common, for non-trivial beans to delegate some or all of their business functionality to other, helper, classes.


Drilling down into these areas reveals more about the structure of an EJB.

Implementing the Business Interface

The first thing to note is that the bean itself does not implement the remote interface previously defined. This may seem slightly bizarre at first sight, because the equivalent RMI server would have to implement the associated remote interface. However, there is a very good reason for this.

As you will see later, it is possible to ask the container to apply services, such as access control, on behalf of the EJB simply by setting attributes in the EJB configuration information. To do this, the container must have some way of intercepting the method call from the client. When it receives such a method call, the container can then decide if any extra services need to be applied before forwarding the method call on to the bean itself. Sticking with the security example, the container would examine security information configured for the EJB before deciding whether to forward the method call to the bean or to reject it. The details about access control are covered on Day 15, “Security,” but you can see that it is necessary to interpose between the client and the bean to “automagically” deliver such services.

The interception is performed by a server-side object called the EJBObject (not to be confused with the interface of the same name). The EJBObject acts as a server-side proxy for the bean itself, and it is the EJBObject that actually implements the EJB's remote interface. Figure 4.2 shows the relationship between the client, the bean, and the EJBObject.

Figure 4.2. The EJBObject acts as a server-side proxy for the bean itself.


As shown in Figure 4.2, the client calls the business methods on the EJBObject implementation. The EJBObject applies the required extra services and then forwards the method calls on to the bean itself. The EJBObject is separate from the RMI stub and skeleton that provide the remote procedure call capability.

So, your bean must implement the business methods defined in the remote interface. The container uses the method signatures defined in the interface, together with the Java reflection API, to find the appropriate methods on the bean, so you must ensure that you use the correct method signatures. Despite this, the bean should not implement the remote interface itself (the reasons for this are discussed later). However, if you are using a developer tool that supports the creation of EJBs, it will generally generate empty methods for you to populate. Listing 4.2 contains the outlines of the business methods in the example AgencyBean.

Listing 4.2. Business Method Implementation Signatures for the AgencyBean
package agency;

import java.rmi.*;
import java.util.*;
import javax.ejb.*;
// Remaining imports removed for clarity

public class AgencyBean implements SessionBean
{
    public String getAgencyName() {
      // Code removed for clarity
    }

    public Collection findAllApplicants() {
      // Code removed for clarity
    }

    public void createApplicant(String login, String name, String email)
      throws DuplicateException, CreateException {
      // Code removed for clarity
    }
    public void deleteApplicant (String login)
      throws NotFoundException {
      // Code removed for clarity
    }

    public Collection findAllCustomers() {
      // Code removed for clarity
    }

    public void createCustomer(String login, String name, String email)
      throws DuplicateException, CreateException {
      // Code removed for clarity
    }

    public void deleteCustomer (String login) throws NotFoundException {
      // Code removed for clarity
    }

    public Collection getLocations() {
      // Code removed for clarity
    }

    public void addLocation(String name) throws DuplicateException {
      // Code removed for clarity
    }

    public void removeLocation(String code) throws NotFoundException {
      // Code removed for clarity
    }

    public Collection getSkills() {
      // Code removed for clarity
    }

    public void addSkill (String name) throws DuplicateException {
      // Code removed for clarity
    }

    public void removeSkill (String name) throws NotFoundException {
      // Code removed for clarity
    }

    public List select(String table) {
      // Code removed for clarity
    }

      // Remaining methods removed for clarity
}
							

The detail of the method implementations have been removed for clarity, because the main area of interest here is how the method signatures match up with those on the remote interface. The contents of the methods are largely the creation and dispatch of JDBC statements and handling the results from the queries.

Note that the bean does not implement the Agency interface. You can also see that various of the methods, such as addSkill(), declare that they throw an application-specific exception—in this case, DuplicateException.

Note

Note that your bean methods will only throw business exceptions or standard Java exceptions. They should not throw java.rmi.RemoteException, because such exceptions should only be generated by the RMI subsystem.


Providing Lifecycle Hooks

Remember that the intention of the EJB environment is that you will spend most of your time writing business logic rather than network and database “plumbing.” Beyond writing the business logic, the only additional thing the bean writer needs to do is to provide lifecycle “hooks” that allow the container to manage the bean.

Each of the different types of EJB discussed earlier has a slightly different lifecycle, but the common parts are as follows:

  • Bean creation and initialization

  • Bean destruction and removal

  • The saving and restoring of the bean's internal state (if applicable)

The details associated with each type of bean lifecycle will be discussed as they are covered. For now, all you need to know is that

  • An EJB will implement one or more lifecycle interfaces depending on its type. The interfaces (SessionBean, EntityBean, MessageDrivenBean, and SessionSynchronization) are defined in the javax.ejb package.

  • The lifecycle methods will generally begin with ejb so that they can be easily distinguished from the business methods around them, for example, ejbCreate().

Listing 4.3 contains the lifecycle methods in the example AgencyBean.

Listing 4.3. Lifecycle Methods on the AgencyBean
package agency;

import java.rmi.*;
import java.util.*;
import javax.ejb.*;
// Remaining imports removed for clarity

public class AgencyBean implements SessionBean
{
    private DataSource dataSource;
    private String name = "";

    private void error (String msg, Exception ex) {
        String s = "AgencyBean: " + msg + "
" + ex;
        System.out.println(s);
        throw new EJBException(s);
    }

    public void ejbCreate () throws CreateException {
        try {
            InitialContext ic = new InitialContext();
            dataSource = (DataSource)ic.lookup("java:comp/env/jdbc/Agency");
            name = (String)ic.lookup("java:comp/env/AgencyName");
        }
        catch (NamingException ex) {
            error("Error connecting to java:comp/env/Agency:", ex);
        }
    }

    public void ejbActivate() {
    }

    public void ejbPassivate() {
    }

    public void ejbRemove() {
        dataSource = null;
    }

    private SessionContext ctx;

    public void setSessionContext(SessionContext ctx) {
        this.ctx = ctx;
    }

      // Remaining methods removed for clarity
}
							

As you can see, the example AgencyBean implements the SessionBean interface. This means that it must implement the ejbCreate(), ejbRemove(), ejbActivate(), ejbPassivate(), and setSessionContext() methods. The ejbCreate() method takes on the role of constructor in that most of the bean initialization will take place in there. The context passed in setSessionContext() provides a way for the bean to communicate with the container.

This concludes the examination of the bean internals for the time being. You will discover more as you learn about the specific types of EJB later.

Factory Information

For an EJB to be used by a client, the client must create a new instance or discover an existing one. Finding and gaining access to the services of a traditional remote server is relatively simple. Such a server will tend to start when the machine boots, reside in a well-know location, and carry on running until the machine shuts down. However, EJBs are far more dynamic than that. It is the ability to dynamically create and reuse beans that provides the scalability inherent in the EJB model.

To facilitate the creation and discovery of EJBs, each type of EJB provides a home interface. The bean developer will provide an EJB home interface that acts as a factory for that particular EJB. A home interface will extend the javax.ejb.EJBHome interface and will contain the necessary methods identified by the bean developer that allow a client to create, find, or remove EJBs.

There are two ways for a client to get hold of the EJB itself, depending on the type of EJB (Session, Entity, or Message-driven) and the way it is intended to be used. The EJB Home interface can contain one or more create() methods to create a new instance of an EJB. So, for example, you will create a new instance of a Session bean before using it. On the other hand, when you interact with Entity EJBs, you will frequently find existing EJBs using one or more findXXX() methods. The home interface may or may not allow you to remove the bean, depending on bean type and usage.

Listing 4.4 shows the home interface for the example Agency EJB.

Listing 4.4. Home Interface for the Agency Bean
package agency;

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

public interface AgencyHome extends EJBHome
{
    Agency create () throws RemoteException, CreateException;
}

Because the Agency EJB is just a simple wrapper around some JDBC-based functionality and does not maintain any business state, all that is required is a simple creation method—create(). This maps onto the ejbCreate() seen in Listing 4.3. The client will call create() to create an instance of the Agency bean.

The code underlying the home interface will work with the container to create, populate, and destroy EJBs as requested by the client. The effects of the method calls will vary depending on the type of EJB being manipulated. As a result, a request to remove a Session EJB will just result in the EJB being thrown away, while the same request on an Entity EJB may cause underlying data to be removed. The types and effects of different home interface methods are discussed in more detail on subsequent days.

Bean Metadata

The final piece of the EJB jigsaw lies in the provision of configuration information, or metadata, for the EJB. This provides a way of communicating the EJB's requirements and structure to the container. If an EJB is to be successfully deployed, the container will have to be provided with extra information, including

  • An identifier or name for the EJB that can be used to look it up.

  • The bean type (Session, Entity, or Message-driven).

  • Which class is the EJB's remote interface. This interface will typically just be named according to the EJB's functionality, for example, Agency or BankTeller.

  • Which class is the EJB's home interface. The name for an EJB's home interface will typically be derived from its remote interface name. So, for example, the Agency EJB has a home interface called AgencyHome. However, because this is a convention rather than being mandatory, the metadata explicitly indicates the name of the home interface.

  • Which class is the bean itself. Again, the name for the bean will typically be derived from the associated remote interface name. So, for example, the Agency bean is called AgencyBean. However, because this is a convention rather than being mandatory, the metadata explicitly indicates the name of the bean.

  • Any name/value pairs to be provided as part of the bean's environment.

  • Information about any external resources required by the EJB, such as database connections or other EJBs.

All of this essential information is bundled into a deployment descriptor that accompanies the EJB classes. As you might expect, given its recent rise as the most ubiquitous way to define data, the deployment descriptor is defined as an XML document. The deployment descriptor is discussed in more detail soon when examining the packaging of an EJB.

In addition to the essential information, the deployment descriptor can also carry other metadata that you will encounter as you progress:

  • Declarative attributes for security and transactions

  • Structural information bean relationships and dependencies

  • Persistence mapping (if applicable)

You are now nearing the conclusion of this whistle-stop tour of the structure of an EJB. After you have examined how an EJB is created and packaged, you will be ready to deploy and use one.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.133.160