The Customer EJB

The Customer EJB is a simple CMP entity bean that models the concept of a cruise customer or passenger, but its design and use are applicable across many commercial domains. This section introduces the Customer bean’s development, packaging, and deployment. We greatly expand the bean’s features as we progress through the chapter.

The Customer Table

Although CMP is database-independent, the examples throughout this book assume that you are using a relational database. This means that we will need a CUSTOMER table from which to get our customer data. The relational database table definition in SQL is as follows:

CREATE TABLE CUSTOMER 
(
    ID INT PRIMARY KEY NOT NULL, 
    LAST_NAME CHAR(20), 
    FIRST_NAME CHAR(20)
)

The CustomerBean

The CustomerBean class is an abstract class that the container uses for generating a concrete implementation, the persistence entity class. The mechanism used by the container for generating a persistence entity class varies, but most vendors generate a subclass of the abstract class provided by the bean developer (see Figure 6-4).

The container tool typically extends the bean class

Figure 6-4. The container tool typically extends the bean class

The bean class must declare accessor (set and get) methods for each persistence field and relationship field defined in the deployment descriptor. The container needs both the abstract accessor methods (defined in the entity bean class) and the XML elements of the deployment descriptor to fully describe the bean’s persistence schema. In this book, the entity bean class is always defined before the XML elements, because it’s a more natural approach for most Java developers. Here is a very simple definition of the CustomerBean class:

package com.titan.customer;

import javax.ejb.EntityContext;

public abstract class CustomerBean implements javax.ejb.EntityBean {
       
    public Integer ejbCreate(Integer id){
        setId(id);
        return null;
    }
    public void ejbPostCreate(Integer id){
    }
   
    // abstract accessor methods

    public abstract Integer getId( );
    public abstract void setId(Integer id);
    
    public abstract String getLastName( );
    public abstract void setLastName(String lname);
    
    public abstract String getFirstName( );
    public abstract void setFirstName(String fname);
    
    // standard callback methods
    
    public void setEntityContext(EntityContext ec){}
    public void unsetEntityContext( ){}
    public void ejbLoad( ){}
    public void ejbStore( ){}
    public void ejbActivate( ){}
    public void ejbPassivate( ){}
    public void ejbRemove( ){}
}

The CustomerBean class is required to be abstract in order to reinforce the idea that the CustomerBean is not deployed directly. Since abstract classes cannot be instantiated, this class must be subclassed by a persistence class generated by the deployment tool. When generating the persistence class, the deployment tool must generate the accessor methods, which are themselves declared as abstract.

The CustomerBean extends the javax.ejb.EntityBean interface, which defines several callback methods, including setEntityContext( ), unsetEntityContext( ), ejbLoad( ), ejbStore( ), ejbActivate( ), ejbPassivate( ), and ejbRemove( ). These methods are important for notifying the bean instance about events in its life cycle, but we do not need to worry about them yet. We will discuss these methods in detail in Chapter 10.

The first method in the entity bean class is ejbCreate( ) , which takes a reference to an Integer object as its only argument. The ejbCreate( ) method is called when the remote client invokes the create( ) method on the entity bean’s home interface. This concept should be familiar, since it’s the same way ejbCreate( ) worked in the Cabin bean developed in Chapter 4. The ejbCreate( ) method is responsible for initializing any persistence fields before the entity bean is created. In this first example, the ejbCreate( ) method is used to initialize the id persistence field, which is represented by the setId( )/getId( ) accessor methods.

The return type of ejbCreate( ) is an Integer, which is the primary key of the entity bean. The primary key is a unique identifier that can take a variety of forms. In this case, the primary key (the Integer) is mapped to the ID field in the CUSTOMER table. This will become evident when we define the XML deployment descriptor. However, although the return type of the ejbCreate( ) method is the primary key, the value actually returned by the ejbCreate( ) method is null. The EJB container and persistence class will extract the primary key from the bean when it is needed. See the sidebar “Why ejbCreate( ) Returns Null” for an explanation of ejbCreate( )’s return type.

The ejbPostCreate( ) method performs initialization after the entity bean is created but before it services any requests from the client. Usually, this method is used to perform work on the entity bean’s relationship fields, which can occur only after the bean’s ejbCreate( ) method has been invoked and added to the database. For each ejbCreate( ) method, there must be a matching ejbPostCreate( ) method that has the same method name and arguments but returns void. This pairing of ejbCreate( ) and ejbPostCreate( ) ensures that the container calls the correct methods together. We’ll explore the use of the ejbPostCreate( ) later; for now, it’s not needed, so its implementation is left empty.

