A Practitioner's Approach to Understanding Session BeansEven if you are new to Enterprise JavaBeans, you should easily understand the conceptual purpose and development benefits they provide in enabling client-side business logic to exist as very portable and loosely coupled components. These components exist within the context of a robust, highly available, and performance-oriented application infrastructure, such as WebLogic Platform. The aspect of EJBs that most people have difficulty understanding is the elements that constitute an EJB and how they exist and are carried out within the EJB container. This section of the chapter is dedicated to giving you a thorough understanding of a session bean's structural elements, explaining how and why they are developed and deployed in the manner they are, and describing how you can call methods on a session bean from a client. The following sections are a practitioner's approach to teaching you about session beans, so be prepared for some hands-on development. The journey ahead will be fast-paced, as shown in Figure 20.2. Figure 20.2. What you'll be learning in this section.
The following list expands on the items shown in Figure 20.2:
The Development Elements of a Session BeanAs shown in Figure 20.3, it's the bean developer's responsibility to supply the following class files that constitute a session bean: Figure 20.3. The elements of a session bean.
Note The Implementation class is also commonly referred to as the SessionBean class, as it implements all available methods in the session bean's remote or local interfaces. To develop a session bean, you provide the Implementation class file plus class files for the remote view or local view interfacesor even both. However, unless you are packaging the bean's client in the same JAR or EAR archive file as the session bean, you need to provide only the remote view interfaces for the bean. The remote view interfaces are required to enable a client to remotely access the bean via RMI/IIOP, which is the standard communication mechanism between distributed Java and J2EE objects, for example, Java clients calling methods on Java remote objects, such as EJBs. To learn more about RMI, see Chapter 12, "Distributed Processing Using RMI," p. 385 . Details on using local client view interfaces are discussed later in this chapter in "Using a Local View to Access a Session Bean." First, to get you better acquainted with the composition of a session bean, the following sections discuss the class files that constitute a simple stateless session bean. This example uses the infamous HelloWorld session bean, and you'll be creating the following class files:
To make sure this example is practical, you will also deploy the HelloWorld session bean to WebLogic Server and create a simple Java client to test the bean's operation. To set up your working environment for this session bean example, create a working directory on your file system (for example, one named Helloworld ), and then create the following subdirectories:
A build script, discussed later in "Building a Session Bean Deployment Unit," uses these subdirectories to compile and package the HelloWorld session bean. However, you can create your own specific working directory and subdirectories and modify the build script accordingly . The Remote InterfaceThe remote interface acts as a proxy for the session bean instance in the EJB container. The methods defined by the remote interface are literally reflections of the public methods declared in the Implementation class. Each business method defined in the remote interface must have an identical (or counterpart ) method in the Implementation class. The business methods defined by the remote interface are what the client calls to process tasks on the session bean instance. The class that implements a session bean's remote interface within the EJB container is known as the session EJBObject , which is generated at deployment. The EJBObject class implements the methods of the javax.ejb.EJBObject interface as well as methods specific to the Implementation class. Within the EJB container, it is the responsibility of EJBObject to delegate a client's method call to the session bean instance. The following rules must be considered when creating a remote interface for a session bean:
Listing 20.1 shows the remote interface for the HelloWorld session bean. Listing 20.1 Remote Interface for the HelloWorld Session Beanpackage examples; import java.rmi.*; import javax.ejb.*; /* The methods defined in this interface are the public interface of the * HelloWorld session bean. */ public interface HelloWorld extends EJBObject { /** * This method is identical to the one defined in the Implementation class of * the Session Bean, except this method must throw a java.rmi.RemoteException. */ public String getHelloWorldMsg () throws RemoteException; } Create a Java class file using Listing 20.1, and save the file as HelloWorld.java in your working /src directory. Tip In a real-world scenario, you could start by creating the remote interface to define the methods required for implementation in the Implementation class. Alternatively, you could also define methods in the Implementation class first and then declare them via the remote interface. Either way, the main objective is that they are identical. The Home InterfaceThe home interface acts as a factory for creating session bean instances and, therefore, exports the methods that provide life cycle services for the bean, such as creation and destruction of the bean. The home interface does not implement any business logic on behalf of the bean or possess any state. The class that implements a session bean's home interface within the EJB container is known as the session EJBHome , which is generated at deployment. The EJBHome class implements the methods of the javax.ejb.EJBHome interface as well as the create< METHOD > methods, which call a matching ejbCreate< METHOD > method in the Implementation class. The EJB container makes the session bean's home interface available to a client through JNDI. The following rules must be considered when creating a home interface for a session bean:
Listing 20.2 shows the home interface for the HelloWorld session bean. Listing 20.2 The Home Interface for the HelloWorld Session Beanpackage examples; import java.rmi.*; import javax.ejb.*; /* Defines the methods for creating an instance of the HelloWorld session /* bean */ public interface HelloWorldHome extends EJBHome { /** * This create() method corresponds to the ejbCreate() method in the * Implementation class of the HelloWorld session bean. * The create() method returns the remote interface of the HelloWorld * session bean. */ public HelloWorld create() throws CreateException,RemoteException; } Create a Java class file using Listing 20.2, and save the file as HelloWorldHome.java in your working /src directory. The Implementation ClassAs a bean developer, you implement all business logic for a session bean within the Implementation class, which is defined by the remote interface. All Implementation classes must implement the javax.ejb.SessionBean interface. The EJB container uses the setSessionContext() method of the SessionBean interface to associate a session bean instance with its context. The following rules must be considered when creating the Implementation class for a session bean:
Note The SessionBean class is allowed to implement other methods (for example, helper methods called internally by business methods) in addition to methods the EJB specification requires.
The following rules must be considered when creating ejbCreate() methods for the Implementation class:
Listing 20.3 shows the Implementation class for the HelloWorld session bean. Listing 20.3 The Implementation Class for the HelloWorld Session Beanpackage examples; import javax.ejb.*; /** The Implementation class for the HelloWorld session bean */ public class HelloWorldBean implements SessionBean { /** * The session context is provided by the EJB container. A session bean must * retain its context. . */ private SessionContext ctx; /** An EJB must have a public, parameterless constructor */ public HelloWorldBean() {} /** Implement a simple logging mechanism to stdout */ private void log(String s) { if (true) System.out.println(s); } /** * This method is required by the EJB specification, * but is not used in this example. The container uses this method * to activate a stateful session bean after the bean has been * passivated using the ejbPassivate() method. */ public void ejbActivate() {} /** * This method is required by the EJB specification, * but is not used in this example. The container uses this method * to passivate an active stateful session bean. */ public void ejbPassivate() {} /** * This method is called by the EJB container to remove the * session bean from the EJB container. */ public void ejbRemove() { log("ejbRemove() called"); } /** Called by the EJB container to set the bean's session context. */ public void setSessionContext(SessionContext ctx) { log("setSessionContext called"); this.ctx = ctx; } /** * Called by the EJB container when a client calls the create() method in * the home interface. This method corresponds to the create() method * in the home interface of the HelloWorld session bean (HelloWorldHome.java). */ public void ejbCreate() throws CreateException { log("ejbCreate() called"); } public String getHelloWorldMsg () { log("Bean Method called"); return ("Stateless SessionBean says Hello World"); } } Create a Java class file using Listing 20.3, and save the file as HelloWorldBean.java in your working /src directory. The Stubs and Skeletons of Session Bean InterfacesWhen you deploy your session bean, method calls to the bean are never handled directly by the remote or home interfaces. Instead, your remote method call to the bean is performed through the collaboration of two EJB container-generated classes called the stub and the skeleton for each interface you implement. Stubs are client-side objects, and skeletons are server-side objects. These classes are responsible for marshaling parameters and return values across the network connection, as shown in Figure 20.4. Figure 20.4. The roles of a stub and skeleton in remote communication.
Note The only restriction for remote methods is that all parameters and return values must be serializable. A stub is basically a proxy of the remote object and has the same interface as the remote object. The stub is also located on the same machine as a calling client, which enables the client to use its methods in the same manner that it would use methods on the remote object directly. The stub is responsible for marshaling method calls into network messages to a target skeleton object. Note Marshal means to serialize parameters so that they can be written to the remote object, which in turn deserializes them back into objects. Marshaling is built into the Java language through the Serializable interface. The skeleton is an object that deconstructs a network message into its method call. After deconstructing a message, the skeleton handles the logic for calling an equivalent method on the remote object that runs on the same machine as the skeleton. The skeleton also takes the return value of the method call and converts it to a network message that can be sent back to the stub. Because a session bean implements two interfaces (remote and home), all remote method calls to a session bean are handled through two sets of stubs and skeletons:
These stubs and skeletons are auto-generated and implemented by the EJB container when the session bean is deployed to WebLogic Server. These home and remote stubs and skeletons work in concert to enable a client to remotely call a method on the session bean (see Figure 20.5). Figure 20.5. The roles of home and remote stubs and skeletons.
The following list explains the numbered callouts in Figure 20.5:
Using a Local View to Access a Session BeanThe primary difference between local and remote views of a bean is that a local view is optimized for accessing a bean by bypassing the need to use RMI for communicating with the bean. Because RMI is not used for communication, a local client accesses a session bean through the bean's local interface or accesses an entity bean through the bean's local home interface. The container provides classes that implement the bean's local and local home interfaces, as follows :
Table 20.2 explores some additional differences between local and remote views to provide guidance on which view you should use to implement a session bean. Table 20.2. Differences Between Local and Remote Views of Session Beans
The local and local home interfaces for the HelloWorld session bean are provided in Listings 20.4 and Listing 20.5. Listing 20.4 The Local Interface for the HelloWorld Session Beanpackage examples; import javax.ejb.*; /** * This is the HelloWorld bean's local interface. */ public interface HelloWorldLocal extends EJBLocalObject { /** * This method is identical to the one defined in the remote interface of the * session bean, except this method does not throw a java.rmi.RemoteException. */ public String getHelloWorldMsg(); } Listing 20.5 The Local Home Interface for the HelloWorld Session Beanpackage examples; import javax.ejb.*; /** * This is the local home interface for the HelloWorld bean. */ public interface HelloWorldLocalHome extends EJBLocalHome { /** * This create() method corresponds to the ejbCreate() method in the * Implementation class of the HelloWorld session bean. * The create() method returns the local interface.of the HelloWorld * session bean. As you can see, remote exceptions are not applicable for * local home interfaces. */ HelloWorldLocal create() throws CreateException; } Developing a Test Java Client for Your Session BeanUnit testing your J2EE components after they're deployed is always the best practice, and this simple session bean is no exception. Because the HelloWorld session bean has only one business method, getHelloWorldMsg() , a simple Java client is required to test this method's operation. Listing 20.6 shows the code for this Java client, and it has been fully commented to explain the processes of locating the home and remote interfaces and implementing the session bean's method. Listing 20.6 The Test Java Client for the HelloWorld Session Beanpackage examples; import java.rmi.*; import javax.rmi.PortableRemoteObject; import javax.naming.*; import java.util.*; /** * This class is an example of client code that calls * methods on a simple stateless session bean. */ public class HelloWorldClient { private static Context createJNDIContext() throws NamingException, RemoteException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); env.put(Context.PROVIDER_URL,"t3://localhost:7001"); Context context = new InitialContext(env); return context; } /** * Main method to unit test the HelloWorld API */ public static void main(String[] args) { try { // use JNDI to look up the home interface for HelloWorld Context context = createJNDIContext(); // You could use the following statement to retrieve // the HelloWorld home /* HelloWorldHome home = (HelloWorldHome) context.lookup("examples.HelloWorldEJB"); * / // However, using javax.rmi.PortableRemoteObject // allows you to narrow the scope // to the home interface HelloWorldHome home = (HelloWorldHome) PortableRemoteObject.narrow(context.lookup("examples.HelloWorldEJB"), examples.HelloWorldHome.class) ; HelloWorld hello = home.create(); /* * The EJBObject will delegate the call to the HelloWorld * session bean, receive the result, and return it to this client. */ System.out.println(hello.getHelloWorldMsg()); /* * Remove the EJBObject. * The container will mark the EJBObject for destruction. */ hello.remove(); } catch(Exception err) { System.err.println(err.toString()); } } } Developing Deployment DescriptorsTwo deployment descriptors are applicable to your session bean example:
Note The weblogic-cmp-rdbms-jar.xml file is applicable only if you are using Container-Managed Persistence. The following sections describe these deployment descriptors in the context of the session bean example. The ejb-jar.xml FileListing 20.7 provides the ejb-jar.xml deployment descriptor file for the HelloWorld session bean example. Listing 20.7 The ejb-jar.xml File for the HelloWorld Session Bean Example<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'> <!-- Generated XML! --> <ejb-jar> <enterprise-beans> <session> <ejb-name>HelloWorldEJB</ejb-name> <home>examples.HelloWorldHome</home> <remote>examples.HelloWorld</remote> <local-home>examples.HelloWorldLocalHome</local-home> <local>examples.HelloWorldLocal</local> <ejb-class>examples.HelloWorldBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> <assembly-descriptor> <container-transaction> <method> <ejb-name>HelloWorldEJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </ assembly-descriptor > </ejb-jar> Table 20.3 describes the primary tag elements in Listing 20.7's deployment descriptor that you should become familiar with. Table 20.3. Tag Elements in the ejb-jar.xml File for the Session Bean Example
Create your ejb-jar.xml file using Listing 20.7, and save the file in your working /deployment directory. The weblogic-ejb-jar.xml FileListing 20.8 provides the weblogic-ejb-jar.xml deployment descriptor file for the HelloWorld session bean example. Listing 20.8 The weblogic-ejb-jar.xml File for the HelloWorld Session Bean Example<!DOCTYPE weblogic-ejb-jar PUBLIC '-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN' 'http://www.bea.com/servers/wls700/dtd/weblogic-ejb-jar.dtd'> <!-- Generated XML! --> <weblogic-ejb-jar> <weblogic-enterprise-bean> <ejb-name>HelloWorldEJB</ejb-name> <stateless-session-descriptor> <pool> <initial-beans-in-free-pool>5</initial-beans-in-free-pool> </pool> <stateless-clustering> <home-is-clusterable>False</home-is-clusterable> <stateless-bean-is-clusterable>False</stateless-bean-is-clusterable> <stateless-bean-load-algorithm>RoundRobin</stateless-bean-load-algorithm> </stateless-clustering> </stateless-session-descriptor> <transaction-descriptor> </transaction-descriptor> <enable-call-by-reference>True</enable-call-by-reference> <jndi-name>examples.HelloWorldEJB</jndi-name> <local-jndi-name>examples.HelloWorldEJBLocal</local-jndi-name> </weblogic-enterprise-bean> </weblogic-ejb-jar> Table 20.4 describes the primary tag elements in Listing 20.8's deployment descriptor that you should become familiar with. Table 20.4. Tag Elements in the weblogic-ejb-jar.xml File for the Session Bean Example
Create your weblogic-ejb-jar.xml file from Listing 20.8 using a text editor or an XML editor, and save the file in your working /deployment directory. You can also use WebLogic Builder to create and validate your deployment descriptors. Building a Session Bean Deployment UnitNow that you have created your HelloWorld session bean class files and deployment descriptors, the next step is to build your deployment unit (JAR file), which requires you to do the following:
This task is performed with the weblogic.ejbc utility, which generates container classes and implementation classes by inspecting the bean's Implementation class file and remote and home interfaces and by examining the session bean's deployment descriptors (see Figure 20.6). Figure 20.6. Use weblogic.ejbc to generate the WebLogic container classes.
To perform these tasks, it is a good practice to create a build/make script that can automate the whole build process for you. The build script ( build.cmd ) for compiling, packaging, and generating the container classes is shown in Listing 20.9. Listing 20.9 An Example of a Build Script@rem ******************************************************* @rem Cleaning working area @rem ******************************************************* echo y rmdir /s classes echo y rmdir /s lib mkdir classes mkdir lib @rem ******************************************************* @rem Compiling the session bean files @rem ******************************************************* javac -d classes src\*.java @rem ******************************************************* @rem Copy deployment descriptors into the META-INF directory @rem ******************************************************* mkdir classes\META-INF copy deployment\*.xml classes\META-INF @rem ******************************************************* @rem Create the temporary .jar file @rem ******************************************************* cd classes jar cf ..\lib\temp.jar * cd .. @rem ******************************************************* @rem Generating the container classes and creating the deployment @rem unit @rem ******************************************************* cd lib java weblogic.ejbc -keepgenerated temp.jar HelloWorld.jar del temp.jar cd .. Tip You can view the contents of the generated container class in HelloWorld.jar by using a decompression utility, such as WinZip. Deploying Your Session BeanYou can use two techniques to deploy your HelloWorld session bean. The easiest is simply copying the HelloWorld.jar file into the applications directory within your WebLogic Server domain directory. If your WebLogic Server is started in development mode, which is the default, WebLogic Server auto-deploys the JAR file. The other deployment technique is a more formal procedure using WebLogic Builder or the Administration Console. Follow these steps to deploy your HelloWorld.jar file to WebLogic Server using the Administration Console:
After your session bean has been deployed to WebLogic Server, the EJB container creates five instances of the bean in the free pool, as specified by the <initial-beans-in-free-pool> element in weblogic-ejb-jar.xml . Because logging was enabled in each method of the HelloWorld bean (see Listing 20.6), the methods called to instantiate the bean into the free pool are displayed in WebLogic Server's stdout . To test your HelloWorld session bean, follow these steps:
|