The Customer EJB

In the following example we will develop a simple CMP 2.0 entity bean—the Customer EJB. The Customer EJB models the concept of a cruise customer or passenger, but its design and use are applicable across many commercial domains.

As the chapter progresses, the Customer EJB will be expanded and its complexity will increase to illustrate concepts discussed in each section. This section serves only to introduce you to the entity bean and some basic concepts regarding its development, packaging, and deployment. To simplify things, we will skim over some concepts that are discussed in detail later in the chapter.

J2EE 1.3 SDK deployment tool

Figure 6-3. J2EE 1.3 SDK deployment tool

The Customer Table

Although CMP 2.0 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 will be used by the container tool for generating a concrete implementation, the persistence entity class, which will run in the EJB container. The mechanism used by the container tool 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 and relationship field defined in the abstract persistence schema of the deployment descriptor. The container tool 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 to developing entity beans.

Here is a very simple definition of the CustomerBean class that is developed and packaged for deployment by the bean developer:

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 defined as an abstract class. This is required by CMP 2.0 to reinforce the idea that the CustomerBean is not deployed directly into the container system. Since abstract classes cannot be instantiated, in order to be deployed the bean class must be subclassed by a persistence class generated by the deployment tool. Also, the accessor methods are themselves declared as abstract, which necessitates that the container tool implement them.

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

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 the ejbCreate() method 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, including that of a wrapper for primitive types and custom-defined classes. 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. 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 Why ejbCreate( ) Returns Null for an explanation of ejbCreate()’s return type.

The ejbPostCreate() method is used to perform initialization after the entity bean is created but before it services any requests from the client. This method usually is used to perform work on the entity bean’s relationship fields, which can occur only after the bean’s ejbCreate() method is invoked and it’s 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() in more detail 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. These methods are defined as abstract without method bodies. As was already mentioned, when the bean is processed by a container tool, 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 will 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 clients will 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 the signatures of 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. When the remote interface methods match the persistence field methods, the client has direct access to the entity bean’s persistence fields.

You are not required to match abstract accessor methods in the bean class with methods in the remote interface. In fact, it’s recommended that the remote interface be as independent of the abstract programming model as possible.

While remote methods can match persistence fields in the bean class, the specification prohibits the remote methods from matching relationship fields, which 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 you learned 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.[26] 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.

The following code contains 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 you 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:

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;
 }

Suffixes are useful when you need create() methods to be more descriptive or need to further qualify them for method overloading. 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 experienced 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, but no matching method needs to be defined in the entity bean class. The implementation of findByPrimaryKey() is generated automatically by the deployment tool. At runtime, the findByPrimaryKey() method will automatically locate and return 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 finder methods are automatically implemented by the deployment tool based on the method signature and an EJB QL statement, which 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 2.0 entity beans must be packaged for deployment with an XML deployment descriptor that describes the bean and its abstract persistence schema. In most cases, the bean developer is not directly exposed to the XML deployment descriptor but rather will use the container’s visual deployment tools to package the beans. In this book, however, I will describe the declarations of the deployment descriptor in detail so you have a full understanding of their content and organization.

The XML deployment descriptor, for our simple Customer EJB, contains many elements that should be familiar to you from Chapter 4. The elements specific to entity beans and persistence are most important to us in this chapter. The following is the complete XML deployment descriptor for the Customer EJB:

<!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>
    <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 first few elements, which declare the Customer EJB name, (CustomerEJB) as well as its home, remote, and bean class, should already be familiar to you from Chapter 4. The <security-identity> element was covered in Chapter 3.

The <assembly-descriptor> elements, which declare the security and transaction attributes of the bean, were also covered briefly in Chapter 4. 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 their 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 tells the container system which version of container-managed persistence is being used. EJB 2.0 containers must support the new container-managed persistence model as well as the old one defined in EJB 1.1. The value of the <cmp-version> element can be either 2.x or 1.x, for the EJB 2.0 and 1.1 versions, respectively. The <cmp-version> element is optional. 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. Reentrant behavior was covered in Chapter 3.

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—the get and set parts of the method names are ignored when matching methods to <field-name> declarations.

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 2.0 requires that the <field-name> values start with a lowercase letter while their matching accessor methods take the form get<field-name value>(), set<field-name value>(), with the first letter of the <field-name> 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 2.0, that field names with multiple words are declared using “camel case,” where 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 only one field of the entity bean’s container-managed fields describes a unique identifier for the bean. 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 11.

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 sold and/or deployed in an EJB container. The examples available from http://www.oreilly.com/catalog/entjbeans3/ contain a properly prepared JAR file that includes the Customer EJB’s interfaces, bean class, and deployment descriptor. You may use these files or develop them yourself. 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

Most EJB servers provide graphical or command-line 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. If you prefer to use these tools, the workbooks will step you through the process of deploying an entity bean using specific vendors’ container- deployment tools.

Deployment

Once the CustomerEJB is packaged in a JAR file, it’s ready to be processed by the deployment tools. For most vendors, these tools are combined into one graphical user interface used at deployment time. 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). These tasks are also accomplished using the deployment tools provided by your vendor. The workbooks provide step-by-step instructions for deploying the CustomerEJB in specific vendor environments.

The Client Application

The Client application is a remote client to the CustomerEJB that will create several customers, find them, and then remove 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(args [ 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(args [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.

The Client ApplicationPlease refer to Workbook Exercise 6.1, Basic Persistence in CMP 2.0. This workbook is available free, in PDF format, at http://www.oreilly.com/catalog/entjbeans3/workbooks.



[26] Chapter 15 explains when you should not define any create methods in the home interface.

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

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