[ Team LiB ] Previous Section Next Section

Working with Stateless Session Beans

In this section, you learn how to create, deploy, and use a Stateless Session Bean. As discussed earlier, Stateless Session Beans maintain no client-specific conversational state. They perform particular tasks on invocation and return the results to the client. Once that's done, other clients can make requests to the same bean instance. The bean does not distinguish between clients. Neither does it distinguish between first and subsequent calls from the same client. Because of this, a client should not assume that it will get the same instance of the bean every time it performs a request. Stateless Session Beans should typically be used to perform any generic operation that can be performed completely by making a single method call to the bean, and does not depend on client-specific state stored within the bean instance.

Stateless Session Beans are very efficient by construction. In the previous chapter, you read about passivation and activation as techniques the container uses to manage resources effectively. Because these beans don't maintain client specific state, every Stateless Session Bean instance is equivalent to another, and hence does not require passivation or activation. That means these beans do not use too many resources and are lightweight.

The Remote Interface

Clients to the XYZ Reservation System aren't expected to access the AirlineReservation EJB directly. They're expected to use the shopping cart bean, which will in turn use this bean to perform the reservation tasks. Therefore, this bean should not provide the capability for remote access; in other words, the bean should not contain a remote interface or remote home interface. All it should provide is a set of local interfaces so that the shopping cart EJB can access the bean services. That having been said, we're still going to create the remote interface and remote home interface for academic reasons.

NOTE

Note that when we refer to a client in our discussion, we aren't necessarily referring to a user interface unless explicitly stated. We're referring to any object that uses the EJB—the client to the bean. Also note that in the following discussion, all classes go into the package com.wlsunleashed.ejb.session.stateless unless otherwise stated.


In accordance with the naming convention that was laid out in the previous chapter, let's name our remote interface AirlineReservationRemoteObject.java.

The AirlineReservation EJB provides three pieces of functionality. First, it enables users to search through the airline database for flights that operate between a given set of cities. This function returns an array of flight numbers. The user can then invoke the second method on the bean with a flight number, which returns the flight information as a Properties object. The third operation is a reservation method, which books the given number of seats in the given flight.

As mentioned earlier, the remote interface should extend from javax.ejb.EJBObject interface. Our remote interface for the AirlineReservation EJB will look like the following:


import java.rmi.RemoteException;
import java.util.Properties;
import javax.ejb.EJBObject;
public interface AirlineReservationRemoteObject extends EJBObject {
  public int[] getFlightNumbers(String origin, String destination)
    throws RemoteException, AirlineReservationException;
  public Properties getFlightInfo(int flightNumber)
    throws RemoteException, AirlineReservationException;
  public void reserveSeats( int flightNumber, int numSeats)
    throws RemoteException, AirlineReservationException;
}

It's obvious from the code that each method in the bean is completely independent of the others. Each operation can be completed by calling exactly one method in the bean. There's no requirement to invoke the methods in any order. All the information needed to perform each operation is passed in as parameters into the methods. Therefore, this bean is an ideal candidate for a Stateless Session Bean.

The parameters and the return types of each of these methods deserve a special mention. Because communication will be over the network using RMI, the data is serialized over the network. For data to be serialized, any non-primitive objects that are passed must be serializable; that is, they must implement the tagging interface, java.io.Serializable. Null references and primitive types can also be passed as parameters. The same rules hold good for return types as well.

NOTE

Refer to Chapter 20 for a detailed discussion of the methods provided by the parent interface (javax.ejb.EJBObject) and their uses. One point to note is that for Stateless Session Beans, the getPrimaryKey method is irrelevant. The EJBObject interface has this method declared in it because the remote interfaces of both Session and Entity Beans extend from the same interface. If you try to invoke this method from a Session Bean, the method throws a RuntimeException.


Understanding Exceptions

All methods in the remote interface throw the exception of type java.rmi.RemoteException. The bean implementation usually won't throw this exception from its code. The methods declare this exception in their signatures because the RMI facility can throw this exception when there is any kind of system-level failure while contacting the remote object. By ensuring that all methods in the remote interface throw this exception, the compiler on the client side forces the client to handle RemoteException.

