Chapter 11. Session Beans

Entity beans provide an object-oriented model that makes it easier for developers to create, modify, and delete data from the database. They allow developers to be more productive by encouraging reuse, thus reducing development costs. For example, once a bean has been defined to represent a concept like a Ship, that bean can be reused throughout a business system without redefining, recoding, or retesting the business logic and data access.

However, entity beans are not the entire story. We have 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 (taskflow) 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.

When do you use an entity bean and when do you use a session bean? As a rule of thumb, an entity bean should 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 taskflow. Taskflow means all the steps required to accomplish a particular task, such as booking passage on a ship or renting a video. Session beans frequently 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. Actors are pointless without a script; they may represent something, but they can’t tell a story. Similarly, entities represented in a database aren’t meaningful unless you can have interactions between entities. It makes no sense to have a database full of cabins, ships, customers, and such 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.

A stateful session bean is an extension of the client application. It performs tasks on behalf of a 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.

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. After all, clients can crash, and users can walk away from their desks and forget what they’re doing; we don’t want stateful session beans associated with dead clients or forgetful users cluttering up our server forever. 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. As soon as a stateless session bean has finished a method invocation, it can be reassigned to service a new client. Stateless session beans may also have a timeout period and can be removed by the client, but the impact of a bean timeout or removal is different than with a stateful 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.

Whether they are stateful or stateless, session beans are not persistent like entity beans. In other words, session beans don’t represent persistent date and are not saved to the database.

The Stateless Session Bean

A stateless session bean is very efficient and relatively easy to develop. A session bean can be swapped freely between EJB objects because it isn’t dedicated to one client and doesn’t maintain any conversational state. As soon as it is finished 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.

Saying that a stateless session bean doesn’t maintain any conversational state means that every method invocation is independent of previous invocations, and that everything the method needs to know has to be passed via the method’s parameters. Since stateless session beans can’t remember anything from one method invocation to the next, they must take care of an 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.

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 all of its requests. Instance variables may have different values in different bean instances, so their values can 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 might 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. Because many different systems charge customers for services, we’ve encapsulated the logic for charging customers 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.

The database table (PAYMENT)

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
)

The remote interface (ProcessPaymentRemote)

A stateless session bean, like an entity bean, may have a local or remote interface, or both. The remote interface obviously needs 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 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 whether the payment succeeded. 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 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. A stateless session bean can serialize and reuse this Handle at any time, as long as the stateless bean type is still available in the container that generated the Handle. 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 class files for this bean.

Dependent objects (CreditCardDO and CheckDO classes)

The ProcessPayment EJB’s remote interface uses two classes that are particularly interesting, CreditCardDO and CheckDO:

/* 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. They are simply serializable Java classes, not enterprise beans; they provide a convenient mechanism for transporting related data. CreditCardDO, for example, collects 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.

An application exception (PaymentException)

Any remote or local interface, whether it’s for an entity bean or a session bean, can throw application exceptions. Application exceptions 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. You must use try/catch blocks to capture checked exceptions like these.

The EJBException indicates that the container ran into problems processing a local interface invocation. EJBException is unchecked, so you won’t get a compile error if you don’t catch it. However, under certain circumstances it is a good idea to catch EJBException, and in other circumstances it should be propagated.

When a bean method catches a checked exception from a subsystem ( JDBC, JNDI, JMS, etc.), it should be rethrown as either an EJBException or an application exception. You would rethrow a checked exception as an EJBException if it represented a system-level problem; use an application exception if the original exception resulted 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:

package com.titan.processpayment;

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

The home interface (ProcessPaymentHomeRemote)

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 either, because session beans do not represent data in the database. Unlike stateful session beans and entity beans, stateless session beans may not define any create< SUFFIX >( ) methods. 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 this method is called, it throws a RemoteException. Here is the definition of the javax.ejb.EJBHome interface:

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 calling getPrimaryKeyClass( ) on the session bean’s EJBMetaData throws a java.lang.RuntimeException:

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
}

The bean class (ProcessPaymentBean)

The ProcessPayment EJB accesses data that is not generally shared by other parts of the system, 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:

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 JDBC connection is obtained from the getConnection( ) method:

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. byCredit( ) verifies that the credit card’s expiration date does not precede the current date. If it does, a PaymentException is thrown. byCheck( ) 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, which uses 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);
    }
}

It is a good idea to capture thresholds and other limits in the bean’s environment properties, rather than hardcoding them: it 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.)

Accessing environment properties (JNDI ENC)

In EJB, the bean container contract includes the JNDI environment naming context (JNDI ENC). The JNDI ENC is a JNDI namespace that is specific to each bean type. This namespace 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 9, 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 and their values defined 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 setting the values of the relevant properties, 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.

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>

The ProcessPayment EJB’s deployment descriptor

Deploying the ProcessPayment EJB presents no significant problems. It is essentially the same as deploying an entity bean, except that the ProcessPayment EJB has no primary key or persistence fields. Here is the XML deployment descriptor for the ProcessPayment EJB in EJB 2.1:

<?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>
        <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 2.0 is exactly the same, except it’s based on a DTD instead of an XML Schema, so it uses a document declaration and has a simpler <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>
     <enterprise-beans>
     ...

Exercise 11.1 in the Workbook shows how to deploy these examples.

Local component interfaces

Like entity beans, stateless session beans can define local component interfaces. Local interfaces allow other beans in the same container to use the stateless session bean more efficiently. The process of defining local interfaces for a stateless or stateful session bean is the same as 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
3.145.191.134