Developing servlets that use enterprise beans

A servlet is a Java application that enables users to access Web server functionality. To use servlets, a Web server is required. The WebSphere Application Server plugs into a number of commonly used Web servers. The IBM HTTP Server with the Advanced Application Server. For more information, consult the Advanced Edition InfoCenter.

Java servlets can be combined with enterprise beans to create powerful EJB applications. This chapter describes how to use enterprise beans within a servlet. The example CreateAccount servlet, which uses the example Account bean, is used to illustrate the concepts discussed in this chapter. The example servlet and enterprise bean discussed in this chapter are explained in Information about the examples described in the documentation.


An overview of standard servlet methods

Usually, a servlet is invoked from an HTML form on the user's browser. The first time the servlet is invoked, the servlet's init method is run to perform any initializations required at startup. For the first and all subsequent invocations of the servlet, the doGet method (or, alternatively, the doPost method) is run. Within the doGet method (or the doPost method), the servlet gets the information provided by the user on the HTML form and uses that information to perform work on the server and access server resources.

The servlet then prepares a response and sends the response back to the user. After a servlet is loaded, it can handle multiple simultaneous user requests. Multiple request threads can invoke the doGet (or doPost) method at the same time, so the servlet needs to be made thread safe.

When a servlet shuts down, the destroy method of the servlet is run in order to perform any needed shutdown processing.


Writing an HTML page that embeds a servlet

Figure 33 shows the HTML file (named create.html) used to invoke the CreateAccount servlet. The HTML form is used to specify the account number for the new account, its type (checking or savings), and its initial balance. The request is passed to the doGet method of the servlet, where the servlet is identified with its full Java package name, as shown in the example.

Figure 33. Code example: Content of the create.html file used to access the CreateAccount servlet

<html>
<head>
<title>Create a new Account</title>
</head>
<body>
<h1 align="center">Create a new Account</h1>
<form method="get"
action="/servlet/com.ibm.ejs.doc.client.CreateAccount">
<table border align="center">
<!-- specify a new account number -->
<tr bgcolor="#cccccc">
<td align="right">Account Number:</td>
<td colspan="2"><input type="text" name="account" size="20"
maxlength="10">
</tr>
<!-- specify savings or checking account -->
...
<!-- specify account starting balance -->
...
<!-- submit information to servlet -->
...
<input type="submit" name ="submit" value="Create">
...
<!-- message area -->
...
</form>
</body>
</html>

The HTML response from the servlet is designed to produce a display identical to create.html, enabling the user to continue creating new accounts. Figure 34 shows what create.html looks like on a browser.

Figure 34. The initial form and output of the CreateAccount servlet

The initial form and output of the CreateAccount servlet


Developing the servlet

This section discusses the basic code required by a servlet that interacts with an enterprise bean. Figure 35 shows the basic outline of the code that makes up the CreateAccount servlet. As shown in the example, the CreateAccount servlet extends the javax.servlet.http.HttpServlet class and implements an init method and a doGet method.

Figure 35. Code example: The CreateAccount class