The business methods may also throw application exceptions. An application exception is any exception thrown by your bean implementation, which indicates an application-level failure (either a violation of a business rule or any other failure that you expect the client to handle). In our case, you can see that our methods throw an exception of type AirlineReservationException. Because the application exception class would extend java.lang.Exception, which is serializable, exceptions can be thrown over the network for clients to handle.

TIP

Other subsystems that your bean might access could also throw exceptions. For instance, when you attempt an SQL operation, the database driver might throw a java.sql.SQLException. It's not a good practice to make your remote interface throw such low-level exceptions to the client. As far as possible, try to handle the exception at the bean level. If you cannot handle it, wrap the exception and throw it as an application exception. This way, you ensure that your bean abstracts the implementation nuances of the subsystems that you use in your bean, while at the same time ensuring that the exception is handled in one way or another.


EJB 2.0 introduced yet another type of exception, called javax.ejb.EJBException. This is an unchecked exception (subclass of java.lang.RuntimeException), and the compiler won't force your client code to catch this exception type and handle it. These exceptions are usually reserved to report system-level failures that might occur at runtime. For example, if your bean attempts to perform a database operation and the database is down, that can be considered a system exception and reported back using the EJBException. This exception can also contain a wrapped exception that can provide more information about the actual condition. This exception object is returned along with the EJBException. All other types of exceptions (both in the business logic and in subsystems) should be reported back to the client as application exceptions.

When a runtime exception (such as EJBException) is thrown from a bean, the container first traps this exception. It then discards the bean instance that generated the runtime exception, and replaces it with another bean instance. The exception is then propagated to the client inside a RemoteException for the client to handle it.

The Remote Home Interface

Think of the remote home interface as a factory for the bean. It contains the life cycle methods for the bean. Using the remote home interface, your application can create, find, and remove beans over the network. While developing a bean, you code only the remote home interface, not its implementation.

In accordance with the naming convention that we adopted earlier, we'll name the remote home interface of the bean AirlineReservationRemoteHome. Our remote home interface will consist of a single method to create a bean.

Remote home interfaces extend the javax.ejb.EJBHome interface. The remote home interface of a Stateless Session Bean is required to declare only a single create method that takes no arguments and has no suffixes. This requirement stems because of the life cycle of a stateless Session Bean. The life cycle of this bean is discussed later in this chapter. The create method returns an object that implements the bean's remote interface.

As mentioned earlier, the remote home interface extends from the javax.ejb.EJBHome interface. This interface extends from the java.io.Remote interface, which makes any implementation of the remote home interface eligible for use over the network. The source code for our remote home interface is given in the following snippet:


import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface AirlineReservationRemoteHome extends EJBHome {
  public AirlineReservationRemoteObject create()
    throws RemoteException, CreateException ;
}

This remote home interface lists one method for creating a new instance of the bean. The bean instance is returned as an object that implements the remote interface of the bean. Note that the create method throws both java.io.RemoteException and javax.ejb.CreateException. Both exceptions are required to be thrown by this method declaration, so that the client using this bean can handle these exception types. The RemoteException is reported back to the client whenever a system-level failure occurs while accessing the bean, or when the create method throws an unchecked exception. When the container wants to let the client know that there was an error while creating the bean, CreateException should be used. This exception must be caught and handled by the client.

The Local Interface

The local interface of an EJB is similar to the remote interface in that the local interface also lists all the business operations provided by the bean. These interfaces are used by other co-located clients that exist within the same application as the bean. When used, local interfaces avoid any RMI calls, thus making the calls very efficient. The same bean may provide both local and remote access by including both sets of interfaces, or just any one type of access by including the appropriate set interfaces in its deployment.

The local interface extends from the javax.ejb.EJBLocalObject interface. According to the established naming convention, we'll name our local interface AirlineReservationLocalObject. The local interface is presented in the following code snippet:


