Chapter 12. Session Beans

As Chapter 6 thorough Chapter 11 demonstrated, entity beans provide an object-oriented interface that makes it easier for developers to create, modify, and delete data from the database. Entity beans allow developers to be more productive by encouraging reuse and reducing development costs. You can reuse a concept like a Ship throughout a business system without having to redefine, recode, or retest the business logic and data access.

However, entity beans are not the entire story. We have also seen another kind of enterprise bean: the session bean. Session beans fill the gaps left by entity beans. They are useful for describing interactions between other beans (workflow) and for implementing particular tasks. Unlike entity beans, session beans do not represent shared data in the database, but they can access shared data. This means that we can use session beans to read, update, and insert data. For example, we might use a session bean to provide lists of information, such as a list of all available cabins. Sometimes we might generate the list by interacting with entity beans, like the cabin list we developed in the TravelAgent EJB in Chapter 4. More frequently, session beans will generate lists by accessing the database directly.

So when do you use an entity bean and when do you use a session bean to directly access data? Good question! As a rule of thumb, an entity bean is developed to provide a safe and consistent interface to a set of shared data that defines a concept. This data may be updated frequently. Session beans access data that spans concepts, is not shared, and is usually read-only.

In addition to accessing data directly, session beans can represent workflow. Workflow describes all the steps required to accomplish a particular task, such as booking passage on a ship or renting a video. Session beans are part of the same business API as entity beans, but as workflow components, they serve a different purpose. Session beans can manage the interactions between entity beans, describing how they work together to accomplish a specific task. The relationship between session beans and entity beans is like the relationship between a script for a play and the actors that perform the play. Where entity beans are the actors, the session bean is the script. Actors without a script can each serve a function individually, but only in the context of a script can they tell a story. In terms of our example, it makes no sense to have a database full of cabins, ships, customers, and other entities if we can’t create interactions between them, such as booking a customer for a cruise.

Session beans are divided into two basic types: stateless and stateful. A stateless session bean is a collection of related services, each represented by a method; the bean maintains no state from one method invocation to the next. When you invoke a method on a stateless session bean, it executes the method and returns the result without knowing or caring what other requests have gone before or might follow. Think of a stateless session bean as a set of procedures or batch programs that execute a request based on some parameters and return a result. Stateless session beans tend to be general-purpose or reusable, such as a software service.

A stateful session bean is an extension of the client application. It performs tasks on behalf of the client and maintains state related to that client. This state is called conversational state because it represents a continuing conversation between the stateful session bean and the client. Methods invoked on a stateful session bean can write and read data to and from this conversational state, which is shared among all methods in the bean. Stateful session beans tend to be specific to one scenario. They represent logic that might have been captured in the client application of a two-tier system. Session beans, whether they are stateful or stateless, are not persistent like entity beans. In other words, session beans are not saved to the database.

Depending on the vendor, stateful session beans may have a timeout period. If the client fails to use the stateful bean before it times out, the bean instance is destroyed and the EJB object reference is invalidated. This prevents the stateful session bean from lingering long after a client has shut down or otherwise finished using it. A client can also explicitly remove a stateful session bean by calling one of its remove methods.

Stateless session beans have longer lives because they do not retain any conversational state and are not dedicated to one client, but they still are not saved in a database because they do not represent any data. Once a stateless session bean has finished a method invocation for a client, it can be reassigned to any other EJB object to service a new client. A client can maintain a connection to a stateless session bean’s EJB object, but the bean instance itself is free to service requests from any client. Because it does not contain any state information, a stateless session bean does not distinguish between clients. Stateless session beans may also have a timeout period and can be removed by the client, but the impact of these events is different than with a stateful session bean. With a stateless session bean, a timeout or remove operation simply invalidates the EJB object reference for that client; the bean instance is not destroyed and is free to service other client requests.

The Stateless Session Bean

A stateless session bean is very efficient and relatively easy to develop. Stateless session beans require few server resources because they are neither persistent nor dedicated to one client. Because they are not dedicated to one client, many EJB objects can share a few instances of a stateless bean. A stateless session bean does not maintain conversational state relative to the client it is servicing, so it can be swapped freely between EJB objects. As soon as a stateless instance finishes servicing a method invocation, it can be swapped to another EJB object. Because it does not maintain conversational state, a stateless session bean does not require passivation or activation, further reducing the overhead of swapping. In short, stateless session beans are lightweight and fast.