The abstract accessor methods (setLastName( ), getLastName( ), setFirstName( ), getFirstName( )) represent the persistence fields in the CustomerBean class. When the bean is processed by a container, these methods will be implemented by a persistence class based on the abstract persistence schema (XML deployment descriptor elements), the particular EJB container, and the database used. Basically, these methods fetch and update values in the database and are not implemented by the bean developer.

The Remote Interface

We need a CustomerRemote interface for the Customer EJB, because the bean will be accessed by clients outside the container system. The remote interface defines the business methods that clients use to interact with the entity bean. The remote interface should define methods that model the public aspects of the business concept being modeled; that is, those behaviors and data that should be exposed to client applications. Here is the remote interface for CustomerRemote:

package com.titan.customer;
import java.rmi.RemoteException;

public interface CustomerRemote extends javax.ejb.EJBObject {
       
    public String getLastName( ) throws RemoteException;
    public void setLastName(String lname) throws RemoteException;
    
    public String getFirstName( ) throws RemoteException;
    public void setFirstName(String fname) throws RemoteException;
}

Any methods defined in the remote interface must match methods defined in the bean class. In this case, the accessor methods in the CustomerRemote interface match persistence field accessor methods in the CustomerBean class—with a few exceptions, methods in the remote interface can match any business method in the bean class

While remote methods can match persistence fields and other business methods in the bean class, the specification prohibits the remote methods from matching callback methods (ejbRemove( ), ejbActivate( ), ejbLoad( ), etc.) or relationship fields—relationship fields are used to access other entity beans. In addition, remote methods may not modify any container-managed persistence fields that are part of the primary key of an entity bean. Notice that the remote interface does not define a setId( ) method, which would allow it to modify the primary key.

The Remote Home Interface

The remote home interface of any entity bean is used to create, locate, and remove entities from the EJB container. Each entity bean type may have its own remote home interface, local home interface, or both. As explained in Chapter 5, the remote and local home interfaces perform essentially the same function. The home interfaces define three basic kinds of methods: home business methods, zero or more create methods, and one or more find methods. The create( ) methods act like remote constructors and define how new entity beans are created. In our remote home interface, we provide only a single create( ) method, which matches the corresponding ejbCreate( ) method in the bean class. The find method is used to locate a specific Customer EJB using the primary key as a unique identifier.

Here is the complete definition of the CustomerHomeRemote interface:

package com.titan.customer;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface CustomerHomeRemote extends javax.ejb.EJBHome {
    
    public CustomerRemote create(Integer id) 
        throws CreateException, RemoteException;
    
    public CustomerRemote findByPrimaryKey(Integer id) 
        throws FinderException, RemoteException;
    
}

A create( ) method may be suffixed with a name in order to further qualify it when overloading method arguments. This is useful if we have two different create( ) methods that take arguments of the same type. For example, we could declare two create( ) methods for Customer that both declare an Integer and a String argument. The String argument might be a Social Security number (SSN) in one case and a tax identification number (TIN) in another—individuals have Social Security numbers while corporations have tax identification numbers. Here’s how these methods might look:

public interface CustomerHomeRemote extends javax.ejb.EJBHome {
    
    public CustomerRemote createWithSSN(Integer id, String socialSecurityNumber) 
        throws CreateException, RemoteException;

    public CustomerRemote createWithTIN(Integer id, String taxIdentificationNumber)
        throws CreateException, RemoteException;
    
    public CustomerRemote findByPrimaryKey(Integer id) 
        throws FinderException, RemoteException;
 }

Each create<SUFFIX>( ) method must have a corresponding ejbCreate<SUFFIX>( ) in the bean class. For example, the CustomerBean class needs to define ejbCreateWithSSN( ) and ejbCreateWithTIN( ) methods as well as matching ejbPostCreateWithSSN( ) and ejbPostCreateWithTIN( ) methods. We are keeping this example simple, so we need only one create( ) method and, therefore, no suffix.

Enterprise JavaBeans specifies that create( ) methods in the remote home interface must throw the javax.ejb.CreateException . In the case of container-managed persistence, the container needs a common exception for communicating problems that may occur during the create process.

Entity remote home interfaces must define a findByPrimaryKey( ) method that takes the entity bean’s primary key type as its only argument. No matching method needs to be defined in the entity bean class. The implementation of findByPrimaryKey( ) is generated automatically. At runtime, the findByPrimaryKey( ) method automatically locates and returns a remote reference to the entity bean with the matching primary key.

The bean developer can also declare other find methods. For example, the CustomerHomeRemote interface could define a findByLastName(String lname) method that locates all the Customer entities with the specified last name. These types of find methods are automatically implemented by the deployment tool based on the method signature and an EJB QL statement. EJB QL is similar to SQL but is specific to EJB. Custom finder methods and EJB QL are discussed in detail in Chapter 8.