import java.util.Properties;
import javax.ejb.EJBLocalObject;
public interface AirlineReservationLocalObject extends EJBLocalObject {
  public int[] getFlightNumbers(String origin, String destination)
    throws AirlineReservationException;
  public Properties getFlightInfo(int flightNumber)
    throws AirlineReservationException;
  public void reserveSeats( int flightNumber, int numSeats)
    throws AirlineReservationException;
}

One thing that is strikingly different from the code snippet for a remote interface (refer to the earlier section about the remote interface) is the fact that none of these methods throw RemoteException. This is quite obvious because no network is functionality involved in using local interfaces. Your methods can throw application exceptions, which must also extend java.lang.Exception. The container and the bean may still throw unchecked exceptions as EJBException, which will be reported back to the client.

The local component interfaces have been optimized in such a way that any call made using the local interface is executed as if the bean were just another object on the JVM. That means for local interfaces, there's no requirement for the parameter objects to be serializable, and they are passed by reference to the bean. It also means that you should be very careful to make sure that your application can handle this behavior.

CAUTION

There's a downside to using local interfaces: It reduces the location transparency of the beans. So, if you're sure that the objects will always colocate, you must give preference to using local interfaces; not otherwise.


The Local Home Interface

The local home interface works in conjunction with the local interface. This interface also declares a create method to create the bean. The difference between this declaration and the one in its remote counterpart is that the create method does not throw RemoteException. The create method now returns an object that implements the local interface, rather than the remote interface of the bean.

The local home interface extends from the javax.ejb.EJBLocalHome interface. The name of the local home interface as specified by our naming convention is AirlineReservationLocalHome. The code of the AirlineReservationLocalHome interface is presented in the following snippet:


import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;
public interface AirlineReservationLocalHome extends EJBLocalHome {
  public AirlineReservationLocalObject create()
    throws CreateException ;
}

The Bean Class

The Bean class contains the implementation of the bean's business functionality. This class does not implement any of the interfaces discussed earlier. Instead, the Bean class defines methods that closely resemble the methods declared in the interfaces. The classes that actually implement the interfaces defined earlier are generated by the container, and the calls are delegated to the bean implementation. The Bean class defines both life cycle methods as well as business methods. Let's first take a look at Listing 21.1, which represents the skeleton of the bean class.

Listing 21.1 A Bean Class Skeleton
import java.rmi.RemoteException;
import java.util.Properties;
import javax.ejb.CreateException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class AirlineReservationBean implements SessionBean {
  SessionContext context = null;
  public AirlineReservationBean() {
    super();
  }
  public void setSessionContext(SessionContext aContext)
    throws RemoteException {
    System.out.println(" setSessionContext called ");
    context = aContext;
  }
  public void ejbCreate() throws CreateException {
    System.out.println("ejbCreate called");
  }
  public void ejbRemove() {
    System.out.println("ejbRemove called");
  }
  public void ejbActivate() {
    System.out.println("ejbActivate called");
  }
  public void ejbPassivate() {
    System.out.println("ejbPassivate called");
  }
  public int[] getFlightNumbers(String origin, String destination)
    throws AirlineReservationException {
    return null;
  }
  public Properties getFlightInfo(int flightNumber)
    throws AirlineReservationException {
    return null;
  }
  public void reserveSeats(int flightNumber, int numSeats)
    throws AirlineReservationException {
  }
}

The business methods haven't been implemented in Listing 21.1, but the code itself is complete enough to be deployed into WebLogic Server. Let's analyze each element of this code.

The EJB specification lays down some strict rules for developers creating the bean implementation. If any rule is violated, the bean cannot be deployed by the container.

First of all, the bean implementation should be declared public, and cannot be declared final or abstract. It should not have constructors other than the default constructor. Any other constructor that's defined will never be used. This is because the container instantiates the Bean class by calling the Class.newInstance method, which uses the default constructor. The class must not define a finalize method.

