A StrutsEJB Sample Application


A Struts/EJB Sample Application

Now it's time to move from theory to practice. This sample application is intended to illustrate a collection of what we consider to be best practices in regard to using Struts with EJBs.

It's our hope that you can take this core application and use it as a base for building actual, real Struts/EJB applications. We've used these basic principles in numerous projects and are confident they'll work for you.

This application is developed using Tomcat as our servlet/JSP container and JBoss 3.0.3 as a container for the EJBs. Basic configuration information is provided directly in the chapter as the need for it arises.

Due to differences between EJB containers (or even between different releases of JBoss), any configuration information provided here might differ slightly (or dramatically!) from your specific situation. In that case, refer to the documentation for the application servers you're using.

The Application: Updating Customer Information

In this basic application, a simple form is provided to allow information about a customer to be updated. Like any Struts form, the information is populated into a form bean when it's submitted. The form bean is then passed to an Action class for processing. After processing, an ActionForward is called to indicate which JSP file to display the results with.

This processing is all very basic and should be ingrained in your memory by now. But there's a twist this time: The customer information is managed using an EJB inside an EJB container (JBoss, in this case). The Action class must update the customer information by invoking methods on the EJB that manages the customer information.

Figure 18.1 shows the form used to enter the information.

Figure 18.1. The EJB sample application ” /strutsEJB/index.jsp .

graphics/18fig01.gif

When you submit the form, the address of the customer changes. That's all. No magic, smoke, or mirrors. The EJB that generates this new address simply chooses randomly between three hard-coded addresses and returns one.

The purpose of this application isn't to be fancy; it's to provide a working application that demonstrates good design fundamentals that you can copy and reuse.

In covering this sample application, you'll first go over configuring the system and building the application. Following that, you'll go through the application code itself.

Because a big part of using EJBs with Struts has to do with configuration of the Web container and the EJB container, we feel this is appropriate material to include in this book. If you've already got a working configuration, please skip the following sections.

Also, be forewarned that although we've done our best to make this accurate, changes in JBoss and/or Tomcat could render parts of this section obsolete. Even if this is this case, the overall approach should still be instructive. To ensure that this sample application goes as smoothly as possible, we recommend using the particular versions of JBoss, Tomcat and XDoclet that are included on the companion CD-ROM that came with this book. These particular versions have been thoroughly tested with the sample application.

Configuring and Building the Application

The following sections lay out the specifics of how to configure the Web container (Tomcat) and the EJB container (JBoss) to work together and how to build the application. If you're using different Web or EJB containers, the basic steps might vary somewhat but should be basically the same.

Note

It's important that you follow the steps in this section closely. If you run into problems, the best thing to do is to back up a step and make sure that you did everything correctly. We worked hard to make this process as clear as possible!


Installing XDoclet

XDoclet is required for building the sample application. If you're familiar with JBoss, you've probably already used XDoclet; it's very common to use XDoclet for developing EJBs with JBoss.

XDoclet is included on the companion CD-ROM for this book. To install it, simply extract the xdoclet-1.1.2.zip file into a convenient directory. In a later section, you are asked to enter the path to this directory in a properties file ( .ant.properties ) that's used by the build script for the application.

The EJB Container: JBoss 3.0.3