The XML Deployment Descriptor

CMP entity beans must be packaged with an XML deployment descriptor that describes the bean and its abstract persistence schema. With many commercial containers, the bean developer is not directly exposed to the deployment descriptor, but instead uses the container’s deployment tools to package beans. In this book, however, I describe the deployment descriptor in detail so you have a full understanding of its content and organization.

Here is the complete XML deployment descriptor for the Customer EJB in EJB 2.1. Many of the elements in this descriptor should be familiar from Chapter 4; we will focus on the new elements:

<?xml version="1.0" encoding="UTF-8" ?>
<ejb-jar 
     xmlns="http://java.sun.com/xml/ns/j2ee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                         http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
     version="2.1">

    <enterprise-beans>
        <entity>
            <ejb-name>CustomerEJB</ejb-name>
            <home>com.titan.customer.CustomerHomeRemote</home>
            <remote>com.titan.customer.CustomerRemote</remote>
            <ejb-class>com.titan.customer.CustomerBean</ejb-class>
            <persistence-type>Container</persistence-type>
            <prim-key-class>java.lang.Integer</prim-key-class>
            <reentrant>False</reentrant>
            <cmp-version>2.x</cmp-version>
            <abstract-schema-name>Customer</abstract-schema-name>
            <cmp-field><field-name>id</field-name></cmp-field>
            <cmp-field><field-name>lastName</field-name></cmp-field>
            <cmp-field><field-name>firstName</field-name></cmp-field>
            <primkey-field>id</primkey-field>
            <security-identity><use-caller-identity/></security-identity>
        </entity>
    </enterprise-beans>
    <assembly-descriptor>
        <security-role>
            <role-name>Employees</role-name>
        </security-role>
        <method-permission>
            <role-name>Employees</role-name>
            <method>
                <ejb-name>CustomerEJB</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>
        <container-transaction>
            <method>
                <ejb-name>CustomerEJB</ejb-name>
                <method-name>*</method-name>
            </method>
            <trans-attribute>Required</trans-attribute>
        <container-transaction>
    </assembly-descriptor>  
</ejb-jar>

The deployment descriptor for EJB 2.0 is exactly the same, except that it uses XML DTD instead of XML Schema, so the first tag in the EJB 2.0 deployment descriptor is the document declaration followed by the <ejb-jar> element.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise
                  JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">

                  <ejb-jar>
     ...
</ejb-jar>

The first few elements in the Customer EJB’s deployment descriptor should be familiar; they declare the Customer EJB name, (CustomerEJB) as well as its home, remote, and bean class. The <security-identity> element should also be familiar, as well as the <assembly-descriptor> elements, which declare the security and transaction attributes of the bean. In this case, they state that all employees can access any CustomerEJB method and that all methods use the Required transaction attribute.

Container-managed persistence entities also need to declare a persistence type, version, and whether they are reentrant. These elements are declared under the <entity> element.

The <persistence-type> element tells the container system whether the bean will be a container-managed persistence entity or a bean-managed persistence entity. In this case it’s container-managed, so we use Container. Had it been bean-managed, the value would have been Bean.

The <cmp-version> element is optional; it tells the container system which version of container-managed persistence is being used. The value of the <cmp-version> element can be either 2.x or 1.x. The 2.x designator is used for EJB 2.1 and 2.0, while 1.x is used for EJB 1.1. EJB 2.1 and 2.0 containers are required to support EJB 1.1 CMP for backward compatibility. If it is not declared, the default value is 2.x. It’s not really needed here, but it’s specified as an aid to other developers who might read the deployment descriptor.

The <reentrant> element indicates whether reentrant behavior is allowed. In this case the value is False, which indicates that the CustomerEJB is not reentrant (i.e., loopbacks are not allowed). A value of True would indicate that the CustomerEJB is reentrant and that loopbacks are permitted.

The entity bean must also declare its container-managed persistence fields and its primary key:

<entity>
    <ejb-name>CustomerEJB</ejb-name>
    <home>com.titan.customer.CustomerHomeRemote</home>
    <remote>com.titan.customer.CustomerRemote</remote>
    <ejb-class>com.titan.customer.CustomerBean</ejb-class>
    <persistence-type>Container</persistence-type>
    <prim-key-class>java.lang.Integer</prim-key-class>
    <reentrant>False</reentrant>
    <cmp-version>2.x</cmp-version>
    <cmp-field><field-name>id</field-name></cmp-field>
                  <cmp-field><field-name>lastName</field-name></cmp-field>
                  <cmp-field><field-name>firstName</field-name></cmp-field>
                  <primkey-field>id</primkey-field>