The following list details the different methods that have been defined in the Bean class (the ones other than the business methods) and how the container uses them:

  • setSessionContext— This is the first method invoked by the EJB container on the bean instance, sometime after the bean has been created; that is, after its constructor is invoked. This method passes in an object of type SessionContext, which provides several pieces of information to the bean instance about the circumstances under which it's being created and used. The bean can keep a reference to that object for later use.

  • ejbCreate— This method is invoked by the EJB container on the bean instance after the setSessionContext method is invoked, but before any business method has been invoked. This method won't necessarily be called when the client calls the create method on the home interface. The container is free to create bean instances whenever it wants, and will call ejbCreate on those instances at creation time.

    In the case of Stateless Session Beans, the bean class must define exactly one ejbCreate method that doesn't take any parameters and returns nothing. This method corresponds with the create method of the remote home and local home interfaces of the bean. This method also must throw application exceptions that are declared in the create method of the home interfaces of the bean. This callback method helps your bean instance to perform initialization routines. For example, if your bean requires the use of JDBC resources, they can be looked up in this method.

    The ejbCreate method does not add too much value in the case of a Stateless Session Bean. All initialization code can be written in the constructor, giving the exact same results. But as we discussed earlier, you must understand that the only constructor that will be invoked is the default constructor because the bean is created using the Class.newInstance method. Note also that if your initialization process requires the session context, you cannot use the constructor for initialization. The advantage of the ejbCreate methods become more apparent in Stateful Session Beans and Entity Beans, where you're allowed to overload ejbCreate methods and accept different parameters, thus giving different flavors to the bean creation process.

  • ejbRemove— This method is invoked by the container when the container decides to destroy the bean instance for any reason. This method indicates to the bean that the instance is about to be removed from memory, and provides an opportunity to the bean instance to perform finalization and cleanup procedures. Remember that when a client invokes the remove method on the component or the home interface of a Stateless Session Bean, the bean instance isn't destroyed. It is simply moved to the method-ready pool. It's important to remember that this method isn't invoked when the bean instance is moved back to the method-ready pool; rather, it is invoked only when the bean instance is actually destroyed by the container. However, if the method-ready pool already has enough bean instances, the container may decide to destroy the bean instance, therefore ending up calling the ejbRemove method. For more information about method-ready pools, refer to the “Life Cycle of a Stateful Session Bean” section later in this chapter.

  • ejbPassivate and ejbActivateChapter 20 introduced you to the concepts of activation and passivation as mechanisms used by the container to conserve resources. Stateless Session Beans do not hold client state, and therefore don't participate in either passivation or activation. These methods are present in the bean class to satisfy the requirements of the inherited interface. These methods don't hold any relevance for Stateless Session Beans. They're never invoked, and do not have to do anything.

Apart from these methods, the Bean class also defines the business methods as declared in the component interfaces. Note that the business methods do not throw RemoteExceptions, unlike the component interfaces' methods. The container usually throws these exceptions if it has to.

Developing the AirlineReservation EJB

In the code snippet provided here, the business methods don't have any logic defined. However, it's quite straightforward to code business logic into these methods. The bean implementation that's packaged with this book contains the entire source code com.wlsunleashed.ejb.session.stateless.AirlineReservationBean. You can also look up the associated interfaces in the same package.

Any JNDI lookups can be typically performed at initialization time. Therefore, in our bean class, we'll look up the initial context and the DataSource object (javax.sql.DataSource, used for creating database connections) in the ejbCreate method and store it locally in the class. As discussed earlier in this chapter, a Stateless Session Bean instance cannot hold conversational state that's specific to one client, but it can definitely hold state, which will be shared across all clients accessing that bean instance. We'll release all these resources in the remove method of the bean instance.

Apart from this, the individual methods function independently of each other. Each method looks up a database connection from the DataSource object and uses it to perform its SQL functions. No method depends on any other method to have been (or not to have been) invoked previously. This makes this bean a suitable candidate for the Stateless Session Bean model. Any SQLException that might be thrown by the SQL processing is reported to the client as an application exception by wrapping it in the AirlineReservationException object. After each method has finished execution, the Statement and Connection objects are closed by invoking the close methods on these objects. For more information on DataSource and connection pools, refer to Chapter 10.

Deployment Descriptors