JBoss (available free at http://www.jboss.org) is by almost all measures the most widely used J2EE application server in the world. It won the JavaWorld 2002 Editor's Choice as Best Java Application Server (BEA's WebLogic Server and IBM's WebSphere finished just behind it). JBoss 3.0.3, the version used during development of this book, is contained on the companion CD-ROM for this book.

Note

Note that the companion CD-ROM for this book contains the Sun JDK version 1.4. However, at the time of this writing, there are a number of known issues running JBoss 3.0.3 with this version of JDK. As a result, I recommend that you use JDK 1.3.1 with the JBoss version 3.0.3 that comes with this book. This is the JDK version that the sample application was developed and tested with.


Installing JBoss

Installing JBoss is extremely simple. You can simply extract it from the companion CD-ROM into a convenient directory. (During development of the book, I used c:\dev\jboss .) From here to the end of the chapter, this directory is referred to as JBOSS_HOME . After this step is complete, start JBoss by changing into the directory JBOSS_HOME/bin and typing run. You must make sure that you've defined the environment variable JAVA_HOME to point to the top directory of the Java SDK you're using.

You should now see startup messages indicating that JBoss is starting all its services.

Moving the JBoss Web Container (Jetty) Out of the Way

JBoss comes with an embedded servlet/JSP engine called Jetty. Because the sample application (like all applications in this book) is run under Jakarta Tomcat, Jetty isn't needed. The fact that it's there isn't a problem; it can be safely ignored. However, if you're running both Tomcat and Jetty, you should know that they have the same default port (port 8080). This will cause problems, so it must be fixed.

How to fix this problem depends on the exact release of JBoss you have. What you must do is find the jboss-service.xml file for the Jetty service and change the jetty.port property from its default value of 8080 to some other value (for example, 8081). The tricky part is finding the Jetty service.

All services installed in your JBoss server are, by default, located in the directory JBOSS_HOME/server/default/deploy . To deploy a new application in JBoss, you just copy the application file ( .war , .jar , .ear , and so on) into this directory. JBoss immediately finds and installs it. The only question is this: Which file (or subdirectory) is the Jetty service defined in?

In JBoss 3.0.3, the jboss-service.xml file that controls the Jetty service is located in the JBOSS_HOME\server\default\deploy\jbossweb.sar\META-INF directory. In a different release, it might be located somewhere else. Look around; there aren't that many default components and you should be able to find Jetty with a minimal amount of effort. After you locate this file, edit it and look for the line

 <SystemProperty name="jetty.port" default="8080"/> 

To move Jetty to a different port, simply change this port to some other number (for example, "8081" ) and then close and save the file. JBoss automatically redeploys the Jetty service to listen on the new port. You should now be able to run Tomcat on its default 8080 port with no problem.

Note

Depending on your version of JBoss and your comfort level working with it, there are a number of other ways to resolve this issue. For example, you might find it easier to simply remove the Jetty service or you may even deploy your Struts application into the Jetty service instead of Tomcat. This approach chosen in this chapter was taken because it is straightforward and easy to explain and understand.

One problem with simply moving Jetty to another port is that Jetty also has a listener for the Apache Jserv Protocol (AJP). This is on port 8009 by default for both Jetty and Tomcat, so there's a conflict for this protocol similar to the conflict with the HTTP protocol on the 8080 port. This isn't a problem if all you're doing is installing JBoss to run this sample application. However, if you're installing and running this application in an existing environment, you should know that this could cause problems for you.

If you believe this might cause a problem, then you can disable this protocol or move it to a different port number by modifying its entry in the same jboss-service.xml file that you modified earlier to change the default Jetty port (8080). Simply locate the entry for the AJP protocol and move it to a new port, or comment it out to keep it from running at all.


Installing the Sample Application

The sample application for this chapter is provided on the companion CD-ROM for this book. It is contained in the strutsEJB.zip file. To install it, simply extract it into a directory where you can work on it. For the development of this application, we put it directly in the JBOSS_HOME directory (at the location JBOSS_HOME/strutsEJB ).

For the rest of the chapter, directories that are part of the build process are referred to using their parameter names as defined in the Ant build.xml file that's provided with the sample application.

After the sample application is installed, you can move to the next step.

Note

Although most of the following steps will have been already completed in the sample application, this chapter proceeds as if they haven't. This is done so that you can understand how the application was built. Please follow all the steps closely!


Configuring Classpaths and Libraries

For your Struts application to be able to communicate with the JBoss server, a number of libraries ( .jar files) are required by the Struts application. This section tells you which .jar files you need, where to find them, and where to put them in the Struts application to make things work.

In the strutsEJB application you just installed, there's a lib directory just under the main strutsEJB directory. This directory is referred to in the build.xml file as the struts.libs.dir and it's defined by a property in the .ant.properties file.

In this directory there must be two sets of files: all the files required for Struts, and all the files required for communications with JBoss. The Struts libraries are all the JAR files that came with your Struts distribution. The files required for JBoss communication are

  • jboss.jar

  • jboss-common-client.jar

  • jboss-j2ee.jar

  • jbosssx -client.jar

  • jnet.jar

  • log4j.jar

You can copy all these files from the JBOSS_HOME/client directory except for the jboss.jar file. The jboss.jar file is located in the directory JBOSS_HOME/server/default/lib .

After all these library files are copied into the strutsEJB/lib directory, you're ready to proceed to the next step and build the application.

Building the Application

This application was adapted from the default application template provided with JBoss. This was done intentionally in order to be useful to those familiar with JBoss. If your background is with some other EJB container, this build process should still be useful, but will probably require modifications to work in your environment.

You should now have JBoss and the sample application installed on your machine, and all the library files should be in place in the strutsEJB/lib directory.

Note

This section makes the assumption that you have a basic understanding of the Jakarta Ant utility and at least some experience in its use. If this isn't the case, we recommend that you become familiar with Ant before you proceed. Jakarta Ant is available at http://jakarta.apache.org/ant as well as on the companion CD-ROM for this book.


Because the application is built using the Jakarta Ant utility, a build.xml file is provided. The build process is customized to your specific environment by modifying parameters in the .ant.properties file that's also provided. These files are versions of the standard JBoss application build files that have been modified to work with Struts.

The build.xml file won't be reviewed in detail here, but the .ant.properties file is shown in Listing 18.1.

Listing 18.1 Struts and JBoss Properties for the Sample Application ( .ant.properties )
 # This file has been modified from the original sample provided with JBoss. # # Some parameters have been left alone and are unchanged from the # original example template. Others have been added or modified from # the original file. # # Entries in this file may override properties set in the build.xml file. # # The following properties are unchanged. These properties may not be # used, but there is no reason to delete them unless you have a need to. jboss.configuration=default xdoclet.force=false ejb.version=2.0 jboss.version=3.0 type.mapping=Hypersonic SQL datasource.name=java:/DefaultDS # The following properties have been added or changed from the original # example provided with JBoss. # # THESE MUST BE MODIFIED FOR YOUR ENVIRONMENT OR THE APP WILL NOT WORK! # # A property identifying the path to the jboss-j2ee.jar file servlet-lib.path=C:\dev\jboss\server\default\lib\jboss-j2ee.jar # The top directory of your Tomcat installation tomcat.home=c:\dev\tomcat # The directory containing struts.jar and other struts libs struts.libs.dir=c:\dev\jboss\strutsEJB\lib # The Ant home directory ant.home=c:\dev\ant # The JBoss home directory. Used to locate libs and for deployment jboss.home=C:\dev\jboss # The Xdoclet home directory. Needed to use XDoclet. xdoclet.home=C:\dev\xdoclet 

It's very important that you modify this file and set all the properties to reflect your environment configuration. If you find yourself running into trouble getting the application to work, make sure that you review this file in detail to confirm that all the settings are correct.

Most of the properties in the .ant.properties file are used in the JBoss build process. The only two properties that have been added to modify it for use with Struts are

  • tomcat.home ” The home directory for your Tomcat installation.

  • struts.libs.dir ” The directory containing all the Struts .jar files and the JBoss .jar files required for communications between Struts and JBoss. This was covered in the previous section.

After the build properties are configured properly, proceed with building the application. The result of the build is two archives:

  • strutsEJB.jar ” A JAR file suitable for deployment in JBoss.

  • strutsEJB.war ” A WAR file suitable for deployment in Tomcat.

Running the build is done by simply typing the command ant in the strutsEJB main directory. A successful build will have output similar to Listing 18.2.

Listing 18.2 Output from Running the ant Command to Build the strutsEJB Sample Application
[View full width]
 C:\dev\jboss\strutsEJB>ant Buildfile: build.xml check-environment: check-jboss: wrong-jboss: check-xdoclet: wrong-xdoclet: init:      [echo] build.compiler = ${build.compiler}      [echo] user.home = C:\Documents and Settings\a      [echo] java.home = C:\jdk1.3.1_01\jre      [echo] ant.home = c:\dev\ant      [echo] jboss.home = C:\dev\jboss      [echo] xdoclet.home = C:\dev\xdoclet      [echo] jboss.client = C:\dev\jboss/client      [echo] tomcat.home = c:\dev\tomcat xdoclet-generate:      [ejbdoclet] Generating Javadoc      [ejbdoclet] Javadoc execution      [ejbdoclet] Loading source file C:\dev\jboss\strutsEJB\src\main\jboss\ch18\session\ graphics/ccc.gif CustomerSessionBean.java      [ejbdoclet] Constructing Javadoc information...      [ejbdoclet] Running <homeInterface/>      [ejbdoclet]   Generating Home interface               for 'jboss.ch18.session.CustomerSessionBean'.      [ejbdoclet] Running <remoteInterface/>      [ejbdoclet]   Generating Remote interface for 'jboss.ch18.session. graphics/ccc.gif CustomerSessionBean'.            [ejbdoclet] Running <dataobject/>      [ejbdoclet] Running <deploymentDescriptor/>      [ejbdoclet]   Generating EJB deployment descriptor.      [ejbdoclet] Running <jboss/>      [ejbdoclet]   Generating jboss.xml. compile: compile:      [echo] Compilation Path = C:\dev\ant\lib\ant.jar;C:\dev\xdoclet\lib\xdoclet.jar;C:\ graphics/ccc.gif dev\jboss\client\log4j.jar;C:\dev\jboss\strutsEJB\lib\commons-beanutils.jar;C:\dev\jboss\ graphics/ccc.gif strutsEJB\lib\commons-collections.jar;C:\dev\jboss\strutsEJB\lib\commons-dbcp.jar;C:\dev\ graphics/ccc.gif jboss\strutsEJB\lib\commons-digester.jar;C:\dev\jboss\strutsEJB\lib\commons-fileupload. graphics/ccc.gif jar;C:\dev\jboss\strutsEJB\lib\commons-lang.jar;C:\dev\jboss\strutsEJB\lib\ graphics/ccc.gif commons-logging.jar;C:\dev\jboss\strutsEJB\lib\commons-pool.jar;C:\dev\jboss\strutsEJB\ graphics/ccc.gif lib\commons-resources.jar;C:\dev\jboss\strutsEJB\lib\commons-services.jar;C:\dev\jboss\ graphics/ccc.gif strutsEJB\lib\commons-validator.jar;C:\dev\jboss\strutsEJB\lib\jakarta-oro.jar;C:\dev\ graphics/ccc.gif jboss\strutsEJB\lib\jdbc2_0-stdext.jar;C:\dev\jboss\strutsEJB\lib\struts.jar;C:\dev\jboss\ graphics/ccc.gif lib\concurrent.jar;C:\dev\jboss\lib\crimson.jar;C:\dev\jboss\lib\getopt.jar;C:\dev\jboss\ graphics/ccc.gif lib\gnu-regexp.jar;C:\dev\jboss\lib\jaxp.jar;C:\dev\jboss\lib\jboss-boot.jar;C:\dev\jboss\ graphics/ccc.gif lib\jboss-common.jar;C:\dev\jboss\lib\jboss-jmx.jar;C:\dev\jboss\lib\jboss-system.jar;C:\ graphics/ccc.gif dev\jboss\lib\log4j-boot.jar;C:\dev\jboss\server\default\lib\javax.servlet.jar;C:\dev\ graphics/ccc.gif jboss\server\default\lib\jboss-j2ee.jar;C:\dev\ant\lib\optional.jar;C:\dev\ant\lib\ graphics/ccc.gif xercesImpl.jar;C:\dev\ant\lib\xml-apis.jar;C:\dev\jboss\strutsEJB\build\classes     [mkdir] Created dir: C:\dev\jboss\strutsEJB\build\classes     [javac] Compiling 13 source files to C:\dev\jboss\strutsEJB\build\classes jboss-jar:     [mkdir] Created dir: C:\dev\jboss\strutsEJB\build\classes     [javac] Compiling 13 source files to C:\dev\jboss\strutsEJB\build\classes jboss-jar:     [mkdir] Created dir: C:\dev\jboss\strutsEJB\build\jbossdeploy       [jar] Building jar: C:\dev\jboss\strutsEJB\build\jbossdeploy\strutsEJB.jar struts-war:      [copy] Copying 1 file to C:\dev\jboss\strutsEJB\build\classes     [mkdir] Created dir: C:\dev\jboss\strutsEJB\build\strutsdeploy       [war] Building war: C:\dev\jboss\strutsEJB\build\strutsdeploy\strutsEJB.war       [war] Warning: selected war files include a WEB-INF/web.xml which will be ignored ( graphics/ccc.gif please use webxml attribute to war task)       [war] Warning: selected war files include a WEB-INF/web.xml which will be ignored ( graphics/ccc.gif please use webxml attribute to war task) deploy-server:      [copy] Copying 1 file to C:\dev\jboss\server\default\deploy      [copy] Copying 1 file to C:\dev\tomcat\webapps BUILD SUCCESSFUL Total time: 19 seconds 

Note

The preceding build was run using a freshly installed copy of Jakarta Ant version 1.5.1 with no additional libraries added. If you have problems building the application, try using the version of Ant that's included on the companion CD-ROM with this book.


Now that the archive files for both JBoss and Tomcat have been built and deployed, you should be able start your JBoss and Tomcat servers and see the application run! To run the application, simply start JBoss and Tomcat and point your browser to http://localhost:8080/strutsEJB/Customer.do .

Reviewing the Sample Application Source Files

This section of the chapter is dedicated to reviewing the sample application code. The goal of this section is to provide you with an understanding of how to use Struts to interact with an EJB container. This design isn't the only one possible, but it's one we consider to be a best practice.

The Value Object: CustomerValueObject.java

A good place to begin with is with the value object. As discussed earlier, a value object is nothing more than a simple bean used to pass data between the Web container and the EJB container. Because a lot of effort is spent encoding and decoding the data (as well as coordinating the transfer) each time data is passed between the two containers, trying to minimize this overhead is good.

The sample application minimizes the overhead related to sending/receiving data by sending the data one bean at a time rather than one property at a time.

CustomerValueObject.java is the value object used in this sample application. Its source code is shown in Listing 18.3.

Listing 18.3 The CustomerValueObject.java Source Listing
 package struts.ch18.client; import jboss.ch18.interfaces.AbstractData; /**  * Example Value Object for showing integration of Struts with EJBs  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerValueObject extends AbstractData {   public CustomerValueObject () {       this.name     = "";       this.custId   = "";       this.address  = "";       this.city     = "";       this.state    = "";       this.zip      = "";   }   public CustomerValueObject (String name,                               String custId,                               String address,                               String city,                               String state,                               String zip                              ) {       this.name     = name;       this.custId   = custId;       this.address  = address;       this.city     = city;       this.state    = state;       this.zip      = zip;   }   private String name;   public String getName() { return this.name; }   public void setName(String nameus) { this.name = name; }   private String custId;   public String getCustId() { return this.custId; }   public void setCustId(String custId) { this.custId = custId; }   private String address;   public String getAddress() { return this.address; }   public void setAddress(String address) { this.address = address; }   private String city;   public String getCity() { return this.city; }   public void setCity(String city) { this.city = city; }   private String state;   public String getState() { return this.state; }   public void setState(String state) { this.state = state; }   private String zip;   public String getZip() { return this.zip; }   public void setZip(String zip) { this.zip = zip; } } 

As you can see from this listing there's nothing fancy about value objects. They simply hold properties for communications between containers.

You might have noticed that this class extends the class AbstractData . AbstractData is an abstract class defined in the template application provided with JBoss. It doesn't require JBoss to be used and its source code is provided. AbstractData is simply a class recommended by the JBoss developers for use with value objects.

The Form Bean: CustomerForm.java

The form bean used with the sample application is similar to the other form beans you've used throughout this book. The only difference is that support has been added to enable it to work easily with value objects.

This added support takes the form of adding set/get methods that accept and return CustomerValueObject s. You'll see how this simplifies things a bit later when you review the form bean.

When the Model portion of your application uses EJBs, one of the primary functions of the Action class is to manage communications with the EJB container. Designing your form bean to make it easy for the Action class to interact with it using value objects is a best practice that you'll want to follow.

CustomerForm.java is the form bean used in this sample application. Its source code is shown in Listing 18.4.

Listing 18.4 The CustomerForm.java Source Listing
 package struts.ch18.client; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import javax.servlet.http.HttpServletRequest; import struts.ch18.client.CustomerValueObject; /**  * Form bean class to demonstrate EJB Integration with Struts  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerForm extends ActionForm {   // Default bean constructor   public CustomerForm() { }   private String name;   public String getName() { return this.name; }   public void setName(String name) { this.name = name; }   private String custId;   public String getCustId() { return this.custId; }   public void setCustId(String custId) { this.custId = custId; }   private String address;   public String getAddress() { return this.address; }   public void setAddress(String address) { this.address = address; }   private String city;   public String getCity() { return this.city; }   public void setCity(String city) { this.city = city; }   private String state;   public String getState() { return this.state; }   public void setState(String state) { this.state = state; }   private String zip;   public String getZip() { return this.zip; }   public void setZip(String zip) { this.zip = zip; }   /*    * Allow the form bean to work with Value Objects.    */   public CustomerValueObject getValueObject () {   return new CustomerValueObject( this.name,                                   this.custId,                                   this.address,                                   this.city,                                   this.state,                                   this.zip );   }   public void  setValueObject (CustomerValueObject vo) {       this.setName(    vo.getName()     );       this.setCustId(  vo.getCustId()   );       this.setAddress( vo.getAddress()  );       this.setCity(    vo.getCity()     );       this.setState(   vo.getState()    );       this.setZip(     vo.getZip()      );   }   public void reset(ActionMapping mapping, HttpServletRequest request) {       this.name = "";       this.custId = "";       this.address = "";       this.city = "";       this.state = "";       this.zip = "";   } } 