</entity>

The container-managed persistence fields are the id, lastName, and firstName, as indicated by the <cmp-field> elements. The <cmp-field> elements must have matching accessor methods in the CustomerBean class. As you can see in Table 6-1, the values declared in the <field-name> element match the names of abstract accessor methods we declared in the CustomerBean class.

Table 6-1. Field names for abstract accessor methods

CMP field

Abstract accessor method

id
public abstract Integer getId( )
public abstract void setId(Integer id)
lastName
public abstract String getLastName( )
public abstract void setLastName(String lname)
firstName
public abstract String getFirstName( )
public abstract void setFirstName(String lname)

CMP requires that the <field-name> values start with a lowercase letter. The names of the matching accessor methods take the form get<field-name>( ), set<field-name >( ) (the first letter of the field name is capitalized). The return type of the get method and the parameter of the set method determine the type of the <cmp-field>. It’s the convention of this book, but not a requirement of CMP, that field names with multiple words are declared using “camel case,” in which each new word starts with a capital letter (e.g., lastName).

Finally, we declare the primary key using two fields, <prim-key-class> and <primkey-field> . <prim-key-class> indicates the type of the primary key, and <primkey-field> indicates which of the <cmp-field> elements designates the primary key. The Customer EJB uses a single-field primary key, in which the bean’s identifier is composed of a single container-managed field. The <primkey-field> must be declared if the entity bean uses a single-field primary key. Compound primary keys, which use more than one of the persistence fields as a key, are often used instead. In this case, the bean developer creates a custom primary key. The <prim-key-class> element is always required, whether it’s a single-field, compound, or unknown primary key. Unknown keys use a field that may not be declared in the bean at all. The different types of primary keys are covered in more detail in Chapter 10.

The EJB JAR File

Now that you have created the interfaces, bean class, and deployment descriptor, you’re ready to package the bean for deployment. As you learned in Chapter 4, the JAR file provides a way to “shrink-wrap” a component so it can be deployed in an EJB container. The command for creating a new EJB JAR file is:

dev % jar cf customer.jar com/titan/customer/*.class 
com/titan/customer/META-INF/ejb-jar.xml

F:..dev>jar cf cabin.jar com	itancustomer*.class com	itancustomer 
META-INFejb-jar.xml

There are a number of tools that create the XML deployment descriptor and package the enterprise bean into a JAR file automatically. Some of these tools even create the home and remote interfaces automatically, based on input from the developer.

Deployment

Once the CustomerEJB is packaged in a JAR file, it’s ready to be deployed in an EJB container. The point is to map the container-managed persistence fields of the bean to fields or data objects in the database. (Earlier in this chapter, Figure 6-2 and Figure 6-3 showed two visual tools used to map the Customer EJB’s persistence fields.) In addition, the security roles need to be mapped to the subjects in the security realm of the target environment and the bean needs to be added to the naming service and given a JNDI lookup name (name binding).

The Client Application

The Client application is a remote client to the CustomerEJB that creates several customers, finds them, and then removes them. Here is the complete definition of the Client application:

import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Properties;

public class Client {
    public static void main(String [] args)) throws Exception {
        //obtain CustomerHome
        Context jndiContext = getInitialContext( );
        Object obj=jndiContext.lookup("CustomerHomeRemote");
        CustomerHomeRemote home = (CustomerHomeRemote)
            javax.rmi.PortableRemoteObject.narrow(obj,CustomerHomeRemote.class);
        //create Customers
        for(int i =0;i <args.length;i++){
            Integer primaryKey =new Integer(i);
            String firstName = args [i ];
            String lastName = args [i ];
            CustomerRemote customer = home.create(primaryKey);
            customer.setFirstName(firstName);
            customer.setLastName(lastName);
        }
        //find and remove Customers
        for(int i = 0;i < args.length;i++){
            Integer primaryKey = new Integer(i);
            CustomerRemote customer = home.findByPrimaryKey(primaryKey);
            String lastName = customer.getLastName( );
            String firstName = customer.getFirstName( );
            System.out.print(primaryKey+"=");
            System.out.println(firstName+""+lastName);
            //remove Customer
            customer.remove( );
        }
    }
    public static Context getInitialContext(
        throws javax.naming.NamingException {
        Properties p =new Properties( );
        //...Specify the JNDI properties specific to the vendor.
        return new javax.naming.InitialContext(p);
    }
}

The Client application creates several Customer EJBs, sets their first and last names, prints out the persistence field values, and then removes the entities from the container system and, effectively, the database. To deploy the examples in this section, see Exercise 6.1 in the Workbook.

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

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