Chapter 20 introduced you to deployment descriptors as mechanisms by which you configure your bean deployment in the container. In this section, we don't focus on all the possible parameters that can be set in deployment descriptors, but only on the relevant portions for Stateless Session Beans.

ejb-jar.xml

In this section, we briefly take a look at this descriptor for the Airline Reservation bean. Listing 21.2 presents the ejb-jar.xml file for our bean.

Listing 21.2 ejb-jar.xml Deployment Descriptor
1.  <!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
graphics/ccc.gif 2.  'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
2.  <ejb-jar>
3.   <enterprise-beans>
4.    <session>
5.     <ejb-name>AirlineReservationBean</ejb-name>
6.     <home>com.wls70unleashed.ejb.session.stateless. AirlineReservationRemoteHome</home>
7.     <remote>com.wls70unleashed.ejb.session.stateless. AirlineReservationRemoteObject<
graphics/ccc.gif/remote>
8.     <local-home>com.wls70unleashed.ejb.session.stateless. AirlineReservationLocalHome<
graphics/ccc.gif/local-home>
9.     <local>com.wls70unleashed.ejb.session.stateless. AirlineReservationLocalObject</local>
10.    <ejb-class>com.wls70unleashed.ejb.session.stateless. AirlineReservationBean</ejb-class>
11.    <session-type>Stateless</session-type>
12.    <transaction-type>Container</transaction-type>
13.   </session>
14.  </enterprise-beans>
15.  <assembly-descriptor>
16.   <container-transaction>
17.    <method>
18.     <ejb-name>AirlineReservationBean</ejb-name>
19.     <method-name>*</method-name>
20.    </method>
21.    <trans-attribute>Supports</trans-attribute>
22.   </container-transaction>
23.  </assembly-descriptor>
24. </ejb-jar>

The first line of this listing identifies the XML document. It specifies to the XML parser that the container will use, that this XML contains a root-element called <ejb-jar>, and its DTD can be found in the URL http://java.sun.com/dtd/ejb-jar_2_0.dtd. The parser will then validate the contents of this XML file based on the rules laid out by the given DTD. If the parser finds any discrepancy between the XML file and the rules specified, it will report an error and the deployment will not be successful.

This XML file (as in any other XML file) always has one outer element in the document body: <ejb-jar>. All other elements of this descriptor lie within the opening and closing <ejb-jar> element. From the listing, it's obvious that there are two entries within this stanza: <enterprise-beans> (line 3) and <assembly-descriptor> (line 15). The only entry that's necessary for deployment is the <enterprise-beans> stanza. The <assembly-descriptor> stanza is optional, as are the other optional entries that can go within the <ejb-jar>.

One EJB deployment descriptor can contain more than one bean. You can also combine different types of beans (Session, Entity, And Message-Driven) within the same EJB deployment. Each bean that's deployed in a bean must have one section within the <enterprise-beans> section of the XML. In line 4 of the listing, you find a stanza that begins with the tag <session>. This identifies a bean of type session. Similarly, you could have other stanzas here—such as <entity> and <message-driven>—to declare beans of those types.

The <session> stanza of the descriptor describes the Session Bean to the container. It must contain the following elements:

  • A unique name identifying the bean (<ejb-name>).

  • The name of the bean's remote interface, if applicable (<remote>).

  • The name of the bean's remote home interface, if applicable (<home>).

  • The name of the bean's local interface, if applicable (<local>).

  • The name of the bean's local home interface, if applicable (<local-home>).

  • The name of the bean's implementation class (<ejb-class>).

  • The bean's session management type (<session-type>). This indicates to the container whether the bean is stateless or otherwise. The two values it can take are Stateless and Stateful.

  • The bean's transaction management type (<transaction-type>). This indicates to the container the type of transaction management that's required for this bean.

The section must contain at least one of the two pairs of home and component interfaces—the remote implementation, the local implementation, or both. The rest of the tags are mandatory. Apart from these tags, this stanza can also list information about entries that the bean's implementation expects to be present in its environment, resource references, references to other beans that are used by this bean, security identities, and more. All these concepts were covered in Chapter 20.