Stateless session beans often perform services that are fairly generic and reusable. The services may be related, but they are not interdependent. This means that everything a method needs to know has to be passed via the method’s parameters. This provides an interesting limitation. Stateless session beans can’t remember anything from one method invocation to the next, which means that they have to take care of the entire task in one method invocation. The only exception to this rule is information obtainable from the SessionContext and the JNDI ENC.

Stateless session beans are EJB’s version of the traditional transaction-processing applications, which are executed using a procedure call. The procedure executes from beginning to end and then returns the result. Once the procedure is done, nothing about the data that was manipulated or the details of the request are remembered. There is no state.

These restrictions don’t mean that a stateless session bean can’t have instance variables or maintain any kind of internal state. Nothing prevents you from keeping a variable that tracks the number of times a bean has been called or that saves data for debugging. An instance variable can even hold a reference to a live resource, such as a URL connection for logging, verifying credit cards, or anything else that might be useful—the resource should be obtained from the JNDI ENC. However, it is important to remember that this state can never be visible to a client. A client can’t assume that the same bean instance will service it every time. If these instance variables have different values in different bean instances, their values will appear to change randomly as stateless session beans are swapped from one client to another. Therefore, any resources you reference in instance variables should be generic. For example, each bean instance might reasonably record debugging messages—that might be the only way to figure out what is happening on a large server with many bean instances. The client doesn’t know or care where debugging output is going. However, it would clearly be inappropriate for a stateless bean to remember that it was in the process of making a reservation for Madame X—the next time it is called, it may be servicing another client entirely.

Stateless session beans can be used for report generation, batch processing, or some stateless services such as validating credit cards. Another good application would be a StockQuote EJB that returns a stock’s current price. Any activity that can be accomplished in one method call is a good candidate for the high-performance stateless session bean.

The ProcessPayment EJB

Chapter 2 and Chapter 3 discussed the TravelAgent EJB, which has a business method called bookPassage() that uses the ProcessPayment EJB. The next section develops a complete definition of the TravelAgent EJB, including the logic of the bookPassage() method. At this point, however, we are primarily interested in the ProcessPayment EJB, which is a stateless bean the TravelAgent EJB uses to charge the customer for the price of the cruise.

Charging customers is a common activity in Titan’s business systems. Not only does the reservation system need to charge customers, but so do Titan’s gift shops, boutiques, and other related businesses. The process of charging a customer for services is common to many systems, so it has been encapsulated in its own bean.

Payments are recorded in a special database table called PAYMENT. The PAYMENT data is batch processed for accounting purposes and is not normally used outside of accounting. In other words, the data is only inserted by Titan’s system; it is not read, updated, or deleted. Because the process of making a charge can be completed in one method, and because the data is not updated frequently or shared, we will use a stateless session bean for processing payments. Several different forms of payment can be used: credit card, check, or cash. We will model these payment forms in our stateless ProcessPayment EJB.

PAYMENT: The database table

The ProcessPayment EJB accesses an existing table in Titan’s system called the PAYMENT table. Create a table in your database called PAYMENT with this definition:

CREATE TABLE PAYMENT 
(
    customer_id     INTEGER, 
    amount          DECIMAL(8,2), 
    type            CHAR(10), 
    check_bar_code  CHAR(50),
    check_number    INTEGER,
    credit_number   CHAR(20), 
    credit_exp_date DATE
)

ProcessPaymentRemote: The remote interface

A stateless session bean, like any other bean, needs a component interface. While EJB 1.1 uses only remote interfaces, in EJB 2.0 session beans may have either a local or remote interface.

For the remote interface, we obviously need a byCredit() method because the TravelAgent EJB uses it. We can also identify two other methods that we’ll need: byCash() for customers paying cash and byCheck() for customers paying with a personal check.

Here is a complete definition of the remote interface for the ProcessPayment EJB:

