ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Java Servlet & JSP Cookbook

Cooking with Java Servlets & JSP, Part 2

Related Reading

Java Servlet & JSP Cookbook
By Bruce W. Perry

by Bruce W. Perry

Editor's note: Last week we published three sample recipes from O'Reilly's Java Servlets & JSP Cookbook. This week, we conclude this two-part series with a look at recipes from the book that use servlets to access EJBs and to connect to Amazon Web Services.

Recipe 25.8: Accessing an EJB Using the WebLogic JNDI Tree

Problem

You want to access an Enterprise JavaBean (EJB) from a servlet on WebLogic.

Solution

Find out the EJB's JNDI name and use the javax.naming package to get a reference to the EJBObject or remote interface so that you can call the EJB's methods.

Discussion

A servlet accesses an EJB by using a specified JNDI name. The process is therefore transparent to the servlet developer. Any EJBs an application uses comprise the business tier of an application. The servlets and JSPs represent the web tier within the multi-tiered distributed architecture of a typical Java 2 Enterprise Edition (J2EE) application. All you need to know is the JNDI name associated with the EJB in order to use the EJB in your programs.

TIP: Enterprise JavaBeans is a comprehensive topic; however, this recipe is devoted to showing how a servlet can connect to an EJB. The "See Also" segment of this recipe includes several links to EJB and J2EE information and books.

You should be aware of the EJB's business methods, but do not have to be an expert on the javax.ejb package to use the EJB. Example 25-13 shows the source code for a stateless session EJB that is managed by BEA WebLogic 7.0 application server.

TIP: A certain type of EJB, a stateless session bean encapsulates business logic that does not require persistence or the saving of the object's state between method calls. On the other hand, a stateful session bean (such as a shopping cart object), must remember the object's state (such as the value of various instance variables) between method calls, as part of a conversation with the EJB client.

Example 25-13 provides a java.util.Map that links U.S. state names with their postal abbreviations. The session bean includes one business method, getAbbreviation( ), which receives a state name as a parameter and returns its postal abbreviation.

Example 25-13. The stateless session EJB

package com.jspservletcookbook;

import javax.ejb.*;

import java.util.Map;
import java.util.HashMap;

public class AbbrevBean implements SessionBean{