After we've defined all the beans that form our deployment, we can describe some properties that describe the assembly of the beans within this deployment. To do this, we use another element within the <ejb-jar> stanza known as the <assembly-descriptor>. This stanza describes two key elements of bean deployment: the transaction attributes of the bean's methods and the security. We'll look at these attributes in detail in later sections of this chapter. For now, it's sufficient to understand that the <assembly-descriptor> stanza indicated in this listing specifies to the container that all methods in the Airline Reservation bean support user transactions, but none of them requires it. For a detailed study of this section of the descriptor, refer to Chapter 20.

weblogic-ejb-jar.xml

The simple WebLogic Server–specific deployment descriptor used for the Airline Reservation EJB is shown in Listing 21.3.

Listing 21.3 weblogic-ejb-jar.xml
<!DOCTYPE weblogic-ejb-jar PUBLIC '-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN''http:
graphics/ccc.gif//www.bea.com/servers/wls700/dtd/weblogic700-ejb-jar.dtd'>
<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
    <ejb-name>AirlineReservationBean</ejb-name>
     <stateless-session-descriptor>
      <pool>
       <initial-beans-in-free-pool>0</initial-beans-in-free-pool>
      </pool>
      <stateless-clustering>
      </stateless-clustering>
     </stateless-session-descriptor>
     <transaction-descriptor>
     </transaction-descriptor>
     <jndi-name>RemoteAirlineReservationBean</jndi-name>
     <local-jndi-name>LocalAirlineReservationBean</local-jndi-name>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

Most of the properties that are part of this descriptor were discussed in Chapter 20. We'll look only at the <stateless-session-descriptor> stanza (under <weblogic-ejb-jar><weblogic-enterprise-bean>). This stanza can contain two sections: <stateless-clustering> and <pool>. The <stateless-clustering> stanza describes the clustering properties of the Stateless Session Bean. We'll look more about this stanza in the “Clustering a Stateless Session Bean” section later in this chapter. The <pool> stanza contains two elements:

  • <max-beans-in-free-pool>— This parameter identifies to the container the maximum number of bean instances that should be active at any time. As long as the number of instances active in the container is lower than this figure, the container will create new instances of the bean when a request comes in and it cannot find free instances to process the request.

  • <initial-beans-in-free-pool>— The number of instances that the container will initialize upon server startup. This value will help in tweaking the performance of your bean.

Stateless Session Bean pools will be discussed in the chapter that describes the stateless bean life cycle.

Packaging and Deploying the Bean

Refer to Chapter 20 for a discussion of packaging and deploying the bean. Essentially, you can deploy the bean by itself as described in Chapter 20 or as an enterprise application archive (EAR) file. If you decide to deploy the bean by itself, feel free to do so. For the sake of this chapter, we've provided an Ant script that will enable you to deploy the application as an EAR file.

To use the Ant script, you should have Ant installed in your system. You can download Ant free of cost from http://ant.apache.org. This script has been tested with Ant version 1.5. Make sure that you've copied the optional.jar into the lib directory of your Ant installation to enable access to the optional Ant tasks. Change to the directory under which you'll find the source code for this chapter. You should find some JSP and HTML files and a directory called airlinedata, which contains the relevant deployment descriptors along with the source package. Execute Ant by using the following command line. You must provide the base directory of the WebLogic Server installation; for example, c:\bea\weblogic81:


ant -Dwlhome=c:\bea\weblogic81

This line will build the EAR file and place it under the current directory with the name AirlineReservation.ear. You can now deploy this file into your WebLogic Server by simply copying the EAR file into the applications folder of your domain. Remember to define the appropriate connection pool and data sources before you deploy this EAR file. This Ant task will also create a client JAR file, which must be included in the classpath of standalone clients. This file will be named AirlineReservationClient.jar.

Accessing a Stateless Session Bean from a Client

Now that we've deployed our bean into WebLogic Server's container, we're ready to write a client application, which will access the bean, perform a search for the flights, and display the results on screen. In a typical reservation system, this job would be done by a graphical user interface. In our case, we're going to build a text-based interface that enables you to search through the flight information.

