Example JSP Web Application
This section will cover all of the code required to produce a sample web application. You’ll look at all of the code required to put together a simple web application as well as the information needed to deploy the completed application to a web container (in this example, Tomcat 5.0).
The Store
The example used in this section is the ubiquitous web-store application. You’ll see a front page, a shopping cart, and a checkout page.
After you’ve looked at the code for the application, you’ll examine the deployment descriptor for the application.
The application will be written in the JSP model 2 style , beginning with the controller.
| Note |
Bear in mind that this is a very simplistic application. Over the course of this book, the examples will grow gradually more sophisticated. |
The Controller Servlet
The Controller servlet (FrontController.java) coordinates the behavior of our store:
package com.apress.projsp20.ch02.store;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class FrontController extends HttpServlet {
public void init() throws ServletException {
HashMap products = new HashMap();
Product p = new Product(1, "Dog", "9.99");
products.put("1", p);
p = new Product(2, "Cat", "4.99");
products.put("2", p);
p = new Product(3, "Fish", "1.99");
products.put("3", p);
//Store products in the ServletContext
getServletContext().setAttribute("products", products);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// load the action
String name = request.getPathInfo().substring(1);
String viewName = “/error.jsp”;
try {
name = “com.apress.projsp20.ch02.store.” + name;
Class c = getClass().getClassLoader().loadClass(name);
Action action = (Action) c.newInstance();
viewName = action.process(request, response);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
RequestDispatcher dispatcher = request.getRequestDispatcher(viewName);
dispatcher.forward(request, response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
}
As you can see, this servlet is fairly simple. Its primary job is to receive a request, work out how to process it, delegate the processing to an appropriate class, and forward it on to the next JSP page in the store. This is achieved by reading the request path and attempting to instantiate a class that matches the last part of it. For example, if the request is http://localhost:8080/store/servlet/DummyAction, the servlet will attempt to instantiate a class called DummyAction in the current package. Once this class has been instantiated it will call the method process on it, passing in the current request and response objects. This relies on the class implementing a known interface called Action. This interface is simple, containing only one method, as follows:
package com.apress.projsp20.ch02.store;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action {
/**
* Peforms the processing associated with this action.
*
* @param request the HttpServletRequest instance
* @param response the HttpServletResponse instance
* @return the name of the next view
*/
public abstract String process(HttpServletRequest request,
HttpServletResponse response)
throws ServletException;
}
It is the job of implementations of this interface to process the current request and return the name of the next page to forward it on to. Our store has several implementations of this interface, which will all be shown and explained in this section.
Getting back to the controller, you can see that it also performs another job. The init() method of the servlet creates a collection of Product objects that are placed into the ServletContext. This would not normally be done, because the products would be obtained from somewhere such as a database or configuration file. We have done it this way to avoid cluttering up the code with data-access code. Product is just an object that holds the basic data for each item:
package com.apress.projsp20.ch02.store;
public class Product {
private String name;
private String price;
private int id;
public Product(int id, String name, String price) {
this.price = price;
this.name = name;
this.id=id;
}
public String getPrice() {
return this.price;
}
public String getName() {
return this.name;
}
public int getId() {
return this.id;
}
public String toString() {
return "Product:id=" + id + " name=" + name + " price=" + price;
}
}
The Store Actions and JavaServer Pages
Now we have the base for our actions and model, let’s flesh it out by combining them with the view.
MainAction
The Main action in our store is implemented (surprisingly) by a class called MainAction. This class implements the Action interface that you saw earlier. This action, the code of which is shown here, has no other purpose at this time than to forward onto the main page in this site, main.jsp.
package com.apress.projsp20.ch02.store;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
public class MainAction implements Action {
public String process(HttpServletRequest request, HttpServletResponse response)
throws ServletException {
return "/main.jsp";
}
}
The main page for this site, main.jsp, is shown here. This page is very simple and displays the list of products available to purchase:
<%@ page import="java.util.*,com.apress.projsp20.ch02.store.*" %>
<%
HashMap products = (HashMap) application.getAttribute("products");
// List the products, clickable to add to cart
Iterator it = products.values().iterator();
out.println("<table>");
while (it.hasNext()) {
out.println("<tr>");
Product product = (Product) it.next();
%>
<td>
<a href='CartAction?add=true&id=<%=product.getId()%>'><%=product.getName()%></a>
</td>
<td>
<%=product.getPrice()%>
</td>
</tr>
<%}%>
</table>
You display the products by getting the previously created hashtable from the ServletContext (this was created by the controller servlet that you read about earlier). You then loop through hashtable, writing each product out into an HTML table row. Each product name is rendered as a hyperlink so that you can add it to the shopping cart that you’ll read about next.
This main page has a header and footer included so that you can simply change the style of the page without changing the code that displays products. This is achieved using the <include-prelude> and <include-coda> subelements of the <jsp-config> deployment-descriptor element. The following excerpt shows the deployment-descriptor elements that are required:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<include-prelude>/header.jsp</include-prelude>
<include-coda>/footer.jsp</include-coda>
</jsp-property-group>
</jsp-config>
This includes the file /header.jsp as the page header and the file /footer.jsp as the page footer. These are included for every JSP page within the system.
The header simply contains a basic header and heading, as follows:
<html>
<head>
<title>The Store</title>
</head>
<body>
<h1>Welcome to the Apress Store</h1>
<br>
While the footer closes the body and adds some navigation links, as shown here:
<br>
<table>
<tr>
<td><a href="CartAction?add=false">Display Cart</a></td>
<td><a href="CheckOutAction">Check Out</a></td>
</tr>
</body>
</html>
Cart Action
The cart in your store holds only one type of each product. This is primarily so that you can illustrate error pages for your sample application. The code that implements your cart is another action, CartAction.java, and a simple Java class. Cart.java is shown here:
package com.apress.projsp20.ch02.store;
import java.util.*;
public class Cart {
private HashMap items = new HashMap();
public Cart() {
}
public Iterator getItems() {
return items.values().iterator();
}
public void addItem(Product product) throws ItemAlreadyAddedException {
Integer id = new Integer(product.getId());
if (this.items.containsKey(id)) {
throw new ItemAlreadyAddedException();
}
this.items.put(id, product);
}
}
The following code shows the Action that handles the web operations on the shopping cart, called CartAction.java:
package com.apress.projsp20.ch02.store;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CartAction implements Action {
public String process(HttpServletRequest request, HttpServletResponse response)
throws ServletException {
// Check to see if you are adding to the cart or
// if you want to display the cart
String adding = request.getParameter("add");
// Get the cart if it exists
HttpSession session = request.getSession();
Cart cart = (Cart) session.getAttribute("cart");
if (cart == null) {
cart = new Cart();
}
if (adding.equalsIgnoreCase("true")) {
// Add to it
addToCart(request, cart);
}
return “/cart.jsp”;
}
private void addToCart(HttpServletRequest request, Cart cart)
throws ItemAlreadyAddedException {
// Get the item to add from the request
// Get the products from the ServletContext
HashMap products = (HashMap)request.getSession().getServletContext().
getAttribute(”products”);
// Find the one represented by the ID that you passed in
try {
String id = request.getParameter(”id”);
Product p = (Product) products.get(id);
System.out.println(p);
// Add it to the cart
cart.addItem(p);
// Add the cart to the session
request.getSession().setAttribute(”cart”,cart);
} catch (NumberFormatException nfe) {
throw new ItemAlreadyAddedException();
}
}
}
In this action CartAction performs two tasks: It both adds to the shopping cart and displays the cart.
The first thing done in this action is to retrieve the shopping cart from the user’s session. If there is no cart in the session, a new Cart object is created as follows:
Cart cart = (Cart) session.getAttribute("cart");
if (cart == null) {
cart = new Cart();
}
Next, this method decides what to do to the cart based on a parameter (called adding) passed to the action in the HttpServletRequest. If the parameter contains the value true then you call the method addToCart(). If the adding parameter is false then you simply redirect to the cart.jsp page. This method looks for another parameter in the HttpServletRequest called id. It then looks for this product in the list of products that you placed into the ServletContext in the initialization, and adds it to the cart object. As mentioned earlier, a cart can only contain one of each product. If the user tries to add more than one of the same item to the cart, an ItemAlreadyAddedException is thrown. As you’ll see later, the web container catches this exception and a special page is shown. Once the item is added to the cart (or if the adding parameter is false) the cart is written out to the user’s browser by redirecting to the cart.jsp page. This page is shown here:
<%@page import=”java.util.*,com.apress.projsp20.ch02.store.Cart, com.apress.projsp20.ch02.store.Product”%> <%Iterator items = ((Cart)session.getAttribute(”cart”)).getItems();%> <h1>Current Cart Contents:</h1> <table> <%while (items.hasNext()) {%> <tr> <%Product p = (Product)items.next();%> <td><%=p.getName()%></td> <td><%=p.getPrice()%></td> </tr> <%}%> </table>
Checking Out
Obviously, a store is only useful if you can actually buy the products that you put into your cart. In the example store, the CheckOutAction handles this. Obviously, being an example, you cannot really buy the items. The checkout process in our store simply displays the contents of the cart and gives the user a Confirm button. The code for the CheckOutAction is as follows:
package com.apress.projsp20.ch02.store;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CheckOutAction implements Action {
public String process(HttpServletRequest req, HttpServletResponse res)
throws ServletException {
return "/checkout.jsp";
}
}
The action simply redirects the user to the checkout.jsp page as shown here:
<jsp:include page="cart.jsp" />
<br>Please Click Confirm to check out<br>
<form action='ConfirmAction'><input type='submit' value='Confirm'></form>
This page includes the content of the cart.jsp page to display the cart to the user and presents it with a Confirm button. This will invoke the ConfirmAction.
ConfirmAction
The confirm action in our store simply redirects to a confirmation page. In a real store this might do credit-card processing or any number of other things. The code for this action is shown here:
package com.apress.projsp20.ch02.store;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
public class ConfirmAction implements Action {
public String process(HttpServletRequest request, HttpServletResponse response)
throws ServletException {
return "/confirmed.html";
}
}
The ConfirmAction simply redirects you to an HTML file to tell the user their order has been confirmed. In a real store you would output a receipt or something like it at this stage.
Having seen all of the important code for the simple store application, you’ll now look at the deployment descriptor required to deploy this application.
The Deployment Descriptor
Now it’s time to deploy the application. The following is the deployment descriptor that you’ll be using to do this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"
version="2.4">
<servlet>
<servlet-name>FrontController</servlet-name>
<servlet-class>
com.apress.projsp20.ch02.store.FrontController
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<jsp-config>
<jsp-property-group>
<url-pattern>/*</url-pattern>
<include-prelude>/header.jsp</include-prelude>
<include-coda>/footer.jsp</include-coda>
</jsp-property-group>
</jsp-config>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<error-page>
<exception-type>
com.apress.projsp20.ch02.store.ItemAlreadyAddedException
</exception-type>
<location>/duplicateItem.html</location>
</error-page>
</web-app>
This deployment descriptor contains all of the elements to build the application and allow it to be deployed into any J2EE-compliant web container.
The first thing that you set up is the controller servlet. This servlet processes all requests beginning /servlet that are received by the web application. This is achieved by using two different deployment-descriptor elements, including the <servlet> element:
<servlet>
<servlet-name>FrontController</servlet-name>
<servlet-class>
com.apress.projsp20.ch02.store.FrontController
</servlet-class>
<load-on-statup>1</load-on-statup>
</servlet>
and the <servlet-mapping> element:
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
This means that the controller servlet will be requested using a path similar to the following:
http://localhost:8080/store/servlet/ActionName
where ActionName is the name of the class that implements the action that you wish to process our request.
Once you’ve mapped the controller servlet, you declare the pages to be included as the header and footer of each page in the application. This is achieved using the following elements:
<jsp-config>
<jsp-property-group>
<url-pattern>/*</url-pattern>
<include-prelude>/header.jsp</include-prelude>
<include-coda>/footer.jsp</include-coda>
</jsp-property-group>
</jsp-config>
This says that for every page (shown by the /* in the <url-pattern> element) in the application, you’ll include /header.jsp at the start and /footer.jsp at the end. This allows you to change the appearance of the site at deployment time.
You then have the welcome file for the application:
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
This index page just ensures that you can direct the user to the MainAction without the user having to know the complex URL required.
Next you define an error page for the application. This page is invoked when a specified error occurs in any of the parts of the application. The <error-page> element in the deployment descriptor that does this is shown here. More information about this mechanism can be found in the earlier discussion of error codes.
<error-page>
<exception-type>
com.apress.projsp20.ch02.store.ItemAlreadyAddedException
</exception-type>
<location>/duplicateItem.html</location>
</error-page>
This definition says that whenever the exception com.apress.projsp20.ch02.store.ItemAlreadyAddedException is thrown, the page duplicateItem.html is shown. If you recall, we have said that only one item of a specific type can be added to the user’s shopping cart. If this constraint is violated, then the previous exception is thrown and the user is presented with a page telling her that she cannot add duplicate items to their cart.
Deploying the Application
Now we’ll demonstrate how to deploy the completed and archived application to Tomcat.
| Note |
The files for this example and the earlier examples, along with the respective WAR files, are available for download from our website. |
First you must create a web application archive (WAR) file containing the application. A WAR file is just like a JAR file except that the files must be located in specific directories and it’s created using the jar utility. Therefore, before you can create the WAR file, you must place all the files in the correct directories (this is the same structure that you saw earlier when you deployed directly to Tomcat):
-
store is the root directory. When you deployed to Tomcat, this was the name of the web application, but when you deploy as a WAR it’s the name of the WAR file that becomes the application’s name (in Tomcat). Under this directory all the JSP files (main.jsp, cart.jsp, checkout.jsp, error.jsp, header.jsp, and footer.jsp) and HTML files (index.html, confirmed.html, and duplicateItem.html) should be located.
-
WEB-INF is the directory indicating that this is a web application—the directory name is case sensitive.
-
web.xml is the deployment descriptor, which sits inside the WEB-INF folder.
-
classes is the directory where you store all of the class files in appropriate subdirectories for the package structure, for example, com/apress/projsp20/ch02/store/ (as shown here) will contain Action.class, Cart.class, CartAction.class, CheckOutAction.class, ConfirmAction.class, FrontController.class, ItemAlreadyAddedException.class, MainAction.class, and Product.class.
Once the files are in their respective folders, run the following command from the command prompt from the root folder as follows:
store>jar -cvf store.war .
This will create a WAR file in the same directory that contains all of the items in the application.
Deploying with Tomcat
Deploying this application with Tomcat is simplicity itself. For basic deployments, all you need to do is copy your WAR file into the webapps directory of your Tomcat installation. It’s located at %TOMCAT_HOME%\webapps. Now restart Tomcat. When Tomcat starts, it automatically detects the WAR file and explodes it, which also creates a \META-INF folder with a MANIFEST.MF file within it.
To deploy the example in a more advanced way, you can follow these steps:
-
Copy your WAR file to the webapps directory of your Tomcat installation. This step is voluntary, but it’s sensible to keep your web-application deployments in the same place.
-
Add an entry to the server.xml file. This file is located in the %TOMCAT_HOME%\conf directory. The line that you should add to deploy the store application is as follows:
<Context path="/store" docBase="/store.war" reloadable="true"/>
This line declares a context to exist with its base URI being /store (this can be any valid path.) This context is fulfilled by the application at /store.war. You are passing another parameter when creating the context.
If the reloadable parameter is set to true then Tomcat monitors classes in /WEB-INF/classes and /WEB-INF/lib for changes and automatically reloads the web application if a change is detected. This feature is useful during application development, but it requires significant runtime overhead and isn’t recommended for use on deployed production applications.
There are many other parameters that can be passed. Most of these are beyond the scope of this chapter, but it would be very useful to enable more control over how your application is deployed. Details of these parameters can be found at http://jakarta.apache.org/tomcat/.
-
Start Tomcat, and your application will be deployed. It can now be accessed at http://localhost:8080/store/. The home page contains a link that passes you through to the main page at http://localhost:8080/store/servlet/MainAction:
-
If you make changes to the application, create a new WAR file, copy it over the old one, and Tomcat will load the new version when restarted.
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.










Comments
No comments yet.
Leave a comment