package com.ibm.ejs.doc.client;
// General enterprise bean code.
import java.rmi.RemoteException;
import javax.ejb.DuplicateKeyException;
// Enterprise bean code specific to this servlet.
import com.ibm.ejs.doc.account.AccountHome;
import com.ibm.ejs.doc.account.AccountKey;
import com.ibm.ejs.doc.account.Account;
// Servlet related.
import javax.servlet.*;
import javax.servlet.http.*;
// JNDI (naming).
import javax.naming.*;  // for Context, InitialContext, NamingException
// Miscellaneous:
import java.util.*;
import java.io.*;
...
public class CreateAccount extends HttpServlet {
   // Variables 
   ...
   public void init(ServletConfig config) throws ServletException {
      ...
   }
   public void doGet(HttpServletRequest req, HttpServletResponse res)
   throws ServletException, IOException {
      // --- Read and validate user input, initialize. ---
      ...
      // --- If input parameters are good, try to create account. ---
      ...
      // --- Prepare message to accompany response. ---
      ...
      // --- Prepare and send HTML response. ---
      ...
}

The servlet's instance variables
Figure 36 shows the instance variables used in the CreateAccount servlet. The nameService, accountName, and providerUrl variables are used to specify the property values required during JNDI lookup. These values are obtained from the ClientResourceBundle class as described in Creating and getting a reference to a bean's EJB object.

The CreateAccount class also initializes the string constants that are used to create the HTML response sent back to the user. (Only three of these variables are shown, but there are many of them). The init method in the CreateAccount servlet provides a way to read strings from a resource bundle to override these US English defaults in order to provide a response in a different national language. The instance variable accountHome is used by all client requests to create a new Account bean instance. The accountHome variable is initialized in the init method as shown in Figure 36.

Figure 36. Code example: The instance variables of the CreateAccount class

...
public class CreateAccount extends HttpServlet {
     // Variables for finding the home
     private String nameService = null;
     private String accountName = null; 
     private String providerURL = null;
     private ResourceBundle bundle =  ResourceBundle.getBundle(
               "com.ibm.ejs.doc.client.ClientResourceBundle");
     // Strings for HTML output - US English defaults shown.
     static String title = "Create a new Account";
     static String number  = "Account Number:";
     static String type  = "Type:";
     ...
     // Variable for accessing the enterprise bean.
     private AccountHome accountHome = null;
     ...
     
}

The servlet's init method
The init method of the CreateAccount servlet is shown in Figure 37. The init method is run once, the first time a request is processed by the servlet, after the servlet is started. Typically, the init method is used to do any one-time initializations for a servlet. For example, the default US English strings used in preparing the HTML response can be replaced with another national language. The init method is also the best place to initialize the value of references to the home interface of any enterprise beans used by the servlet. In the CreateAccount's init method, the accountHome variable is initialized to reference the EJB home object of the Account bean.

As in other types of EJB clients, the properties required to do a JNDI lookup are specific to the EJB implementation. Therefore, these properties are externalized in a properties file or a resource bundle class. For more information on these properties, see Creating and getting a reference to a bean's EJB object.

Note that in the CreateAccount servlet, a HashTable object is used to store the properties required to do a JNDI lookup whereas a Properties object is used in the TransferApplication. Both of these classes are valid for storing these properties.

Figure 37. Code example: The init method of the CreateAccount servlet

// Variables for finding the EJB home object
private String nameService = null;
private String accountName = null;
private String providerURL = null;
private ResourceBundle bundle = ResourceBundle.getBundle(
          "com.ibm.ejs.doc.client.TransferResourceBundle");
...
public void init(ServletConfig config) throws ServletException {
     super.init(config);
     ...
    try {
         // Get NLS strings from an external resource bundle
          ...
          createTitle = bundle.getString("createTitle");
          number = bundle.getString("number");
          type = bundle.getString("type");
          ...
          //Get values for the naming factory and home name.
          nameService = bundle.getString("nameService");
          accountName = bundle.getString("accountName");
          providerURL = bundle.getString("providerURL");
     } 
     catch (Exception e) {
         ...
     }
     // Get home object for access to Account enterprise bean.
     Hashtable env = new Hashtable();
     env.put(Context.INITIAL_CONTEXT_FACTORY, nameService);
     try {
          // Create the initial context.
          Context ctx = new InitialContext(env);
          // Get the home object.
          Object homeObject = ctx.lookup(accountName);
          // Get the AccountHome object.
          accountHome = (AccountHome) javax.rmi.PortableRemoteObject.narrow(
                         homeObject, AccountHome.class);
     }
     // Determine cause of failure.
     catch (NamingException e) {
          ...
      }
      catch (Exception e) {
          ...
     }
}
Note:
Although the init method is a good place to obtain references to EJB home objects, it is not a good place to create enterprise beans or access other beans that might be protected with WebSphere security. Depending upon the authorization policy on the protected objects, creating or accessing these objects from within the init method could fail for authentication or authorization reasons because they were not accessed with the proper security credentials.

Creating or accessing protected objects should be done after the init method, in one of the servlet's doXXX methods.

The servlet's doGet method
The doGet method is invoked for every servlet request. In the CreateAccount servlet, the method does the following tasks to manage user input. These tasks are fairly standard for this method:

Figure 38 shows the parts of the doGet method that handle user input. Note that the req variable is used to read the user input from the HTML form. The req variable is a javax.servlet.http.HttpServletRequest object passed as one of the arguments to the doGet method.

Figure 38. Code example: The doGet method of the CreateAccount servlet

public void doGet (HttpServletRequest req, HttpServletResponse res) 
     throws ServletException, IOException {
     // --- Read and validate user input, initialize. ---
     // Error flags.
     boolean accountFlag  = true;
     boolean balanceFlag  = true;
     boolean inputFlag = false;
     boolean createFlag  = true;
     boolean duplicateFlag = false;
     // Datatypes used to create new account bean.
     AccountKey key;
     int typeAcct  = 0;
     String typeString = "0";
     float initialBalance = 0;
     // Read input parameters from HTML form.
     String[] accountArray = req.getParameterValues("account");
     String[] typeArray = req.getParameterValues("type");
     String[] balanceArray = req.getParameterValues("balance");
     // Convert input parameters to needed datatypes for new account.
     // (account)
     long accountLong = 0;
     ... 
     key = new AccountKey(accountLong);
     // (type)
     if (typeArray[0].equals("1")) {
          typeAcct   = 1;           // Savings account.
          typeString = "savings";
     }
     else if (typeArray[0].equals("2")) {
          typeAcct   = 2;           // Checking account
          typeString = "checking";
     }
     // (balance)
     try {
          initialBalance = (Float.valueOf(balanceArray[0])).floatValue();
     } catch (Exception e) {
          balanceFlag = false;
     }
     ...
      // --- If input parameters are good, try to create account bean. ---
     ...
     // --- Prepare message to accompany response. ---
     ...
     // --- Prepare and send HTML response. ---
     ...
}

Creating an enterprise bean
If the user input is valid, the doGet method attempts to create a new account based on the user input as shown in Figure 39. Besides the initialization of the home object reference in the init method, this is the only other piece of code that is specific to the use of enterprise beans in a servlet.

Figure 39. Code example: Creating an enterprise bean in the doGet method

public void doGet(HttpServletRequest req, HttpServletResponse res) 
     throws ServletException, IOException {
     // --- Read and validate user input, initialize ---.
     ...
     // --- If input parameters are good, try to create account bean. ---
     if (accountFlag && balanceFlag) {
          inputFlag = true;
          try {
               // Create the bean.
               Account account = accountHome.create(key, typeAcct, initialBalance);
          }
          // Determine cause of failure.
          catch (RemoteException e) {
               ...
          }
          catch (DuplicateKeyException e) {
                ...
         }
         catch (Exception e) {
               ...
          }
     }
     // --- Prepare message to accompany response. ---
     ...
     // --- Prepare and send HTML response. ---
     ...
 }

Determining the content of the user response
Next, the doGet method prepares a response message to be sent to the user. There are three possible responses:

Figure 40 shows the code used by the servlet to determine which response to send to the user. If no errors are encountered, then the response indicates success.

Figure 40. Code example: Determining a user response in the doGet method

public void doGet(HttpServletRequest req, HttpServletResponse res) 
     throws ServletException, IOException {
       // --- Read and validate user input, initialize. ---
      ...
      // --- If input parameters are good, try to create account bean. ---
      ...
      // --- Prepare message to accompany response. ---
      ...
    String messageLine  = "";
    if (inputFlag) {
          // If you are here, the client input is good.
          if (createFlag) {
          // New account enterprise bean was created.
          messageLine =  createdaccount + " " + accountArray[0] + ", " + 
                    createdtype + " " + typeString + ", " + 
                    createdbalance + " " + balanceArray[0]; 
          }
          else if (duplicateFlag) {
               // Account with same key already exists.
               messageLine = failureexists + " " + accountArray[0];
          }
          else {
          // Other reason for failure.
               messageLine = failureinternal + " " + accountArray[0]; 
          }
     }
     else {
          // If you are here, something was wrong with the client input.
          String separator = "";
          if (!accountFlag) {
               messageLine = failureaccount + " " + accountArray[0];
               separator = ", ";
     }
     if (!balanceFlag) {
          messageLine = messageLine + separator + 
                    failurebalance + " " + balanceArray[0];
     }
     // --- Prepare and send HTML response. ---
     ...
 }

Sending the user response
With the type of response determined, the doGet method then prepares the full HTML response and sends it to the user's browser, incorporating the appropriate message. Relevant parts of the full HTML response are shown in Figure 41. The res variable is used to pass the response back to the user. This variable is an HttpServletResponse object passed as an argument to the doGet method. The response code shown here mixes both display (HTML) and content in one servlet. You can separate the display and the content by using JavaServer Pages (JSP). A JSP allows the display and content to be developed and maintained separately.

Figure 41. Code example: Responding to the user in the doGet method

public void doGet(HttpServletRequest req, HttpServletResponse res) 
     throws ServletException, IOException {
      // --- Read and validate user input, initialize. ---
      ...
      // --- If input parameters are good, try to create account bean. ---
      ...
      // --- Prepare message to accompany response. ---
      ...
     // --- Prepare and send HTML response. ---
     // HTML returned looks like initial HTML that invoked this servlet. 
     // Message line says whether servlet was successful or not. 
     res.setContentType("text/html"); 
     res.setHeader("Pragma", "no-cache"); 
     res.setHeader("Cache-control", "no-cache"); 
     PrintWriter out = res.getWriter();
     out.println("<html>");  
     ...
     out.println("<title> " + createTitle + "</title>");
     ...
     out.println(" </html>");   
}

Threading issues

Except for the instance variable required to get a reference to the Account bean's home interface and to support multiple languages (which remain unchanged for all user requests), all other variables used in the CreateAccount servlet are local to the doGet method. Each request thread has its own set of local variables, so the servlet can handle simultaneous user requests.

As a result, the CreateAccount servlet is thread safe. By taking a similar approach to servlet design, you can also make your servlets thread safe.