package com.titan.processpayment;

import java.rmi.RemoteException;
import java.util.Date;
import com.titan.customer.CustomerRemote;

public interface ProcessPaymentRemote extends javax.ejb.EJBObject {

    public boolean byCheck(CustomerRemote customer, CheckDO check, double amount)
        throws RemoteException,PaymentException;

    public boolean byCash(CustomerRemote customer, double amount)
        throws RemoteException,PaymentException;

    public boolean byCredit(CustomerRemote customer, CreditCardDO card, 
        double amount) throws RemoteException,PaymentException;
}

Remote interfaces in session beans follow the same rules as in entity beans. Here we have defined the three business methods, byCheck(), byCash(), and byCredit(), which take information relevant to the form of payment used and return a boolean value that indicates the success of the payment. In addition to the required RemoteException, these methods can throw an application-specific exception, the PaymentException. The PaymentException is thrown if any problems occur while processing the payment, such as a low check number or an expired credit card. Notice, however, that nothing about the ProcessPaymentRemote interface is specific to the reservation system. It could be used just about anywhere in Titan’s system. In addition, each method defined in the remote interface is completely independent of the others. All the data that is required to process a payment is obtained through the method’s arguments.

As an extension of the javax.ejb.EJBObject interface, the remote interface of a session bean inherits the same functionality as the remote interface of an entity bean. However, the getPrimaryKey() method throws a RemoteException, since session beans do not have a primary key to return:

public interface javax.ejb.EJBObject extends java.rmi.Remote {
    public abstract EJBHome getEJBHome() throws RemoteException;
    public abstract Handle getHandle() throws RemoteException;
    public abstract Object getPrimaryKey() throws RemoteException;
    public abstract boolean isIdentical(EJBObject obj) throws RemoteException;
    public abstract void remove() throws RemoteException, RemoveException;
}

The getHandle() method returns a serializable Handle object, just like the getHandle() method in the entity bean. For stateless session beans, this Handle can be serialized and reused any time, as long as the stateless bean type is still available in the container that generated the Handle.

Tip

Unlike stateless session beans, stateful session beans are available through the Handle for only as long as that specific bean instance is kept alive on the EJB server. If the client explicitly destroys the stateful session bean using one of the remove() methods, or if the bean times out, the instance is destroyed and the Handle becomes invalid. As soon as the server removes a stateful session bean, its Handle is no longer valid and will throw a RemoteException when its getEJBObject() is invoked.

You can obtain a remote reference to the bean from the Handle by invoking its getEJBObject() method:

public interface javax.ejb.Handle {
    public abstract EJBObject getEJBObject() throws RemoteException;
}

The ProcessPayment EJB has its own package, which means it has its own directory in our development tree, dev/com/titan/processpayment. That’s where we’ll store all the code and compile class files for this bean.

Dependent objects: The CreditCardDO and CheckDO classes

The ProcessPayment EJB’s remote interface uses two classes in its definition that are particularly interesting: CreditCardDO and CheckDO. The definitions for these classes are as follows:

/* CreditCardDO.java */
package com.titan.processpayment;

import java.util.Date;

public class CreditCardDO implements java.io.Serializable {
    final static public String MASTER_CARD = "MASTER_CARD";
    final static public String VISA = "VISA";
    final static public String AMERICAN_EXPRESS = "AMERICAN_EXPRESS";
    final static public String DISCOVER = "DISCOVER";
    final static public String DINERS_CARD = "DINERS_CARD";

    public String number;
    public Date expiration;
    public String type;

    public CreditCardDO(String nmbr, Date exp, String typ) {
        number = nmbr;
        expiration = exp;
        type = typ;
    }
}

/* CheckDO.java */
package com.titan.processpayment;

public class CheckDO implements java.io.Serializable {
    public String checkBarCode;
    public int checkNumber;

    public CheckDO(String barCode, int number) {
        checkBarCode = barCode;
        checkNumber = number;
    }
}