    private SessionContext context;
    private Map abbrevMap;
    
    
  public AbbrevBean( ){ //the bean's no-arguments constructor
    
      //A Map containing the names of states and abbreviations
      abbrevMap = new HashMap( );
    
      abbrevMap.put("ALABAMA","AL");
      abbrevMap.put("ALASKA","AK");
      abbrevMap.put("AMERICAN SAMOA","AS");
      abbrevMap.put("ARIZONA","AZ");
      abbrevMap.put("ARKANSAS","AR");
      abbrevMap.put("CALIFORNIA","CA");
      abbrevMap.put("COLORADO","CO");
      abbrevMap.put("CONNECTICUTT","CT");
    
      abbrevMap.put("DELAWARE","DE");
      abbrevMap.put("DISTRICT OF COLUMBIA","DC");
      abbrevMap.put("FEDERATED STATES OF MICRONESIA","FM");
      abbrevMap.put("FLORIDA","FL");
      abbrevMap.put("GEORGIA","GA");
      abbrevMap.put("GUAM","GU");
      abbrevMap.put("HAWAII","HI");
      abbrevMap.put("IDAHO","ID");
    
      abbrevMap.put("ILLINOIS","IL");
      abbrevMap.put("INDIANA","IN");
      abbrevMap.put("IOWA","IA");
      abbrevMap.put("KANSAS","KS");
      abbrevMap.put("KENTUCKY","KY");
      abbrevMap.put("LOUISIANA","LA");

      abbrevMap.put("MAINE","ME");
      abbrevMap.put("MARSHALL ISLANDS","MH");
      abbrevMap.put("MARYLAND","MD");
      abbrevMap.put("MASSACHUSETTS","MA");
      abbrevMap.put("MICHIGAN","MI");
      abbrevMap.put("MINNESOTA","MN");
    
      abbrevMap.put("MISSISSIPPI","MS");
      abbrevMap.put("MISSOURI","MO");
      abbrevMap.put("MONTANA","MT");
      abbrevMap.put("NEBRASKA","NE");
      abbrevMap.put("NEVADA","NV");
      abbrevMap.put("NEW HAMPSHIRE","NH");
    
      abbrevMap.put("NEW JERSEY","NJ");
      abbrevMap.put("NEW MEXICO","NM");
      abbrevMap.put("NEW YORK","NY");
      abbrevMap.put("NORTH CAROLINA","NC");
      abbrevMap.put("NORTH DAKOTA","ND");
      abbrevMap.put("NORTHERN MARIANA ISLANDS","MP");
    
      abbrevMap.put("OKLAHOMA","OK");
      abbrevMap.put("OREGON","OR");
      abbrevMap.put("PALAU","PW");
      abbrevMap.put("PENNSYLVANIA","PA");
      abbrevMap.put("PUERTO RICO","PR");
      abbrevMap.put("RHODE ISLAND","RI");
      abbrevMap.put("SOUTH CAROLINA","SC");
      abbrevMap.put("SOUTH DAKOTA","SD");
    
      abbrevMap.put("TENNESSEE","TN");
      abbrevMap.put("TEXAS","TX");
      abbrevMap.put("UTAH","UT");
      abbrevMap.put("VERMONT","VT");
      abbrevMap.put("VIRGIN ISLANDS","VI");
      abbrevMap.put("VIRGINIA","VA");
      abbrevMap.put("WASHINGTON","WA");
      abbrevMap.put("WEST VIRGINIA","WV");
      abbrevMap.put("WISCONSIN","WI");
      abbrevMap.put("WYOMING","WY");
 
  }//constructor
    
  public void setSessionContext(SessionContext ctx) throws 
    EJBException {
      context = ctx;
  }//setSessionContext

  public Map getAbbrevMap( ){
      return abbrevMap;
  }
    
  //The bean's business method
  public String getAbbreviation(String state){
      return (String) abbrevMap.get(state);
  }
    
  //javax.ejb.SessionBean method; it has to be implemented in a session 
  //bean, but is not relevant to Stateless session beans.
  public void ejbActivate( ){}

  //javax.ejb.SessionBean method; it has to be implemented in a Session 
  //bean, but is not relevant to stateless session beans.
  public void ejbPassivate( ){}

  //javax.ejb.SessionBean method;
  public void ejbRemove( ) {}

}

Example 25-13 could easily be implemented as an ordinary Java helper or utility class. However, I show a simple example of an EJB so that the recipe can focus on how a servlet connects to these objects.

EJBs have a deployment descriptor, similar to the web.xml file that web applications use. The EJB deployment descriptor must be named ejb-jar.xml. When you package the EJB(s) before they are deployed on an application server, include this deployment descriptor as part of the archive. The ejb-jar.xml file describes the related EJB component(s); the application server uses this descriptive information in order to properly deploy the EJB.

For example, the ejb-jar.xml file in Example 25-14 specifies the type of EJB (e.g., stateless session bean) and the fully qualified class names of its related Java classes, such as its remote interface.

Example 25-14. The ejb-jar.xml file

<?xml version="1.0"?>

  <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.
  //DTD Enterprise JavaBeans 2.0//EN"
  "http://java.sun.com/dtd/ejb-jar_2_0.dtd"
   >

<ejb-jar>
  <enterprise-beans>
    <session>
      <ejb-name>AbbreviationEjb</ejb-name>
      <home>com.jspservletcookbook.AbbrevHome</home>
      <remote>com.jspservletcookbook.Abbrev</remote>
      <local-home>com.jspservletcookbook.AbbrevLocalHome</local-home>
      <local>com.jspservletcookbook.AbbrevLocal</local>
      <ejb-class>com.jspservletcookbook.AbbrevBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
    </session>
  </enterprise-beans>
</ejb-jar>