The Action Class: CustomerAction.java

The value of all this design and organization really shows itself in the Action class. What could otherwise be a complicated class is actually pretty simple.

The first simplifying feature comes from creating the value object and writing the form bean to make use of it. As a result, the Action class can retrieve all the values it needs from the form bean with a single method call. It can also update the form bean with the results from the EJB container in a single method call. This is the benefit of using value objects in the design.

Another important benefit of value objects is the impact doing so has on maintaining the application. Over the life of the application, properties will likely be added and changed. When that happens, the form bean and value objects must change, but the Action class doesn't. By having the Action class work with the properties one bean at a time, changes inside the value object itself have no impact on it.

The second simplifying feature comes from burying the details of interacting with the EJB container inside a facade class. Because the Action class is primarily concerned with coordinating program flow and directing the processing, the details of interacting with the EJB container aren't really important to it.

The Action class really needs to know only whether the update was successful. In its role as the Controller in the MVC architecture, it doesn't need the details ”it only needs to know how to direct processing.

As part of burying the EJB container interactions inside the facade, all exceptions that can occur as part of the communications also should be buried there. This is done by having the facade throw only an application-specific exception ( CustomerUpdateException ). The facade catches any exceptions that can occur when interacting with the EJB container and rethrows them as CustomerUpdateException . Therefore, any changes in how the facade handles exceptions are isolated from the Action class.

