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 artifacts that make up an EJB. In other words, what's in one?

There are four components to an EJB:

  • The remote interface— Defining the signatures of the business methods for the EJB

  • The home interface— Defining the signature of the methods associated with the bean lifecycle (creation, use and destruction)

  • The bean itself— A concrete Java class implementing the business and lifecycle method functionality

  • The deployment descriptor— Meta data about the EJB, such as component classes, EJB type, transaction demarcation, resource and EJB references, environment entries, and security requirements

The names of the two Java interfaces and the Bean class usually follow a simple convention. For a given remote interface XXX the home interface is called XXXHome and the bean implementation is XXXBean. (Some users prefer to use XXXEJB for the implementation class.) Although this convention is not enforced, it is recommended that you use it when naming your EJB components.

Similarly, as discussed later in the section “The Bean Implementation,” there are corresponding rules about the names of methods defined in the interfaces and the names of methods in the bean implementation. Unlike the class names, these rules are part of the EJB specification and must be rigorously applied, otherwise your EJBs will not deploy correctly.

The Business Interface

As stated already, 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 remote (or business) interface as opposed to the home interface you will look at in a moment.

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 exceptions. This allows the RMI subsystem to signal network-related errors to the client.

  • RMI rules apply to parameters and return values, so any types used must either be primitives (int, boolean, float, and the like), or implement the Serializable or Remote interfaces. Most Java classes, such as String and the primitive wrapper classes, implement Serializable.

  • The interface must declare that it extends the javax.ejb.EJBObject interface.

NOTE

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 an object as a parameter into a local method call, a reference to the original object is 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. This mechanism is known as pass by reference.

On the other hand, when using RMI remote methods, only objects that are serializable (that is, implement the Serializable interface) are passed. A copy of the object is made and this copy is passed over the remote interface to the method. 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. This mechanism is known as pass by value.

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 introduced on Day 2, “The J2EE Platform and Roles.”

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 getApplicants()
      throws RemoteException;
    void createApplicant(String login, String name, String email)
      throws RemoteException, DuplicateException, CreateException;
    void deleteApplicant (String login)
      throws RemoteException, NotFoundException;

    Collection getCustomers() 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 remote 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 Collection objects.

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 utilize 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 Home Interface

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 to 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 or Entity) 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 can create a new instance of a Session bean before using it. On the other hand, when you interact with Entity EJBs, you will frequently use findXXX()methods to use existing beans. Message-Driven beans do not have a home interface as their lifecycle is more rigidly controlled compared to Session and Entity beans (the Message-driven bean life-cycle is covered in detail on Day 10.

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

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

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

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

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 Days 5 and 6.

The Bean Implementation

After the interfaces are defined, there is the none-too-trivial task of implementing the business logic behind them. 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, 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; however, there is a very good reason for this.

In order for the container to apply services, such as access control, on behalf of the EJB 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.

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

Figure 4.1. The EJB Object acts as a server-side proxy for the bean itself.


As shown in Figure 4.1, the client calls the business methods on the EJB Object implementation. The EJB Object applies the required extra services and then forwards the method calls to the bean itself.

The J2EE server generates the EJB Object class when you deploy the EJB. The deployment process uses the information you provide in the home and remote interfaces and the DD to generate the required EJB Object class.

Every business method in the remote interface must have a corresponding business method in the bean. The method name, parameters, and return type must be identical and the bean method must throw the same exceptions as the interface method apart from the RemoteException required by all remote methods. All business methods must have public visibility.

For example, the remote interface method

public Collection findAllApplicants() throws RemoteException; must have the corresponding method in the bean:

public Collection findAllApplicants();

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.3 shows the outlines of the business methods in the example AgencyBean with some of the code removed for clarity.

Listing 4.3. 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() { ... }
    public Collection findAllApplicants() { ... }


    public void createApplicant(String login, String name, String email)
      throws DuplicateException, CreateException { ... }

    public void deleteApplicant (String login)
      throws NotFoundException { ... }

    public Collection findAllCustomers() { ... }

    public void createCustomer(String login, String name, String email)
      throws DuplicateException, CreateException { ... }

    public void deleteCustomer (String login) throws NotFoundException { ... }

    public Collection getLocations() { ... }

    public void addLocation(String name) throws DuplicateException { ... }

    public void removeLocation(String code) throws NotFoundException { ... }

    public Collection getSkills() { ... }

    public void addSkill (String name) throws DuplicateException { ... }

    public void removeSkill (String name) throws NotFoundException { ... }

    public List select(String table) { ... }
}

An EJB implementation must implement the appropriate javax.ejb class, as described in the section “Implementing the Home Interface.” The AgencyBean example is a Session EJB and therefore must extend javax.ejb.SessionBean.

NOTE

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


Implementing the Home Interface

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 EJBs discussed earlier (Session, Entity, and MDB) 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 are defined in the javax.ejb package.

  • Standard lifecycle methods must be provided.

  • 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.4 contains the lifecycle methods in the example AgencyBean with most of the implementation code removed for clarity

Listing 4.4. 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 = "";

    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() { ... }

    private SessionContext ctx;

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

As you can see, the example AgencyBean implements the SessionBean interface. This means that it must implement the ejbRemove(), ejbActivate(), ejbPassivate(), and setSessionContext() methods. The context passed in setSessionContext() provides a way for the bean to communicate with the container. It is usual to save the session context object in an instance variable for use by the other bean methods.

In an Entity bean the ejbCreate() method takes on the role of constructor in that most of the bean initialization will take place in this method, and corresponds to the create() method defined in the home interface. The ejbCreate() method is not defined in the SessionBean interface, because its signature will vary from one EJB to another (as described tomorrow when Session EJBs are discussed in detail).

The Deployment Descriptor

The final piece of the EJB jigsaw lies in the provision of configuration information, or meta data, 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 for JNDI lookup to locate the bean.

  • The bean type Session, Entity, or Message-Driven.

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

  • The EJB's home interface class. The name for an EJB's home interface will typically be derived from its remote interface name.

  • The bean class itself. Again, the name for the bean will typically be derived from the associated remote interface name.

  • Any name/value pairs to be provided as part of the bean's environment. Effectively, these are variables that can be given values by the assembler or deployer as well as the developer.

  • 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. The deployment descriptor is defined as an XML document, and is discussed in more detail later when examining the packaging of an EJB.

In addition to the essential information, the deployment descriptor can also carry other metadata such as:

  • Declarative attributes for security and transactions

  • Structural information about bean relationships and dependencies

  • Persistence mapping (if applicable)

You will see examples of all of these as you progress through this book.

Verifying an EJB

One of the problems most developers come across when writing EJBs is making sure the bean implementation provides the requisite functionality in the remote and home interfaces. Because your bean does not implement the remote interface (or the home interface), you must manually check that you have defined all the business methods in the bean implementation and that every create() method in the home interface has a corresponding ejbCreate() method in the implementation.

There are two ways of solving this problem:

  • Generate a template bean implementation class from the home and remote interfaces.

  • Use a tool that checks the bean implementation against the home and remote interfaces.

The drawback to the first approach is that changes to the home or remote interfaces necessitate regenerating the template class and copying existing functionality into the newly generated template. The process of manually copying existing code into the new template could lead to errors and is best avoided.

Use of a separate verification utility is more common, as this can be used any time changes are made to the interfaces or the implementation. The J2EE RI comes with the verifier tool, which validates the contents of an enterprise application and generates a comprehensive list of any errors encountered. The verifier is discussed in more detail in the section “Verifying the Case Study Application.”

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.116.42.9