Remote Clients to the Bean

Our application, which uses the EJB to search the flight database for flights when given two cities, will be built as a standalone application that will run in its own JVM. Although this requires that WebLogic Server to be up and running, this client won't execute by itself within WebLogic Server's JVM. This type of client is referred to as a remote client.

Invoking a method on the EJB from a remote client involves the following steps:

  • Establish an initial context with the server.

  • Query the JNDI tree and look up the remote home of the EJB using the name that it's bound under. Note that, by definition, such clients cannot use the local interfaces.

  • Create a bean instance by using the life cycle methods of the home interface obtained.

  • Call the appropriate method on the bean instance.

  • Close the initial context to release resources.

For more information about the steps involving the JNDI tree, refer to Chapter 8, “Naming and Directory Services with JNDI.” Listing 21.4 outlines the basic steps to perform an EJB call that have been described in this section.

Listing 21.4 Simple Client to a Stateless Session Bean
 try {
    InitialContext ic = null;
    Hashtable props = new Hashtable() ;
    props.put( Context.INITIAL_CONTEXT_FACTORY,
          "weblogic.jndi.WLInitialContextFactory" );
    props.put( Context.PROVIDER_URL,
          "t3://localhost:7001");
    ic = new InitialContext( props ) ;
    Object o = ic.lookup("RemoteAirlineReservationBean");
    AirlineReservationRemoteHome arrho = (AirlineReservationRemoteHome)
      PortableRemoteObject.narrow(o,
        AirlineReservationRemoteHome.class );
    AirlineReservationRemoteObject arro = (AirlineReservationRemoteObject)
        arrho.create();
   System.out.println("remote instance created");
   int[] values = arro.getFlightNumbers(origin, destination);
   System.out.println("Flight Numbers between " + origin +
      " and " + destination );
   for (int i = 0; i < values.length; i++) {
      System.out.println(" --> " + values[i] );
   }
   System.out.println ( "-- finished." );
   ic.close();
 }
 catch (Exception ex) {
   ex.printStackTrace();
 }

One point of interest in this code listing is highlighted in bold. Notice that the remote home is looked up as merely an Object. Following this, we invoke the static narrow method on the class PortableRemoteObject and cast that into the remote interface. To accommodate remote objects written in languages other than Java, and which don't have any concept of casting between two types, IIOP places the restriction on stubs implementing multiple interfaces. The stub object returned merely implements the interface that is declared to be returned by the remote method. In the case of the lookup method, the remote method returns an object of type Object; therefore, the stub merely implements the interface specified by this class. To make this stub more usable in our EJB client, Java's RMI IIOP classes provide the narrow method in the PortableRemoteObject class. When invoked with the stub object obtained from the server along with the interface class, the narrow method dynamically casts the object into the right interface and returns an object that can then be cast into the appropriate interface.

This client application can be found in the source folder, under com/wlsunleashed/ejb/session/stateless/SearchFlightRemoteClient.java,. To execute this client, make sure that your WebLogic Server is up and running. You also should have deployed the stateless EJB that we built under the remote JNDI name of AirlineReservationBean. This will be done automatically if you used the deployment descriptors provided. The client also assumes that the WebLogic Server is running in the localhost, listening in port 7001. Invoke this client by passing in the two cities on the command line. Not passing the cities will make the client default to two cities and perform the search. To test this example, you must include the client JAR file generated by the build process in your classpath. The JAR file is named AirlineReservationClient.jar, and it contains all the required classes and interfaces for the client side.

Other Components Within the Application Acting as Clients