CreditCardDO and CheckDO are dependent objects a concept we explored with the Address EJB in Chapter 6. If you examine the class definitions of the CreditCardDO and CheckDO classes, you will see that they are not enterprise beans; they are simply serializable Java classes. These classes provide a convenient mechanism for transporting and binding together related data. CreditCardDO, for example, binds all the credit card data together in one class, making it easier to pass the information across the network as well as making our interfaces a little cleaner.

PaymentException: An application exception

Any remote or local interface, whether it’s for an entity bean or a session bean, can throw application exceptions. Application exceptions are created by the bean developer and should describe a business logic problem—in this case, a problem making a payment. Application exceptions should be meaningful to the client, providing a brief and relevant identification of the error.

It is important to understand what exceptions to use and when to use them. The RemoteException indicates subsystem-level problems and is used by the RMI facility. Likewise, exceptions such as javax.naming.NamingException and java.sql.SQLException are thrown by other Java subsystems; usually these should not be thrown explicitly by your beans. The Java Compiler requires that you use try/catch blocks to capture checked exceptions like these.

In EJB 2.0, the EJBException can express problems the container has with processing local interface invocations. The EJBException is an unchecked exception, so you won’t get a compile error if you don’t write code to handle it. However, under certain circumstances it is a good idea to catch EJBException, and in other circumstances it should be propagated.

When a checked exception from a subsystem ( JDBC, JNDI, JMS, etc.) is caught by a bean method, it should be rethrown as an EJBException or an application exception. You would rethrow a checked exception as an EJBException if it represented a system-level problem; checked exceptions are rethrown as application exceptions when they result from business logic problems. Your beans incorporate your business logic; if a problem occurs in the business logic, that problem should be represented by an application exception. When the enterprise bean throws an EJBException or some other type of RuntimeException, the exception is first processed by the container, which discards the bean instance and replaces it with another. After the container processes the exception, it propagates an exception to the client. For remote clients, the container throws a RemoteException; for local clients (co-located enterprise beans), the container rethrows the original EJBException or RuntimeException thrown by the bean instance.

The PaymentException describes a specific business problem, so it is an application exception. Application exceptions extend java.lang.Exception. Any instance variables you include in these exceptions should be serializable.

Here is the definition of the PaymentException application exception:

package com.titan.processpayment;

public class PaymentException extends java.lang.Exception {
    public PaymentException() {
        super();
    }
    public PaymentException(String msg) {
        super(msg);
    }
}

ProcessPaymentHomeRemote: The home interface

The home interface of a stateless session bean must declare a single create() method with no arguments. This is a requirement of the EJB specification. It is illegal to define create() methods with arguments, because stateless session beans do not maintain conversational state that needs to be initialized. There are no find methods in session beans, because session beans do not have primary keys and do not represent data in the database.

Although EJB 2.0 defines create< SUFFIX >() methods for stateful session beans and entity beans, stateless session beans may define only a single create() method, with no suffix and no arguments. This is also the case in EJB 1.1. The reason for this restriction has to do with the life cycle of stateless session beans, which is explained later in this chapter.

Here is the definition of the remote home interface for the ProcessPayment EJB:

package com.titan.processpayment;

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

public interface ProcessPaymentHomeRemote extends javax.ejb.EJBHome {
    public ProcessPaymentRemote create() throws RemoteException, CreateException;
}

The CreateException is mandatory, as is the RemoteException. The CreateException can be thrown by the bean itself to indicate an application error in creating the bean. A RemoteException is thrown when other system errors occur; for example, when there is a problem with network communication or when an unchecked exception is thrown from the bean class.

The ProcessPaymentHomeRemote interface, as an extension of the javax.ejb.EJBHome, offers the same EJBHome methods as entity beans. The only difference is that remove(Object primaryKey) does not work, because session beans do not have primary keys. If EJBHome.remove(Object primaryKey) is invoked on a session bean (stateless or stateful), a RemoteException is thrown. Logically, this method should never be invoked on the remote home interface of a session bean. Here are the definitions of the javax.ejb.EJBHome interface for EJB 1.1 and 2.0:

public interface javax.ejb.EJBHome extends java.rmi.Remote {
    public abstract HomeHandle getHomeHandle() throws RemoteException;
    public abstract EJBMetaData getEJBMetaData() throws RemoteException;
    public abstract void remove(Handle handle) throws RemoteException, 
        RemoveException;
    public abstract void remove(Object primaryKey) throws RemoteException, 
        RemoveException;
}

