6.13. Home InterfacesAn EJB Home Interface is a factory that creates and removes EJB objects in an EJB container. A Session Bean's create( ) method initializes a new Session Bean and returns a proxy object so the client can start using the EJB. An EJB Home is analogous to a POJO's constructor, except that the EJB Home's create method gives you a client-side proxy rather than a concrete object. If you use a Local EJB Home, then you're using a Local Interface to call its business methods. If you look up a Remote EJB Home, then you're working with a Remote Interface to access a Bean's business methods. 6.13.1. Local Home InterfaceThe Local Home Interface defines an EJB's lifecycle methodsthat create and remove bean instancesused by local, or co-located clients (Servlets, POJOs, or EJBs) that run inside the container. Our Local Home Interface, InventoryFacadeLocalHome, looks like Example 6-11. Example 6-11. InventoryFacadeLocalHome.javapackage com.jbossatwork.ejb; public interface InventoryFacadeLocalHome extends javax.ejb.EJBLocalHome { public static final String COMP_NAME="java:comp/env/ejb/InventoryFacadeLocal"; public static final String JNDI_NAME="InventoryFacadeLocal"; public com.jbossatwork.ejb.InventoryFacadeLocal create( ) throws javax.ejb.CreateException; } This is a Session Bean, so you only need a simple create( ) method with an empty argument list. 6.13.2. Remote Home InterfaceThe Remote Home Interface defines an EJB's lifecycle methodsthat create and remove bean instancesused by applications outside the container. Example 6-12 is our Remote Home Interface, InventoryFacadeRemoteHome. Example 6-12. InventoryFacadeRemoteHome.javapackage com.jbossatwork.ejb; public interface InventoryFacadeRemoteHome extends javax.ejb.EJBHome { public static final String COMP_NAME="java:comp/env/ejb/InventoryFacade"; public static final String JNDI_NAME="InventoryFacadeRemote"; public com.jbossatwork.ejb.InventoryFacadeRemote create( ) throws javax.ejb.CreateException,java.rmi.RemoteException; } 6.13.3. The Bean ClassThe Bean Class provides the implementation for a Session Bean's business methods. Our Bean Class, InventoryFacadeBean, looks like Example 6-13. Example 6-13. InventoryFacadeBean.javapackage com.jbossatwork.ejb; import java.util.*; import javax.ejb.*; import com.jbossatwork.dao.*; import com.jbossatwork.dto.CarDTO; public class InventoryFacadeBean implements SessionBean { private SessionContext sessionCtx; // EJB 2.1 mandated methods. public void setSessionContext(SessionContext sessionCtx) throws EJBException { this.sessionCtx = sessionCtx; } public void ejbCreate( ) throws CreateException { } public void ejbRemove( ) throws EJBException { } public void ejbActivate( ) throws EJBException { } public void ejbPassivate( ) throws EJBException { } // Business methods. public List listAvailableCars( ) throws EJBException { CarDAO carDAO = new HibernateCarDAO( ); return carDAO.filterByStatus(CarDTO.STATUS_AVAILABLE); } ... } Think of the InventoryFacadeBean as an encapsulation of all the inventory-related business functions. Each business method wraps all the activities to implement a User Story or Use Case. Consider each business method as a service that's available to clients, such as a web application, a GUI, or a Web Service client. The listAvailableCars( ) method tells the CarDAO to return only the cars that are still available (unsold). The DAO is devoid of any real business logicit's only responsible for CRUD operations on the CAR table in the database. In addition to business methods, we must implement the following callback methods because a Session Bean implements the javax.ejb.SessionBean interface:
The EJB container calls these methods during an EJB's lifecycle (from creation, invoking business methods, through removal). With the exception of setSessionContext( ), all these methods are usually empty for Stateless Session Beans. Even though we have only one real business method, we have to add five other methods (four of which are empty) just to comply with the EJB specification. These extra methods are inconvenient because they don't add any real value, but can't deploy our EJB if we don't have them. We've written the code for the InventoryFacadeBean, and now it's time to deploy the Bean. 6.13.4. EJB Deployment DescriptorsAfter developing a Session Bean's classes and interfaces, deploy it by adding information about the EJB (meta-data) to the J2EE standard (ejb-jar.xml) and JBoss (jboss.xml) EJB deployment descriptors . In ejb-jar.xml, the <enterprise-beans> element lists all EJBs in the application. The <session> element describes the bean by telling the container:
Example 6-14 is the ejb-jar.xml file. Example 6-14. ejb-jar.xml<?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> <display-name>InventoryFacadeSB</display-name> <ejb-name>InventoryFacade</ejb-name> <home>com.jbossatwork.ejb.InventoryFacadeRemoteHome</home> <remote>com.jbossatwork.ejb.InventoryFacadeRemote</remote> <local-home>com.jbossatwork.ejb.InventoryFacadeLocalHome</local-home> <local>com.jbossatwork.ejb.InventoryFacadeLocal</local> <ejb-class>com.jbossatwork.ejb.InventoryFacadeBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <resource-ref> <res-ref-name>hibernate/SessionFactory</res-ref-name> <res-type>org.hibernate.SessionFactory</res-type> <res-auth>Container</res-auth> </resource-ref> </session> ... </enterprise-beans> ... <assembly-descriptor> ... <container-transaction> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Local</method-intf> <method-name>listAvailableCars</method-name> <method-params> </method-params> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>InventoryFacade</ejb-name> <method-intf>Remote</method-intf> <method-name>listAvailableCars</method-name> <method-params> </method-params> </method> <trans-attribute>Required</trans-attribute> </container-transaction> ... </assembly-descriptor> </ejb-jar> The <resource-ref> elements specify the JNDI resources available to the InventoryFacadeBean (or any POJO that it calls). The <res-ref-name> is the JNDI name for the resourcejava:comp/env/hibernate/SessionFactory. Notice that you don't have to specify java:comp/env/it is the assumed prefix. The <res-type> for the hibernate/SessionFactory is a Hibernate Session Factory, so org.hibernate.SessionFactory is its fully qualified class name. We want JBoss to manage our resources, so we set <res-auth> to Container. The <assembly-descriptor> element describes security constraints, and transactions for all EJBs in the application. Each <container-transaction> element describes the transactional environment for every business method for each bean, for both the Remote and Local component interfaces. The read-only listAvailableCars( ) method wouldn't normally require a transaction, but we're using Hibernate 3 and are forced to set the transaction attributed to Required so that the method is guaranteed to run within a transaction. The "EJB Transaction Settings" section covers transactions, and later sections discuss Hibernate 3 and its relationship to EJBs and CMT. A JNDI resource is linked into an application only if we ask for it. JBoss binds resources under its in-JVM context, java:/. The jboss.xml file provides a mapping between the J2EE-style ENC names and the local JBoss-specific JNDI names that JBoss uses to deploy EJBs and any related JNDI-based resources. Example 6-15 is the jboss.xml descriptor. Example 6-15. jboss.xml<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd"> <jboss> ... <enterprise-beans> ... <session> <ejb-name>InventoryFacade</ejb-name> <jndi-name>InventoryFacadeRemote</jndi-name> <local-jndi-name>InventoryFacadeLocal</local-jndi-name> <resource-ref> <res-ref-name>hibernate/SessionFactory</res-ref-name> <jndi-name>java:/hibernate/SessionFactory</jndi-name> </resource-ref> <method-attributes> </method-attributes> </session> ... </enterprise-beans> ... <assembly-descriptor> </assembly-descriptor> ... <resource-managers> </resource-managers> </jboss> All EJBs described in jboss.xml must be defined in ejb-jar.xml. Here are the key elements:
The <resource-ref> element enables the InventoryFacadeBean (or any POJO that it calls) to look up resources using JNDI. As before, <res-ref-name> is the JNDI name for each resource for which java:comp/env/ is the assumed prefix. The textual value of each <res-ref-name> element in jboss.xml MUST match the value of the <res-ref-name> in ejb-jar.xml. The <jndi-name> is the local JBoss-specific JNDI name that JBoss uses to deploy a Hibernate Session Factory. Since the java:/ ENC is internal to JBoss, the java:/hibernate/SessionFactory JNDI name indicates that the Hibernate Session Factory is available only to applications running inside of JBoss. Now that we have our classes and interfaces in place, let's take a more detailed look at how EJBs relate to J2EE transactions. 6.13.5. EJB Transaction SettingsThe two ways to manage transactions in J2EE are through Container-Managed Transactions (CMT) and Bean-Managed Transactions (BMT). BMT requires extra Java coding to handle transaction logic and forces the developer to manage the transaction and define his own transaction boundaries. In contrast, CMT doesn't require handle transaction logic, pushes transaction settings into deployment descriptors, uses JBoss transaction services, and manages transaction boundaries on behalf of the developer. We'll focus on CMT because it is considered a J2EE best practice and it allows the container to do the work. A transaction attribute tells the container how to handle CMT transactions for an EJB's business methods. Specify transaction attributes for each Bean in the ejb-jar.xml deployment descriptor. You can set transaction attributes for an entire EJB or for each business methodmethod-level settings take precedence over bean-level settings. Here are the possible values for EJB transaction settings:
Despite the six possible values for EJB transaction settings, you'll use Required, RequiresNew, or Supports most of the time. 6.13.6. Difficulties Using EJBAt this point, you can deploy the example EJB on JBoss, but the overhead of creating at least one set of Home and Component Interfaces plus two XML deployment descriptors (ejb-jar.xml and jboss.xml, including transactional attributes) as well as the business code (the Bean Class) is tedious and burdensome. Ideally, we'd only have to write the Bean Class and somehow let deployment take care of itself. Even the Bean Class has issueswe don't care too much about the callback methods yet, but we're forced to implement them because they're part of the Session Bean interface.
It's important to know about the future, but we're still on EJB 2.1 because the specification and the JBoss implementation are complete and we know they'll work in a production environment. For now, we need XDoclet to generate the extra programming artifacts, and we just have to live with writing empty callback implementations in our Session Beans. The next section shows how XDoclet generates the bean's deployment descriptors and the Home and Component Interfaces, freeing you to concentrate on the EJB's business logic. 6.13.7. Automating Stateless Session Bean Deployment Using XDoclet TagsExample 6-16 shows the XDoclet tags in InventoryFacadeBean. Example 6-16. InventoryFacadeBean.java/** * @ejb.bean * name="InventoryFacade" * display-name="InventoryFacadeSB" * local-jndi-name="InventoryFacadeLocal" * jndi-name="InventoryFacadeRemote" * type="Stateless" * transaction-type="Container" * * @ejb.resource-ref * res-ref-name="hibernate/SessionFactory" * res-type="org.hibernate.SessionFactory" * res-auth="Container" * * @jboss.resource-ref * res-ref-name="hibernate/SessionFactory" * jndi-name="java:/hibernate/SessionFactory" * */ public class InventoryFacadeBean implements SessionBean { ... /** * @ejb.interface-method * */ public List listAvailableCars( ) throws EJBException { ... } ... } The class-level @ejb.bean XDoclet tag defines the EJB and generates the <session> element in ejb-jar.xml and jboss.xml. The class-level @ejb.resource-ref XDoclet tag generates the <resource-ref> element for the EJB's Hibernate Session Factory in ejb-jar.xml, and the @jboss.resource XDoclet tag generates the corresponding <resource-ref> element in jboss.xml. Each method-level @ejb.interface-method tag adds a business method to the Remote and Local component Interfaces. The XDoclet tags appear in class- and method-level Javadoc comments, and you need to run an external program to look at the Java code, interpret the tags, and generate code and deployment descriptors. The next section shows how to run the XDoclet code generator from Ant. 6.13.8. Ant Build Script Using XDocletAfter modifying the EJB to use XDoclet tags, we now use XDoclet's Ant tasks to generate the Home and Component Interfaces, along with the EJB deployment descriptors (ejb-jar.xml and jboss.xml). Example 6-17 shows a portion of the ejb sub-project's Ant build script that uses XDoclet to generate deployment artifacts. Example 6-17. ejb/build.xml... <target name="run-ejbdoclet" description="Generate EJB artifacts"> <taskdef name="ejbdoclet" classname="xdoclet.modules.ejb.EjbDocletTask" classpathref="xdoclet.lib.path"/> <mkdir dir="${gen.source.dir}" /> <ejbdoclet destdir="${gen.source.dir}" ejbspec="2.1"> <fileset dir="${source.dir}"> <include name="**/*Bean.java"/> </fileset> <remoteinterface pattern="{0}Remote"/> <localinterface pattern="{0}Local"/> <homeinterface pattern="{0}RemoteHome"/> <localhomeinterface pattern="{0}LocalHome"/> <deploymentdescriptor destdir="${gen.source.dir}"/> <jboss version="4.0" destdir="${gen.source.dir}"/> </ejbdoclet> </target> ... The run-ejbdoclet invokes XDoclet's <ejbdoclet> Ant task to generate the bean interfaces and deployment descriptors. Here are the important attributes and sub-elements of <ejbdoclet>:
Now that we have all of the EJB-related pieces in place, let's JAR everything up. 6.13.9. EJB JAR FileAn EJB JAR file is the standard deployment unit for the EJB component (EJBs, JMS Destinations, and so on) portion of a J2EE application. It contains ejb-jar.xml (the J2EE standard EJB deployment descriptor), jboss.xml (the JBoss-specific EJB deployment descriptor), the EJB classes, and a JAR manifest. We deploy the example EJB JAR file inside an EAR file alongside the WAR file containing the Controller Servlet that invokes the InventoryFacade EJB. See Figure 6-1 for the structure of the EJB JAR file. Figure 6-1. EJB JAR file structureThe EJB JAR file above has the following structure:
Now that we know the structure of the EJB JAR file, let's use Ant to create the JAR file. 6.13.10. Ant Task for Creating EJB JARExample 6-18 is another portion of the ejb sub-project's Ant build script that creates the EJB JAR file. Example 6-18. ejb/build.xml... <target name="build-ejb-jar" depends="run-ejbdoclet, compile" description="Packages the EJB files into a EJB JAR file"> <mkdir dir="${distribution.dir}" /> <jar destfile="${distribution.dir}/${ejb.jar.name}" basedir="${classes.dir}"> <metainf dir="${gen.source.dir}" includes="*.xml"/> </jar> </target> ... The <build-ejb-jar> target depends on the run-ejbdoclet (from the previous Ant code example) to generate all the EJB-related programming artifacts and the compile target to compile all the code. The <jar> task creates the EJB JAR file (ejb.jar) and copies the compiled Bean Class, along with the Home and Component Interfaces, into the JAR file (preserving the package directory structure). The <jar> task also copies the EJB deployment descriptors (ejb-jar.xml and jboss.xml) into the JAR's META-INF directory. So there's no real "magic" to creating an EJB JAR filethe <jar> task above looks similar to something you've seen before. The only real difference between a plain JAR file and the EJB JAR file is that you have deployment descriptors in the META-INF directory. Now that we have our EJB JAR, let's include it in our EAR. 6.13.11. Adding an EJB JAR to the EARWe've already covered the structure and contents of an EAR file in the Deployment chapter. Here are the steps to add the EJB JAR file to the EAR:
Example 6-19 is a portion of the application.xml file that now includes the new EJB JAR file, ejb.jar. Example 6-19. application.xml<?xml version="1.0" encoding="UTF-8"?> <application 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/application_1_4.xsd" version="1.4"> <display-name>JBossAtWorkEAR</display-name> <module> <web> <web-uri>webapp.war</web-uri> <context-root>ch06</context-root> </web> </module> ... <module> <ejb>ejb.jar</ejb> </module> ... </application> As we've seen in previous chapters, the <module> element defines a J2EE module (WAR, EJB JAR, and JAR) contained in the EAR. The <ejb> element specifies the EJB JAR file name relative to the EAR file's top-level directory. Adding the EJB JAR file to the EAR is trivial. Example 6-20 shows a portion of the main Ant build script (ch06-a/build.xml) that creates the EAR. Example 6-20. build.xml ... <ear destFile="${distribution.dir}/${ear.name}" appxml="${meta-inf.dir}/application.xml"> <fileset dir="${ejb.jar.dir}"/> <fileset dir="${webapp.war.dir}"/> <fileset dir="${common.jar.dir}"/> ... </ear> ... We've seen the <ear> task before. The only difference is that now we're copying the EJB JAR file into the EAR by adding a <fileset> task that includes the EJB JAR file. |