The package that contains this EJB, and with which the EJB is deployed on the application server, is a JAR file named myejb.jar (just a name I concocted; you do not have to use the same name).

Since this stateless session bean is deployed on BEA WebLogic Server, the JAR file must include a vendor-specific deployment descriptor named weblogic-ejb-jar.xml. This deployment descriptor gives the deployer the opportunity to configure several aspects of how the EJB is deployed on WebLogic, such as the JNDI names of its home and local home interfaces.

TIP: The "home" object is an implementation of the "home" interface, and the "local home" object is an implmentation of the local home interface. These objects are "factories" for EJB objects, which delegate the business-method calls to the EJB deployed in the server. A factory is a Java class that generates objects of a different kind of Java class. In this recipe's case, the client uses JNDI to get a reference to the home object, which creates an EJB object. The servlet (client) then calls the EJB object's getAbbreviation( ) method; the EJB object is a remote object or "stub" that delegates this method call to the original EJB stored on the server.

You will encounter the home object's JNDI name in the servlet depicted later on in this recipe.

TIP: When you deploy an EJB on WebLogic using the Administration Console, WebLogic automatically binds the home and local home objects within the WebLogic JNDI tree, using the names specified by the weblogic-ejb-jar.xml deployment descriptor.

Example 25-15 shows the weblogic-ejb-jar.xml deployment descriptor for our stateless session bean.

Example 25-15. The weblogic-ejb-jar.xml file

<!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'>

<weblogic-ejb-jar>
  <weblogic-enterprise-bean>
    <ejb-name>AbbreviationEjb</ejb-name>
    <stateless-session-descriptor>
        <pool>
            <initial-beans-in-free-pool>1</initial-beans-in-free-pool>
        </pool>
    </stateless-session-descriptor>
    <jndi-name>AbbrevHome</jndi-name>
    <local-jndi-name>AbbrevLocalHome</local-jndi-name>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

An EJB module is a complicated package that includes bean classes, remote interfaces, and two different deployment descriptors. Example 25-16 shows the contents of the myejb.jar file. I use the jar tvf myejb.jar. command in a command-line window to display the contents of the specified JAR file (it works in both Unix and Windows).

Example 25-16. The contents of the ejb-jar.xml file

H:\book\cookbook\code\chap27\src\ejbs\ejbjar>jar tvf myejb.jar

    META-INF/
    META-INF/MANIFEST.MF
    com/
    com/jspservletcookbook/
    com/jspservletcookbook/Abbrev.class
    com/jspservletcookbook/AbbrevBean.class
    com/jspservletcookbook/AbbrevHome.class
    com/jspservletcookbook/AbbrevLocal.class
    com/jspservletcookbook/AbbrevLocalHome.class
 
    META-INF/ejb-jar.xml
    META-INF/weblogic-ejb-jar.xml

In Example 25-16, the session bean is AbbrevBean.class, the remote interface is Abbrev.class, and the home object (the factory for EJB objects that implement the Abbrev interface) is AbbrevHome.class.

