Sessions
HTTP, the protocol used for communication between a browser and the Web server, is essentially stateless. This means that the Web server does not remember interaction with the browser.
A session is an interaction between the Web server and a client browser that is remembered by the Web server. The importance of maintaining a session becomes evident when developing server-side applications where the application needs to be able to identify and associate different browser requests with the respective browsers. Hence, sessions are primarily used to create a state between the browser and the Web server.
Maintaining a session can be carried out either on the server side or on the client side.
There are four primary ways of implementing session handling in your applications:
Session handling using hidden variablesThis is a server-side method of session handling. The developer assumes responsibility for maintaining the session.
Session handling using cookiesMaintaining a session using cookies requires support from the browser. Hence, this method is a client-side way of session handling.
Session handling using URL rewritingURL rewriting is primarily the developer's responsibility since the modification of the URL is done by the server-side application.
Session handling using the HttpSession APIThe HttpSession API provides a full range of support for state maintenance. This method is a server-side state maintenance since the association between the browser and the server is supported by the server's implementation of the Java servlet specification.
WebLogic Server provides support for the session-handling techniques using URL rewriting, cookies, and the HttpSession API. Since the responsibility of maintaining a session rests with the developer in session handling using hidden variables, there is no support expected from WebLogic Server for this technique.
These different types of session handling will be demonstrated using a shopping cart servlet example. The shopping cart is one of the simplest and most intuitive ways to demonstrate session maintenance.
The shopping cart servlet application consists of two pages served to the user. The book-listing page is where users can see the list of books in the shop and select books to add to their shopping cart. The second page displays the contents of the shopping cart to the user. The session handling performed for this servlet is the shopping cart for each user who accesses the online shop using his or her browser.
Shopping Cart Servlet Design
Now that the problem statement is clear, you can begin work on the design of the shopping cart servlet application and then implement each method of session handling. The design will be defined for the user interface as well as the shopping cart servlet.
The book-listing page will be divided into three zones: header, book listing, and footer. The cart-viewing page will be divided into three similar zones: header, shopping cart contents, and footer. By dividing the pages into these zones, you can identify and separate the static content of the page from the dynamic content. This helps you to develop code that can be reused for generating the static content.
The class diagram in Figure 3.5 shows the different classes in the shopping cart servlet:
BookShoppingServletThe main class in your application, which extends the HttpServlet class since it services HTTP requests from browsers.
BookStores the book name, book quantity, and the book price for each book. Your online shop is a small one; you will sell only three books.
UtilityA support class that provides functionality that can be reused not only for your application but also for other applications that you will be developing as you go along in this book.
Figure 3.5 Class diagram of the BookShoppingServlet.
Now take a detailed look at each of the classes.
The BookShoppingServlet servlet services the GET and POST requests from the browser. Hence, it has the doGet() and doPost() methods. The main function supported by the servlet is session maintenance, by which it maintains the contents of the shopping cart for each user. Apart from this, BookShoppingServlet contains additional methods to display the book-listing and shopping cart contents pages.
The Book class represents your domain object. Hence, it is used as a holder for storing the attributes of the books that you will be offering for purchase, such as the book ID, book title, price, and quantity to be purchased. The Book class has getter (also known as accessor) methods to retrieve and setter (also known as mutator) methods to set the attributes for a book.
The Utility class is a helper class that provides general-purpose functionality in a single location. For your application, the Utility class provides methods for currency format conversion, rounding, and so on.
The three classes for your application will be organized in different packages. From the package diagram in Figure 3.6, you can see that there will be two packages for your application:
com.sams.learnweblogic7.servlets com.sams.learnweblogic7.utils
Figure 3.6 Package diagram for the BookShoppingServlet.
The com.sams.learnweblogic7.servlets package will contain the BookShoppingServlet and the Book class. The com.sams.learnweblogic7.utils package will contain the Utility class.
The Example Servlet's Dynamic Behavior
The static design for your application is now in place. The structure and the organization of the application are complete. The next step is to understand the dynamic behavior of the application. For this, take a look at another UML diagram, the sequence diagram. The sequence diagram is a depiction of the flow of events that will occur in your application.
From Figure 3.7, you can see that all requests are initiated by the Browser class, which is a logical representation for the user's browser. The BookShoppingServlet is the only point of interaction for the Browser class. The interaction is initiated by calling the doGet() method of the BookShoppingServlet. The doGet() method performs the generation of the book-listing page and then kicks off the session-handling functionality to determine and define the user's session.
Figure 3.7 Sequence diagram for the BookShoppingServlet.
The other important interaction between the Browser and the BookShoppingServlet is the calling of the doPost() method by the Browser. This method is called when the user performs the "Add to Cart" action in the browser. The other interactions in the sequence diagram are between the BookShoppingServlet and the Book and Utility classes, which are kicked off within the primary doGet() and doPost() method calls.
Since the goal is a simple application, a lot of the functionality of the shopping cart is limited. One limitation is that the users will not be able to remove their selections from their shopping carts. A utopian shopping cart for businesses!
Session Handling: Hidden Variables
In this technique, the developer assumes responsibility for implementing the session- handling functionality. The servlet generates a random identification for a client. This can be as simple as a random number generated using the Java API. The servlet generates this identification when the logical client session is initiated, say, by logging in to your servlet application. The servlet writes this identification to every page served to the client browser until a logout is called by the client browser. On every request sent by the browser to the servletfor example, submission of data in an HTML formthe session identifier is also sent to the servlet. The servlet uses this identifier to maintain a logical session for the client. After processing the data, the servlet uses the same identifier and passes it as a hidden value in the HTML page sent to the browser as a response.
Since this technique is not tied to any server, it can be implemented for any application server. However, the developer bears responsibility for creating and maintaining the session. This technique is difficult to implement. Also, if the user refers to a page outside the application, then the context is lost and the session can break.
Sample Program
Now, go ahead and take a look at a sample program that implements this session- handling technique (see Listing 3.1).
Listing 3.1 BookShoppingServlet.java
/****************************************************************************** * Class Name:BookShoppingServlet * Extends:HttpServlet * Description:Shopping Cart Application Using Hidden Variables, * @author Mandar S. Chitnis, Lakshmi AM. @version 1.1 * Copyright (c) by Sams Publishing. All Rights Reserved. ******************************************************************************/ package com.sams.learnweblogic7.servlets; //import the utils package import com.sams.learnweblogic7.utils.*; //importing standard servlet packages import javax.servlet.*; import javax.servlet.http.*; //import io,util, and math packages import java.io.*; import java.util.*; import java.math.*; public class BookShoppingServlet extends HttpServlet { Book book1; Book book2; Book book3; PrintWriter out; private static final int DEFAULT_ZERO_VALUE = 0; private static final String EXAMPLE_TYPE = "Shopping Cart Using Hidden Variables"; /** * This method obtains the names of the books and their prices from the * initialization parameters, which are set in the web.xml file. * It then creates book objects which persist for the life of the servlet. * @param config A servlet configuration object used by a servlet * container used to pass information to a servlet during initialization * @exception ServletException Defines a general exception a servlet * can throw when it encounters difficulty. Extends java.lang.exception */ public void init(ServletConfig config) throws ServletException { super.init(config); //getting the init params String book1Id = config.getInitParameter("book1Id"); String book1Name = config.getInitParameter("book1Name"); double book1Price = new Double(config.getInitParameter("book1Price")).doubleValue(); String book1Image = config.getInitParameter("book1Image"); String book1Description = config.getInitParameter("book1Description"); book1 = new Book(book1Id,book1Name,book1Price,book1Image,book1Description); String book2Id = config.getInitParameter("book2Id"); String book2Name = config.getInitParameter("book2Name"); double book2Price = new Double(config.getInitParameter("book2Price")).doubleValue(); String book2Image = config.getInitParameter("book2Image"); String book2Description = config.getInitParameter("book2Description"); book2 = new Book(book2Id,book2Name,book2Price,book2Image,book2Description); String book3Id = config.getInitParameter("book3Id"); String book3Name = config.getInitParameter("book3Name"); double book3Price = new Double(config.getInitParameter("book3Price")).doubleValue(); String book3Image = config.getInitParameter("book3Image"); String book3Description = config.getInitParameter("book3Description"); book3 = new Book(book3Id,book3Name,book3Price,book3Image,book3Description); }//end of init /** * This method is called when the user enters the path * of the servlet in the address bar space * * or when he clicks on viewcart button. * * @exception ServletException Defines a general exception a servlet * can throw when it encounters difficulty. Extends java.lang.Exception * @exception IOException Signals that an I/O exception of some sort * has occurred. This class is the general class of exceptions, * produced by failed or interrupted I/O operations. * @param req The request object passed by the servlet container as an * argument to the servlet's doGet / doPost method * @param res The response object is created by the servlet container * and passed to the servlet's doGet/doPost method */ public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{ doPost(req, res); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{ res.setContentType("text/html"); out = res.getWriter(); String exampleType = "Book Shopping Cart using Hidden Variables"; writeHeaderZone(out,EXAMPLE_TYPE); doSessionUsingHiddenVariables(out, req); writeFooterZone(out); }//end of doPost public void doSessionUsingHiddenVariables(PrintWriter outputObject, HttpServletRequest req){ String clickedButton = (req.getParameter("buttonName") == null)?"":req.getParameter("buttonName"); if(clickedButton.equals("view")) { outputObject.println("<FORM name=\"viewcart\" -action=\"/ShoppingApp/BookShoppingServlet\" method=\"get\">"); outputObject.println("<B> <font face = \"Verdana\" color=\"blue\" size=-2> -<p align = \"right\">Shopping Cart using Hidden Variables</p> </font></B>"); int hiddenBook1Qty = Utility.getDefaultValue(req.getParameter("hiddenBook1Qty"),0); int hiddenBook2Qty = Utility.getDefaultValue(req.getParameter("hiddenBook2Qty"),0); int hiddenBook3Qty = Utility.getDefaultValue(req.getParameter("hiddenBook3Qty"),0); writeViewCartZone(outputObject, hiddenBook1Qty, hiddenBook2Qty, hiddenBook3Qty); outputObject.println("<p align =\"center\"> <INPUT type=\"submit\" name=\"buttonName\" value=\"Empty Shopping Cart\"></p>"); } else { outputObject.println("<FORM name=\"addToCart\" -action=\"/ShoppingApp/BookShoppingServlet\" method=\"post\">"); outputObject.println("<B> <font face = \"Verdana\" color=\"blue\" size=-2> -<p align = \"right\">Shopping Cart using Hidden Variables</p> </font></B>"); writeBookListZone(outputObject); outputObject.println("<CENTER><TABLE width = 100%><TR>"); writeHiddenVariable(outputObject, req); outputObject.println("<TD align = \"center\"> -<INPUT type=\"submit\" name=\"buttonName\" value=\"add\"></TD>"); outputObject.println("<TD align = \"center\"> -<INPUT type=\"submit\" name=\"buttonName\" value=\"view\"></TD>"); outputObject.println("</TR></TABLE></CENTER>"); } //close form tags outputObject.println("</FORM>"); } public void writeHiddenVariable(PrintWriter outputObject, HttpServletRequest req){ int hiddenBook1Qty = Utility.getDefaultValue(req.getParameter("hiddenBook1Qty"),0)+ Utility.getDefaultValue(req.getParameter("book1_qty"),0); int hiddenBook2Qty = Utility.getDefaultValue(req.getParameter("hiddenBook2Qty"),0)+ Utility.getDefaultValue(req.getParameter("book2_qty"),0); int hiddenBook3Qty = Utility.getDefaultValue(req.getParameter("hiddenBook3Qty"),0)+ Utility.getDefaultValue(req.getParameter("book3_qty"),0); outputObject.println("<INPUT type=\"hidden\" name = \"hiddenBook1Qty\" value = \""+hiddenBook1Qty+"\">"); outputObject.println("<INPUT type=\"hidden\" name = \"hiddenBook2Qty\" value = \""+hiddenBook2Qty+"\">"); outputObject.println("<INPUT type=\"hidden\" name = \"hiddenBook3Qty\" value = \""+hiddenBook3Qty+"\">"); } // ******** GENERIC FUNCTIONS **************// public void writeHeaderZone(PrintWriter outputObject, String title){ outputObject.println("<HTML><TITLE>"+title+"</TITLE>"); outputObject.println("<BODY><FONT face=\"Arial\" size=\"-1\">"); } public void writeFooterZone(PrintWriter outputObject){ outputObject.println("</FONT></BODY></HTML>"); } public void writeBookListZone(PrintWriter outputObject){ outputObject.println("<CENTER><B>Most popular books from Sams Publishing</B></CENTER><BR>"); outputObject.println("<TABLE width = 100%"); outputObject.println("<TR><TH> </TH> <TH>Book Name and Description</TH> <TH>Book Price</TH> <TH>No of Books to add to cart</TH></TR>"); outputObject.println("<TR bgcolor='#cccc99'> <TD><img src="+book1.getImagePath()+"></TD> <TD align='left'><b>"+book1.getBookName()+"</b> <BR>"+book1.getBookDescription()+"</TD> <TD align='center'>$"+book1.getBookPrice()+"</TD> -<TD align='center'><INPUT type='text' name='book1_qty' size=3></TD> </TR>"); outputObject.println("<TR bgcolor='#eeeeee'> <TD><img src="+book2.getImagePath()+"></TD> <TD align='left'><b>"+book2.getBookName()+"</b> <BR>"+book2.getBookDescription()+"</TD> <TD align='center'>$"+book2.getBookPrice()+"</TD> -<TD align='center'><INPUT type='text' name='book2_qty' size=3></TD> </TR>"); outputObject.println("<TR bgcolor='#cccc99'> <TD><img src="+book3.getImagePath()+"></TD> <TD align='left'><b>"+book3.getBookName()+"</b> <BR>"+book3.getBookDescription()+"</TD> <TD align='center'>$"+book3.getBookPrice()+"</TD> -<TD align='center'><INPUT type='text' name='book3_qty' size=3></TD> </TR>"); outputObject.println("</TD></TR></TABLE><BR>"); } public void writeViewCartZone(PrintWriter outputObject, int book1Qty, int book2Qty, int book3Qty){ double totBook1Price = Utility.getRoundedAmount(book1Qty * book1.getBookPrice()); double totBook2Price = Utility.getRoundedAmount(book2Qty * book2.getBookPrice()); double totBook3Price = Utility.getRoundedAmount(book3Qty * book3.getBookPrice()); double grandTotal = totBook1Price + totBook2Price + totBook3Price; outputObject.println("<TABLE width = 100%>"); -outputObject.println("<CENTER><B>Your Shopping Cart Contains:</B> </CENTER><BR>"); outputObject.println("<TR><TH> </TH> <TH>Book Name and description</TH><TH>Book Price</TH> <TH>Qty</TH><TH>Total for each book</TH></TR>"); outputObject.println("<TR bgcolor='#cccc99'> <TD><img src="+book1.getImagePath()+"></TD> <TDalign='left'><b>"+book1.getBookName()+"</b> <BR>"+book1.getBookDescription()+"</TD><TD align='left'> $"+book1.getBookPrice()+"</TD><TD align ='center'>"+book1Qty+"</TD><TD align='center'>$"+totBook1Price+"</FONT></TD></TR>"); outputObject.println("<TR bgcolor='#eeeeee'> <TD><img src="+book2.getImagePath()+"></TD> <TD align='left'><b>"+book2.getBookName()+"</b> <BR>"+book2.getBookDescription()+"</TD><TD align='left'> $"+book2.getBookPrice()+"</TD><TD align ='center'>"+book2Qty+"</TD><TD align='center'>$"+totBook2Price+"</FONT></TD></TR>"); outputObject.println("<TR bgcolor='#cccc99'> <TD><img src="+book3.getImagePath()+"></TD> <TD align='left'><b>"+book3.getBookName()+"</b> <BR>"+book3.getBookDescription()+"</TD><TD align='left'> -$"+book3.getBookPrice()+"</TD><TD align ='center'>"+book3Qty+"</TD> <TD align='center'>$"+totBook3Price+"</FONT></TD></TR>"); outputObject.println("<TR bgcolor='#eeeeee'><TD> </TD> <TD> </TD><TD> </TD><TD> </TD><TD align='left'> <b>Total = $"+grandTotal+"</b></FONT></TD></TR>"); outputObject.println("</TD></TR></TABLE>"); } }//end of BookShoppingServlet
In the init() method you initialize the book objects in the BookShoppingServlet with the initialization values that you will define for the servlet in web.xml during deployment. The init() method will be called only once by the WebLogic Server.
The doGet() method is the first method that is called by the browser. The doGet() method is responsible for generating the book-listing page and sending it to the browser. Since you are generating an HTML page, you need to set this MIME type in the response object. The doGet() method calls the method writeHeaderZone(), the session-handling method doSessionUsingHiddenVariables(), and the method writeFooterZone(). The writeHeaderZone() and writeFooterZone() methods are responsible for generating the static part of the application.
The doSessionUsingHiddenVariables() method is the critical piece in this sample program. This is where all the session-handling action happens!
The doSessionUsingHiddenVariables() method handles the functionality for displaying the shopping cart contents and for adding users' selections to their shopping cart. To determine whether the user clicked the View Cart button or the Add To Cart button, the method retrieves the value for the buttonName parameter from the request object. A null value identifies an Add To Cart action while a value containing view identifies a View Cart operation.
If the user action is Add To Cart, the existing shopping cart contents are retrieved if they are present. This shopping cart data is stored as hidden variables in the form. The user selections are also retrieved and added to the retrieved shopping cart contents. When the operation is initiated for the first time, the hidden variables do not have any values. In addition to data retrieval, the method generates the new book-listing page with the updated shopping cart details embedded in the page. This is done by calling the writeBookListZone() and writeHiddenVariable() methods. The writeHiddenVariable() method is responsible for propagating the shopping cart to keep the session alive. If this is not done, all session hand-ling, which is based on the hidden variables, will fall apart, and the user's shopping cart data will be lost! The writeHiddenVariable() makes extensive use of the Utility class.
If the user action is View Cart, the writeViewCartZone() method in the doSessionUsingHiddenVariables() method is called. The writeViewCartZone() method generates the View Cart HTML page and displays the contents of the user's shopping cart. The shopping cart data stored in the hidden variable is retrieved, and the quantity and total price is calculated for each book in the user's shopping cart. The writeViewCartZone() method makes extensive use of the Utility class to format the shopping cart data, such as the price and so on.
Compile the Program
Use the provided batch file compile.bat to compile the servlet. The batch file compiles the BookShoppingServlet.java, Book.java, and Utility.java files located in this directory in your domain:
applications\ShoppingApp\WEB-INF\classes\com\sams\learnweblogic7\servlets\
From Figure 3.8, you can see the command used for compiling is
javac .\ShoppingApp\WEB-INF\classes\com\sams\learnweblogic7\servlets\*.java
Figure 3.8 Setting the environment and compiling BookShoppingServlet.
The compile.bat file should be executed from the applications directory in your domain. If your Java files are in any directory other than this directory, the command should be modified to
javac d c:\bea\user_domains\mydomain\applications\ShoppingApp\WEB-INF\classes <yourSourceCodeDirectory>\*.java
TIP
If you get any compilation errors, they are probably due to an incorrect setting of the CLASSPATH. Check that the CLASSPATH is set properly to the BEA_HOME\JAVA_HOME\lib\dt.jar and the current directory.
To verify that the compilation was successful, confirm that the corresponding class files for the BookShoppingServlet.java, Book.java, and Utility.java were created in the directory
c:\bea\user_domains\mydomain\applications\ShoppingApp\WEB-INF\classes\com\sams\
learnweblogic7\servlets
as shown in Figure 3.9.
Figure 3.9 The directory structure.
Deploy the Program
After a successful compilation, the servlet needs to be deployed in the WebLogic Server environment. Now you will walk through the deployment activities.
Creating a .war Web Archive File
Servlets are Web applications. Hence, to deploy Web applications in WebLogic, the servlet classes need to be packaged in a Web archive file called a .war file. The Web archive file is actually a .jar file containing the servlet application classes and renamed with the .war extension. The .war file for deployment for WebLogic Server complies with the J2EE specification. To create a .war file, in a dos prompt, go to the root directory of your Web application:
c:\bea\user_domains\mydomain\applications\ShoppingApp
Then type the following command:
jar cv0f ShoppingApp.war .
You should see output similar to that shown in Figure 3.10.
Figure 3.10 Creating a Web archive for the BookShoppingServlet.
Registering Your Servlet in web.xml
Once the .war file containing your servlet is ready, you need to register your servlet with the WebLogic Server. To register your servlet, you need to edit the web.xml file in your deployment directory, which in this case is
c:\bea\user_domains\mydomain\applications\ShoppingApp\WEB-INF\
Add the following tags to web.xml:
<servlet> <servlet-name>BookShoppingServlet</servlet-name> -<servlet-classes>com.sams.learningweblogic7.servlets.BookShoppingServlet
</servlet-classes> </servlet>
The <servlet> </servlet> tags encapsulate the registration information for your servlet. Define the name for your servlet within the <servlet-name> </servlet-name> tags. The actual class filename qualified with the package name should be listed in the <servlet-classes> </servlet-classes> tags.
To register the name with which the servlet can be called from the browser, you can map the servlet to a URL. This can be done by adding the following tags to the web.xml file:
<servlet-mapping> <servlet-name>BookShoppingServlet</servlet-name> <url-pattern>/BookShoppingServlet/*</url-pattern> </servlet-mapping>
The preceding mapping enables you to execute the BookShoppingServlet by typing the /BookShoppingServlet name instead of com.sams.learnweblogic7.servlets.BookShoppingServlet. The contents of the web.xml file should be as shown in Figure 3.11.
Figure 3.11 Registering the servlet in the web.xml file.
Setting Initialization Parameters for Your Servlet in web.xml
Since your servlet needs the book information to be initialized, you need to define the initialization values in the web.xml file along with the registration of the servlet. You can do this by using <init-param> </init-param> tags embedded within the <servlet> </servlet> tags:
<servlet> ... <init-params> <param-name>book1> <param-value>XXX</param-value> </init-params> </servlet>
The contents of your weblogic.xml file should be similar to Figure 3.12.
Figure 3.12 Adding initialization parameters to the web.xml file.
Setting WebLogic ServerSpecific Parameters in weblogic.xml
If there are any WebLogic Serverspecific parameters, these are registered in the weblogic.xml file and not the web.xml file. In this example you do not need to change any settings specific to the WebLogic Server, and hence you need not modify weblogic.xml.
Execute the Program
The book listing page is invoked from the browser by calling the following URL:
http://localhost:7001/ShoppingApp/BookShoppingServlet
Figures 3.13 and 3.14 show the two screens generated by your servlet.
Figure 3.13 Book Listing screen of the servlet.
Figure 3.14 View Cart screen of the servlet.