The home interface of a session bean can return the EJBMetaData for the bean, just like an entity bean. EJBMetaData is a serializable object that provides information about the bean’s interfaces. The only difference between the EJBMetaData for a session bean and an entity bean is that the getPrimaryKeyClass() on the session bean’s EJBMetaData throws a java.lang.RuntimeException when invoked:

public interface javax.ejb.EJBMetaData {
    public abstract EJBHome getEJBHome();
    public abstract Class getHomeInterfaceClass();
    public abstract Class getPrimaryKeyClass();
    public abstract Class getRemoteInterfaceClass();
    public abstract boolean isSession();
    public abstract boolean isStateless();  // EJB 1.0 only
}

ProcessPaymentBean: The bean class

As stated earlier, the ProcessPayment EJB accesses data that is not generally shared by systems, so it is an excellent candidate for a stateless session bean. This bean really represents a set of independent operations—another indication that it is a good candidate for a stateless session bean.

Here is the definition of the ProcessPaymentBean class, which supports the remote interface functionality:

package com.titan.processpayment;
import com.titan.customer.*;

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

import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.ejb.EJBException;
import javax.naming.NamingException;

public class ProcessPaymentBean implements javax.ejb.SessionBean {

    final public static String CASH = "CASH";
    final public static String CREDIT = "CREDIT";
    final public static String CHECK = "CHECK";
        
    public SessionContext context;

    public void ejbCreate() {
    }
    
    public boolean byCash(CustomerRemote customer, double amount)
        throws PaymentException{
        return process(getCustomerID(customer), amount, CASH, null, -1, null, null);
    }
    