Finally, Example 25-17 shows the servlet that uses the session bean from Example 25-13. The code is self-explanatory. The important thing to remember is that the servlet receivese a reference to the AbbrevHome object from the WebLogic JNDI tree. Then the servlet, in its doGet( ) method, calls the AbbrevHome object's create( ) method to get an instance of the session bean's remote interface (in this example, it's an Abbrev type ).

Example 25-17. A servlet that accesses the EJB on WebLogic using JNDI

package com.jspservletcookbook;    

import java.io.IOException;  
import java.io.PrintWriter;   

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;  

import javax.rmi.PortableRemoteObject;

import javax.servlet.*;
import javax.servlet.http.*;

public class WebJndiServlet extends HttpServlet {

  public void doGet(HttpServletRequest request, 
    HttpServletResponse response)
    throws ServletException, java.io.IOException {
    
      //The request parameter looks like 'state=Massachusetts'
      String state = request.getParameter("state");
      Context env = null;
      Abbrev abbrev = null;
      AbbrevHome home = null;
        
      try{
            
          env = (Context) new InitialContext( );
           
          //Look up the home or factory object on the WebLogic JNDI tree  
          Object localH  = env.lookup("AbbrevHome");
             
          //This method call is necessary for EJB code that uses a 
          //technology called RMI-IIOP
          home = (AbbrevHome) PortableRemoteObject.narrow(localH,
              AbbrevHome.class);
             
          //close the InitialContext
          env.close( );
             
          if (home == null)
              throw new ServletException(
              "AbbrevHome is an unknown JNDI object");
             
          //Get the remote interface by calling the home object's create( ) 
          //method
          abbrev = (Abbrev) PortableRemoteObject.narrow(home.create( ),
              Abbrev.class);
             
      } catch (NamingException ne) { 
          try{ env.close( );} catch (NamingException nex) { }
          throw new ServletException(ne);
      } catch (javax.ejb.CreateException ce) { 
             throw new ServletException(ce);
      }
    
    
      //set the MIME type of the response, "text/html"
      response.setContentType("text/html");
    
      java.io.PrintWriter out = response.getWriter( );
    
      out.println("<html><head>");
    
      out.println("<title>State abbreviations</title></head><body>");
      out.println("<h2>Here is the state's abbreviation</h2>");
    
      //Call the EJBObject's getAbbreviation( ) method; the EJBObject  
      //delegates this method call to the session bean. Put the request 
      //parameter in all upper-case, because this is how the session bean's 
      //java.util.Map stores the state names, which are the Map's keys

      if (state != null)
          out.println( abbrev.getAbbreviation(state.toUpperCase( )) );
        
          try{
              //The servlet is through with the EJBObject; call its remove( ) 
          //method
          abbrev.remove( );
          } catch (javax.ejb.RemoveException re){}
   
      out.println("</body></html>");

     }//doGet
     
  public void doPost(HttpServletRequest request, 
    HttpServletResponse response)
    throws ServletException, java.io.IOException {
       
         doGet(request, response);
    
  }//doPost
    
}//WebJndiServlet

The value of the abbreviation for a state such as "Oregon" is ultimately retrieved on the server side by calling the session bean's getAbbreviation( ) method. Figure 25-8 shows a web browser window after a user has requested the servlet. The URL looks something like http://localhost:7001/webjndi?state=Oregon. The URL pattern /webjndi is mapped in web.xml to the servlet of Example 25-17.

Figure 25-8
Figure 25-8. An EJB-accessing servlet's web browser display

See Also

Recipe 25.4 on configuring a JNDI object with WebLogic; Recipe 25.6 on accessing a JNDI object with a servlet on WebLogic; Chapter 2 on deploying web components with WebLogic; a web link for the javax.ejb API: http://java.sun.com/j2ee/1.4/docs/api/javax/ejb/package-summary.html; the documentation page for WebLogic Server 7.0: http://edocs.bea.com/wls/docs70/index.html; a link to J2EE tutorials, including an Enterprise JavaBean tutorial: http://java.sun.com/j2ee/tutorial/index.html; Enterprise JavaBeans, Third Edition (O'Reilly); J2EE Design Patterns (O'Reilly).

Recipe 27.5: Getting Set Up with Amazon's Web Services API

Problem

You want to connect to Amazon Web Services (AWS) with a servlet or JSP.

Solution

Download the Amazon Web Services SDK, acquire an Amazon developer's token, and create a Java-SOAP package for interacting with AWS.

Discussion

The process for setting up AWS goes like this:

  1. Download the AWS SDK at http://www.amazon.com/gp/aws/download_sdk.html/002-2688331-0628046. This kit.zip file includes several code samples and web services API documentation in HTML format.

  2. Acquire a developer's token from: http://associates.amazon.com/exec/panama/associates/join/developer/application.html/002-2688331-0628046. Similar to the license key you use with Google's Web APIs, the free-of-charge token comprises a series of encoded characters that must accompany each interaction between your Java code and AWS.

  3. Develop the Java API for making SOAP requests to AWS. The end result is a JAR file containing the classes that your servlets or JSPs use to make SOAP requests. The rest of this recipe describes how to generate this JAR file, because it is a multistep process.

TIP: Interacting with AWS using SOAP messages is one option that Amazon makes available to developers. Another one involves encoding the web services requests in URLs, and thereby making AWS requests via HTTP (called "XML over HTTP"). Recipe 27.7 shows an example of this URL search (they are useful for debugging your SOAP applications). If you store an XSLT file on the Web, AWS uses this file to format the response to XML-over-HTTP requests. See the SDK documentation for more details.

SOAP with Apache Axis

The creation of a Java-SOAP API for using AWS begins with downloading an open source SOAP toolkit named Apache Axis (http://ws.apache.org/axis/). Here are the steps involved in creating the API:

  1. Download Axis and extract the Axis ZIP file to the directory of your choice (this creates a directory named axis-1_1).

  2. Inside the axis-1_1/lib directory are several JAR files. Place these JAR files on your classpath and then run a program named org.apache.axis.wsdl.WSDL2Java to generate Java source files. These Java source files comprise the Java API you will use with AWS when you compile the files.

  3. Download the Web Services Description Language (WSDL) file associated with the Amazon Web Services. At this writing, the file can be found at: http://soap.amazon.com/schemas3/AmazonWebServices.wsdl.

  4. The following command line generates the com.amazon.soap.axis package for your Java API. The command lines in this recipe work on both Windows- and Unix-based machines. The command line is designed to refer to the AmazonWebServices.wsdl file in the current directory. The WSDL2Java program generates Java classes based on the XML elements described by the WSDL XML file (XML-to-Java conversion). This allows you to work with AWS using only Java objects, which is very nice - it's why you are enduring the initial pain of creating these Java classes! Break up this command line into separate lines to make it more readable, but when you actually run it, the commands must all be combined on one line:

    java -cp .;lib/axis.jar;lib/commons-discovery.jar;lib/commons-
      logging.jar;lib/jaxrpc.jar;lib/saaj.jar;lib/wsdl4j.jar 
        org.apache.axis.wsdl.WSDL2Java AmazonWebServices.wsdl --verbose 
          --package com.amazon.soap.axis
  5. This command line generates Java source files in a directory tree that matches the specified package name (com.amazon.soap.axis). Now you have to compile these classes with the javac tool, as in the following command line (the current directory contains the com directory). Once again, we break up this single-line command into separate lines just for the sake of readability (you have to run the command line unbroken by any newline characters):

    javac -classpath  .;lib/axis.jar;lib/commons-discovery.jar;lib/commons-
    logging.jar;lib/jaxrpc.jar;lib/saaj.jar;lib/wsdl4j.jar 
    com/amazon/soap/axis/*.java
  6. Now JAR up all these files. In the same directory containing the top-level com directory, this command creates a JAR file named amazonapi.jar, which is just a name I created for it:

    jar cvf amazonapi.jar ./com
  7. Take the amazonapi.jar (or whatever you've named the JAR file) and place it in WEB-INF/lib. There's one more step left.

  8. Make sure that the JAR files or libraries that the com.amazon.soap.axis package depends on are also available to the web application. The amazonapi.jar file depends on the same Axis libraries that you added to the class path in the prior java and javac command-line sequences. You have to add these JARs to WEB-INF/lib as well (unless your application server makes all of these libraries generally available to web applications).

Okay, now for the fun part, where your Java code gets to explore books and other stuff at Amazon using servlets. Your servlets should use the com.amazon.soap.axis package for this purpose.

See Also

The AWS SDK http://www.amazon.com/gp/aws/download_sdk.html/002-2688331-0628046; Apache Axis: http://ws.apache.org/axis/; the Amazon Web Services WSDL file: http://soap.amazon.com/schemas3/AmazonWebServices.wsdl.

Bruce W. Perry is an independent software developer and writer, and the author of Java Servlet & JSP Cookbook and just-published Ajax Hacks.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.