CustomerAction.java is the Action class used in this sample application. Its source code is shown in Listing 18.5.

Listing 18.5 The CustomerAction.java Source Listing
 package struts.ch18.client; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import struts.ch18.client.CustomerValueObject; /**  * Action class to demonstrate EJB Integration with Struts  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerAction extends Action {     /**      * Update the customer's address.      *      * @param mapping The ActionMapping from this struts-config.xml entry      * @param actionForm The ActionForm to process, if any      * @param request The JSP request object      * @param response The JSP response object      *      * @exception Exception if business logic throws an exception      */     public ActionForward execute(ActionMapping mapping,                                  ActionForm form,                                  HttpServletRequest request,                                  HttpServletResponse response)         throws Exception {         /*          * Cast the form bean to CustomerForm          */         CustomerForm cf = (CustomerForm) form;         /*          * Instantiate the facade - it hides the details          * for us and simplifies the Action class.          */         CustomerEJBFacade facade = new CustomerEJBFacade();         /*          * The Value Object allows us to pass data "a bean at a time".          * This increases speed, reduces maintenance and simplifies          * tha Action class as well. Add methods to the form bean to          * allow it to work with Value Objects too.          */         CustomerValueObject cvo = cf.getValueObject();         try {           /*            * Pass in a value object, and get one in return            */           cvo = facade.addressChange( cvo );         } catch (CustomerUpdateException cue) {           // In a real application, this exception would be handled better.           // return (mapping.findForward("CustUpdateError"));         }         /*          * Update the form bean with all values returned at once.          */         cf.setValueObject(cvo);         return (mapping.findForward("default"));     } } 
The Facade: CustomerEJBFacade.java

As discussed in the previous section, the facade class coordinates interaction with the EJB container and provides a simple interface for the action class to work with. It also handles exception processing and rethrows any exceptions it can't handle as the application-specific exception CustomerUpdateException .

The main points regarding the functioning of this facade class are

  • The first thing it does is create an InitialContext to allow it to connect with the EJB container. Although this is a requirement for any EJB container, the details of how it's done are dependent on the specific EJB container you're using.

  • Another point is that, like the Action class, the facade class communicates to the EJB container using value objects. This simplifies processing in addition to making the communications more efficient.

  • The EJB that this class creates to work with is a stateless session bean. This is the preferred type of bean to work with. If the application requires information to be kept in session, this should be kept in session in the Web container while all EJBs remain stateless, if possible.

  • The stateless EJB is created just prior to use and removed as soon as the need for it is gone. This helps the EJB container manage its resources most efficiently .

  • The only exception thrown is the application-specific exception, CustomerUpdateException .

CustomerEJBFacade.java is the facade used in this sample application. Its source code is shown in Listing 18.6.

Listing 18.6 The CustomerEJBFacade.java Source Listing
 package struts.ch18.client; import java.util.Properties; import javax.naming.Context; import javax.naming.InitialContext; import jboss.ch18.interfaces.AbstractData; import jboss.ch18.interfaces.CustomerSession; import jboss.ch18.interfaces.CustomerSessionHome; /**  * Example facade which hides interactions with the back-end servers  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerEJBFacade {   public CustomerEJBFacade () { }   /**    * This method sends the value object to the EJB server    * for it to process. It's goal is to hide the implementation    * details of interacting with the EJB server.<p>    *    * Note that this method captures any EJB-access related Exceptions    * and rethrows them as "application-specific" Exceptions    * (CustomerUpdateException). This helps to "decouple" the    * application from the EJB-backend.    *    * @param cvo_in    * @return CustomerValueObject    * @throws CustomerUpdateException    */   public CustomerValueObject addressChange( CustomerValueObject cvo_in )   throws CustomerUpdateException   {       try {          /*           * The first section of code is to connect the servlet engine           * (Tomcat in this case) to the EJB server. Details are           * available in the JBoss code.           */          Properties props = new Properties();          props.put(Context.INITIAL_CONTEXT_FACTORY,                   "org.jnp.interfaces.NamingContextFactory" );          props.put(Context.PROVIDER_URL, "localhost:1099" );          props.put(Context.URL_PKG_PREFIXES,                   "org.jboss.naming:org.jnp.interfaces" );          props.put("jnp.socketFactory",                   "org.jnp.interfaces.TimedSocketFactory" );          InitialContext csContext = new InitialContext(props);          /*           * We now have an InitialContext - we can access EJBs through it;           * To access an EJB, first look up the Home Interface of the bean.           */          CustomerSessionHome csHome = (CustomerSessionHome)                 csContext.lookup( "ejb/jboss/ch18/CustomerSession" );          /*           * We now have the Bean's Home Interface. Now let's create a bean.           */          CustomerSession csBean = csHome.create();          /*           * We now have a bean. Pass the value object to the addressChange()           * method. It passes an updated one back.           */          CustomerValueObject cvo_return = csBean.addressChange(cvo_in);          /*           * Since the bean is stateless and will not be reused after this           * "action", go ahead and let the container know we are through           * and don't need it anymore.           */          csBean.remove();          /*           * Send back the updated value bean.           */          return cvo_return;       } catch( Exception e ){         throw new CustomerUpdateException(e.toString());       }    } } 
The Application Exception: CustomerUpdateException.java

Creating application-specific exceptions is a common way to have applications control and manage exceptions that can occur during processing. This is a best practice that, in this case, allows the Action class to be isolated from handling any of the variety of exceptions that have to do with EJB processing.

The CustomerUpdateException.java class is an application-specific exception that can be copied and modified for use with any application you're building. Its source code is shown in Listing 18.7.

Listing 18.7 The CustomerUpdateException.java Source Listing
 package struts.ch18.client; /**  * An application exception that wraps any exceptions that could occur when  * updating customer information.<p>  *  * It is expected that the CustomerEJBFacade will throw only this exception.  * All exceptions behind the facade should either catch and handle  * exceptions or re-throw them as CustomerUpdateException.<p>  *  * @author Kevin Bedell & James Turner  * @version 1.0  */ public class CustomerUpdateException extends java.lang.Exception {     /**      * Constructs a CustomerUpdateException with the specified detail message.      *      * @param msg - The detail message.      */     public CustomerUpdateException( String msg ) {         super( msg );     }     /**      * Constructs a CustomerUpdateException with no detail message.      *      */     public CustomerUpdateException() {         super();     } } 
The EJB: CustomerSessionBean.java

Finally, the EJB chapter actually presents an EJB! I hope it was worth the wait!

To be honest, you're probably going to be disappointed. After all, this is a book about Struts, so the real value that we tried to add is in how to design and build the Struts portion of the code. The EJB itself is actually pretty straightforward.

The only really important point with regard to the design that has been presented in this chapter is that the EJB, like all the Struts code, also works with our value object, CustomerValueObject .

This EJB was modified from one of the EJBs provided in the template application provided with JBoss. Because the JBoss developers recommend using XDoclet to generate home and remote interfaces as well as deployment descriptors, that's what the sample application does. More information about EJBs, JBoss, and XDoclet isn't provided here because there are other better sources for that information.

The CustomerSessionBean.java class is the stateless session bean used with this application. Its source code is shown in Listing 18.8.

Listing 18.8 The CustomerSessionBean.java Source Listing
 /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package jboss.ch18.session; import java.util.Random; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBException; import javax.ejb.FinderException; import javax.ejb.RemoveException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; import jboss.ch18.interfaces.InvalidValueException; import struts.ch18.client.CustomerValueObject; /**  * Session Bean for Customer information updating. This is a modification  * of the session bean that comes with the JBoss template project.  *  * It uses XDoclet to create the Remote and Home interfaces as well as  * to create the ejb-jar.xml and jboss deployment descriptors.  *  * ATTENTION: Some of the XDoclet tags are hidden from XDoclet by  *            adding a "--" between @ and the namespace. Please remove  *            this "--" to make it active or add a space to make an  *            active tag inactive.  *  * @ejb:bean name="jboss/ch18/CustomerSession"  *           display-name="Customer Test Session Bean"  *           type="Stateless"  *           transaction-type="Container"  *           jndi-name="ejb/jboss/ch18/CustomerSession"  *  ***/ public class CustomerSessionBean    implements SessionBean {    // -------------------------------------------------------------------------    // Static    // -------------------------------------------------------------------------    // -------------------------------------------------------------------------    // Members    // -------------------------------------------------------------------------    private SessionContext mContext;    // -------------------------------------------------------------------------    // Methods    // -------------------------------------------------------------------------    // -------------------------------------------------------------------------    // Methods    // -------------------------------------------------------------------------    /**     * Change the address property in the value object. This method randomly     * chooses one of three addresses and changes the value object address     * to that address.     *     * @param CustomerValueObject A value object which containing the     * current customer information.     *     * @return CustomerValueObject containing the updated address     *     * @throws RemoteException     *     * @ejb:interface-method view-type="remote"     * @ejb:transaction type="NotSupported"     **/   public CustomerValueObject addressChange( CustomerValueObject cvo )      throws RemoteException   {      Random r = new Random();      switch (r.nextInt(3)) {          case 0:            cvo.setAddress("1313 Mockingbird Lane");            break;          case 1:            cvo.setAddress("Corner of Michigan and Trumble");            break;          case 2:            cvo.setAddress("62 Hauser Street");            break;      }      /*       * This allows us to watch activity in the JBoss console log.       */      System.out.println( "CustomerSessionBean.addressChange() called." +                          " Set address to: " + cvo.getAddress() );      return cvo;   }   /**   * Create the Session Bean   *   * @throws CreateException   *   * @ejb:create-method view-type="remote"   **/   public void ejbCreate()      throws         CreateException   {      System.out.println( "CustomerSessionBean.ejbCreate() called" );   }   /**   * Describes the instance and its content for debugging purpose   *   * @return Debugging information about the instance and its content   **/   public String toString()   {      return "CustomerSessionBean [ " + " ]";   }   // -------------------------------------------------------------------------   // Framework Callbacks   // -------------------------------------------------------------------------   public void setSessionContext( SessionContext aContext )      throws         EJBException   {       mContext = aContext;   }   public void ejbActivate()      throws         EJBException   {   }   public void ejbPassivate()      throws         EJBException   {   }   public void ejbRemove()      throws         EJBException   {      System.out.println( "CustomerSessionBean.ejbRemove() called" );   } } 


Struts Kick Start
Struts Kick Start
ISBN: 0672324725
EAN: 2147483647
Year: 2002
Pages: 177

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net