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.
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.
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
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 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; ... }
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) { ... } }
Creating or accessing protected objects should be done after the init method, in one of the servlet's doXXX methods.
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. --- ... }
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. --- ... }
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. --- ... }
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>"); }
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.