Other components that live within the same WebLogic application, running inside the same JVM as the EJB, can also access the bean. When we do this, we can actually use the local interfaces and do away with any network overhead while accessing the bean. We'll look at the sample program while working with Stateful Session Beans. For now, it's sufficient to understand that there's not much difference between these clients and the remote clients. One of the few differences is to actually look up the local home from the JNDI tree using the name defined in <local-jndi-name> (under <weblogic-ejb-jar><weblogic-enterprise-bean> in the weblogic-ejb.jar descriptor) in Listing 21.3. From here on, you can create the local object by invoking the create method on the local home. The following steps are involved:

  • Obtain the initial context. This time, don't pass any parameters to the InitialContext's constructor. This will make the class create an initial context to the same WebLogic Server instance. For more information about performing JNDI lookups, refer to Chapter 8.

  • Look up the local home of the EJB by using the JNDI name under which the local home is bound.

  • Create the local object using the life cycle methods of the local home.

  • Perform the EJB operations.

  • Close the Initial context.

Also note that while using local interfaces with colocated EJBs, you don't need to perform a call to PortableRemoteObject.narrow because you are no longer dealing with remote objects. RemoteExceptions don't need to be caught either, but EJBExceptions may be caught instead.

Life Cycle of a Stateless Session Bean

Figure 21.1 illustrates the life cycle of the Stateless Session Bean within the WebLogic Server container.

Figure 21.1. Stateless Session Bean Life cycle.

graphics/21fig01.gif

Initially, the container does not have any beans. While deploying the bean, you can set up a parameter called <initial-beans-in-free-pool> (in weblogic-ejb.jar, under <weblogic-ejb-jar><weblogic-enterprise-bean><stateless-session-descriptor><pool>), which indicates to the container that you require a certain number of bean instances to be created even before any request arrives to the container. The container creates these instances by first calling the newInstance method on the Class. The container subsequently invokes the setSessionContext and the ejbCreate methods on the instances. At this point, the bean instances move to the method-ready pool or the method-ready state.

The method-ready pool is a mechanism by which the container pools Stateless Session Bean instances so that they're already created and available when the client invokes a business method on a bean instance. The container creates as many bean instances as specified in the deployment descriptor and populates them into the method-ready pool. When a client request arrives, the container picks up a free bean instance and hands it to the client. Thus, no time is spent creating the bean instance. Also, because initialization code of the bean is usually written in the ejbCreate method and that method has also been called already, the bean doesn't spend any time performing initializations. However, if a client request arrives and the container doesn't find any free bean instances, it creates a new instance.

This discussion gives rise to a very important point. We said earlier that a Stateless Session Bean could have only a single create method in its home interface—one that doesn't take any parameters. This is because the create method invocation on the home interface doesn't necessarily translate into an ejbCreate method invocation of the bean instance. For a bean, ejbCreate is invoked only once in its lifetime. The home interface's create method simply tells the container to provide the client with a bean instance, not necessarily to create one. A second reason is because of the fact that the bean is stateless. Any client-specific initialization you might do in the ejbCreate method will not be available when you invoke the business methods.

When a business method is invoked on this bean instance, the container moves the bean instance to a busy state. This bean instance isn't given out to other clients that might request one. The container always returns only idle bean instances to process client requests. After the business method invocation is finished, the instance is returned to the method-ready pool. WebLogic Server provides a mechanism by which you can limit the number of free instances in the free pool. This can be done by setting the parameter <max-beans-in-free-pool> (in weblogic-ejb.jar, under <weblogic-ejb-jar><weblogic-enterprise-bean><stateless-session-descriptor><pool>) to an appropriate value.

When the number of free bean instances exceeds this value, the container starts removing bean instances. Remember that when a client to a Stateless Session Bean invokes the remove method on an instance, the container doesn't necessarily remove the bean instance from memory. In fact, this method does nothing. The bean instance returns to the method-ready pool immediately after each method invocation. The container might decide to destroy the bean instance for other reasons, such as the setting of <max-beans-in-free-pool>, or other resource crunches. But destruction of a bean instance is never caused directly because a client calls remove on the bean.

When bean instances are removed, the ejbRemove method is invoked. This provides an opportunity to the bean to release any resources that it might be holding on, and to perform other cleanup tasks. After this method is invoked, the bean instance is de-referenced and made eligible for garbage collection. The bean instance ceases to exist; in other words, it transitions to the “does not exist” state.

    [ Team LiB ] Previous Section Next Section