    public boolean byCheck(CustomerRemote customer, CheckDO check, double amount)
        throws PaymentException{
        int minCheckNumber = getMinCheckNumber();
        if (check.checkNumber > minCheckNumber) {
            return process(getCustomerID(customer), amount, CHECK, 
                check.checkBarCode, check.checkNumber, null, null);
        }
        else {
            throw new PaymentException("Check number is too low. 
                Must be at least "+minCheckNumber);
        }
    }
    public boolean byCredit(CustomerRemote customer, CreditCardDO card, 
        double amount) throws PaymentException {
        if (card.expiration.before(new java.util.Date())) {
            throw new PaymentException("Expiration date has"+ " passed");
        }
        else {
            return process(getCustomerID(customer), amount, CREDIT, null,
            -1, card.number, new java.sql.Date(card.expiration.getTime()));
        }
    }
    private boolean process(Integer customerID, double amount, String type, 
        String checkBarCode, int checkNumber, String creditNumber, 
        java.sql.Date creditExpDate) throws PaymentException {

        Connection con = null;
        
        PreparedStatement ps = null;

        try {
            con = getConnection();
            ps = con.prepareStatement
                ("INSERT INTO payment (customer_id, amount, type,"+ 
                "check_bar_code,check_number,credit_number,"+
                "credit_exp_date) VALUES (?,?,?,?,?,?,?)");
            ps.setInt(1,customerID.intValue());
            ps.setDouble(2,amount);
            ps.setString(3,type);
            ps.setString(4,checkBarCode);
            ps.setInt(5,checkNumber);
            ps.setString(6,creditNumber);
            ps.setDate(7,creditExpDate);
            int retVal = ps.executeUpdate();
            if (retVal!=1) {
                throw new EJBException("Payment insert failed");
            }         
            return true;
        } catch(SQLException sql) {
            throw new EJBException(sql);
        } finally {
            try {
                if (ps != null) ps.close();
                if (con!= null) con.close();
            } catch(SQLException se) {
                se.printStackTrace();
            }
        }
    }
    public void ejbActivate() {}
    public void ejbPassivate() {}
    public void ejbRemove() {}
    public void setSessionContext(SessionContext ctx) {
        context = ctx;
    }
    private Integer getCustomerID(CustomerRemote customer) {
        try {
            return (Integer)customer.getPrimaryKey();
        } catch(RemoteException re) {
            throw new EJBException(re);
        }
    }
    private Connection getConnection() throws SQLException {
        // Implementations shown below
    }
    private int getMinCheckNumber() {
        // Implementations shown below
    }
}

The three payment methods all use the private helper method process() , which does the work of adding the payment to the database. This strategy reduces the possibility of programmer error and makes the bean easier to maintain. The process() method simply inserts the payment information into the PAYMENT table. The use of JDBC in this method should be familiar to you from your work on the bean-managed Ship EJB in Chapter 10. The JDBC connection is obtained from the getConnection() method, as shown in the following code:

private Connection getConnection() throws SQLException {
    try {
        InitialContext jndiCntx = new InitialContext();
        DataSource ds = (DataSource)
            jndiCntx.lookup("java:comp/env/jdbc/titanDB");
        return ds.getConnection();
    } catch(NamingException ne) {
        throw new EJBException(ne);
    }
}

The byCheck() and byCredit() methods contain some logic to validate the data before processing it. The byCredit() method verifies that the credit card’s expiration date does not precede the current date. If it does, a PaymentException is thrown.

The byCheck() method verifies that the serial number of the check is above a certain minimum, which is determined by a property that is defined when the bean is deployed. If the check number is below this value, a PaymentException is thrown. The property is obtained from the getMinCheckNumber() method. We can use the JNDI ENC to read the value of the minCheckNumber property:

private int getMinCheckNumber() {
    try {
        InitialContext jndiCntx = new InitialContext();
        Integer value = (Integer)
            jndiCntx.lookup("java:comp/env/minCheckNumber");
        return value.intValue();
    } catch(NamingException ne) {
        throw new EJBException(ne);
    }
}

Here we are using an environment property set in the deployment descriptor to change the business behavior of the bean. It is a good idea to capture thresholds and other limits in the environment properties of the bean rather than hardcoding them. This gives you greater flexibility. If, for example, Titan decided to raise the minimum check number, you would need to change the bean’s deployment descriptor only, not the class definition. (You could also obtain this type of information directly from the database.)

JNDI ENC: Accessing environment properties

In EJB, the bean container contract includes the JNDI environment naming context ( JNDI ENC). The JNDI ENC is a JNDI name space that is specific to each bean type. This name space can be referenced from within any bean, not just entity beans, using the name "java:comp/env". The enterprise naming context provides a flexible, yet standard, mechanism for accessing properties, other beans, and resources from the container.

We’ve already seen the JNDI ENC several times. In Chapter 10, we used it to access a resource factory, the DataSource. The ProcessPaymentBean also uses the JNDI ENC to access a DataSource in the getConnection() method. Furthermore, it uses the JNDI ENC to access an environment property in the getMinCheckNumber() method. This section examines the use of the JNDI ENC to access environment properties.

Named properties can be declared in a bean’s deployment descriptor. The bean accesses these properties at runtime by using the JNDI ENC. Properties can be of type String or one of several primitive wrapper types including Integer, Long, Double, Float, Byte, Boolean, and Short. By modifying the deployment descriptor, the bean deployer can change the bean’s behavior without changing its code. As we’ve seen in the ProcessPayment EJB, we could change the minimum check number that we’re willing to accept by modifying the minCheckNumber property at deployment. Two ProcessPayment EJBs deployed in different containers could easily have different minimum check numbers. Here’s how to declare a named property:

<ejb-jar>
    <enterprise-beans>
        <session>
            <env-entry>
                <env-entry-name>minCheckNumber</env-entry-name>
                <env-entry-type>java.lang.Integer</env-entry-type>
                <env-entry-value>2000</env-entry-value>
            </env-entry>
            ...
        </session>
        ...
    </enterprise-beans>
    ...
</ejb-jar>

EJBContext

The EJBContext.getEnvironment() method is optional in EJB 2.0 and 1.1, which means that it may or may not be supported. If it is not functional, the method will throw a RuntimeException. If it is functional, it returns only those values declared in the deployment descriptor as follows (where minCheckNumber is the property name):

<ejb-jar>
    <enterprise-beans>
        <session>
            <env-entry>
                <env-entry-name>ejb10-properties/minCheckNumber</env-entry-name>
                <env-entry-type>java.lang.String</env-entry-type>
                <env-entry-value>20000</env-entry-value>
            </env-entry>
            ...
        </session>
        ...
    </enterprise-beans>
    ...
</ejb-jar>

The ejb10-properties subcontext specifies that the minCheckNumber property is available from both the JNDI ENC "java:comp/env/ejb10-properties/minCheckNumber" (as a String value) and the getEnvironment() method.

Only those properties declared under the ejb10-properties subcontext are available via the EJBContext. Furthermore, such properties are available through the EJBContext only in containers that choose to support the EJB 1.0 getEnvironment() method; all other containers will throw a RuntimeException. It is expected that most EJB 2.0 vendors will have dropped support for this feature, and developers are encouraged to use the JNDI ENC instead of the EJBContext.getEnvironment() method to obtain property values.

The ProcessPayment EJB’s deployment descriptor

Deploying the ProcessPayment EJB presents no significant problems. It is essentially the same as deploying entity beans, except that the ProcessPayment EJB has no primary key or persistence fields. Here is the XML deployment descriptor for the ProcessPayment 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>
        <session>
            <description>
                A service that handles monetary payments.
            </description>
            <ejb-name>ProcessPaymentEJB</ejb-name>
            <home>
                com.titan.processpayment.ProcessPaymentHomeRemote
            </home>
            <remote>
                com.titan.processpayment.ProcessPaymentRemote
            </remote>
            <ejb-class>
                com.titan.processpayment.ProcessPaymentBean
            </ejb-class>
            <session-type>Stateless</session-type>
            <transaction-type>Container</transaction-type>
            <env-entry>
                <env-entry-name>minCheckNumber</env-entry-name>
                <env-entry-type>java.lang.Integer</env-entry-type>
                <env-entry-value>2000</env-entry-value>
            </env-entry>
            <resource-ref>
                <description>DataSource for the Titan database</description>
                <res-ref-name>jdbc/titanDB</res-ref-name>
                <res-type>javax.sql.DataSource</res-type>
                <res-auth>Container</res-auth>
            </resource-ref>
        </session>
    </enterprise-beans>
 
    <assembly-descriptor>
        <security-role>
            <description>
                This role represents everyone who is allowed full access 
                to the ProcessPayment EJB.
            </description>
            <role-name>everyone</role-name>
        </security-role>

        <method-permission>
            <role-name>everyone</role-name>
            <method>
                <ejb-name>ProcessPaymentEJB</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>

        <container-transaction>
            <method>
                <ejb-name>ProcessPaymentEJB</ejb-name>
                <method-name>*</method-name>
            </method>
            <trans-attribute>Required</trans-attribute>
        </container-transaction>
    </assembly-descriptor>
</ejb-jar>

The deployment descriptor for EJB 1.1 is exactly the same, except its header specifies the EJB 1.1 specification and deployment descriptor:

<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise
JavaBeans 1.1//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">

The ProcessPayment EJB’s deployment descriptorPlease refer to Workbook Exercise 12.1, A Stateless Session Bean. This workbook is available free, in PDF format, at http://www.oreilly.com/catalog/entjbeans3/workbooks.

EJB 2.0: Local component interfaces

Like entity beans, stateless session beans can define local component interfaces. This allows the local interfaces of a stateless session bean to be used by other co-located enterprise beans, including other stateless and stateful session beans and even entity beans. Obviously, it is more efficient to use local component interfaces between two beans in the same container system than to use the remote interfaces.

The process of defining local interfaces for a stateless or stateful session bean is the same as that for entity beans. The local interfaces extend javax.ejb.EJBLocalObject (for business methods) and javax.ejb.EJBLocalHome (for the home interfaces). These interfaces are then defined in the XML deployment descriptor in the <local> and <local-home> elements.

For the sake of brevity, we will not define local interfaces for either the stateless ProcessPayment EJB or the stateful TravelAgent EJB developed later in this chapter. Your experience creating local interfaces for entity beans in Chapter 5, Chapter 6, and Chapter 7 can be applied easily to any kind of session bean.

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

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