Number Store Main Control Mechanism

Figure 13-2 shows the classes involved in the main Web page request and control mechanism. This diagram shows three «server page» classes, each of which is implemented as «JSP» components. The user session class is the J2EE HttpSession object and documents the key session state values used in this mechanism. The ScreenDefinition class, a simple wrapper of screen definitions, makes getting at all the discrete values of a single screen easy.

Figure 13-2. Class diagram of Number main control mechanism

The RequestProcessor class is implemented as a Java servlet. This class, which uses the user's session and a UseCaseCoordinator class, is the main entry point for all user HTTP requests, and is defined by the web.xml configuration file and the fragment from this file (Listing 13-1).

The overall flow of user request handling is described by the sequence diagram of Figure 13-3.

Listing 13-1 Fragment from the web.xml file

screenRequestProcessor centralServlet no description com.numberstore.util.RequestProcessor screenRequestProcessor *.scr

Figure 13-3. Sequence diagram showing the main page request scenario

The implementation for the RequestProcessor servlet follows. In this code, the main entry points and the operations that start the instance are the doGet() and the doPost() operations. These operations are called whenever a client browser requests a Web page with the extension .scr from the server. The doPost() operation delegates the processing to the doGet() operation, which is the main operation of the class.

In the doGet() operation, the UseCaseCoordinator is obtained from the user's session. If it is not there, a fresh one is created. The coordinator is asked for the current UseCaseController class. This controller is then asked to process the incoming HttpServletRequest. The controller manages the updating of business state in the entity tier. When finished, it is asked whether the scenario is completed. If it is, it is removed from the active controller stack, and the next controller on the stack is made current.

The current controller is checked to make sure that it doesn't require exclusive control, that is, won't allow another controller to be placed on top of it. If it is not exclusive, the incoming request is checked to see whether a new controller has been requested. If a new controller has been specified, it is made the current controller and given the chance to examine and to process the incoming request.

Finally, the controller is asked for the next screen ID that is set in the user session. The servlet then forwards the processing of the request, via the request dispatcher, to the ControlTemplate.jsp (Listing 13-2).

Listing 13-2 RequestProcessor source

package com.numberstore.util; import java.io.*; import java.text.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /** * The RequestProcessor is responsible for handling all incoming Http Requests for the * application. It delegates the actual processing of the request to UseCaseController * instances which themselves are managed by a UseCaseCoordinator instance. The Coordinator * is responsible for managing the collection of active use case controller instances. Once * the processing of the requests is completed the RequestProcessor forwards the request to a * controller JSP page, which uses information captured in the session to build the * appropriate response page. * @author jim conallen * @version 1.0 * @see UseCaseCoordinator * @see UseCaseController */ public class RequestProcessor extends HttpServlet { /** * The doGet method is the main point of logic for the request processor. * The doGet method handles all incoming page requests for the application. *

The basic flow of this method is to first get the UseCaseCoordinator instance * from the session, creating one and placing it in the session if necessary.

* *

Next the coordinator is asked for the active UseCaseController instance. With * this instance the system is updated with the incoming HttpServletRequest. The active * controller is responsible for updating the business state of the system. Next the * controller is asked if its task is completed. If so the controller is removed from * the coordinator's active stack.

* *

Next the request is examined to see if a new controller has been specified as a * parameter. If it has, then the coordinator is told to add the new controller to * the active controller stack. Then it is asked for the active controller.

* * @param request incoming request for a page resource. * @param response outgoing response. * @see UseCaseCoordinator * @see UseCaseController */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // general algorithm: accept request, update business state through // the current controller, then delegate to the template indicating // the next page. HttpSession session = request.getSession(); UseCaseCoordinator coordinator = (UseCaseCoordinator) session.getAttribute("coordinator"); if( coordinator == null ) { coordinator = new UseCaseCoordinator(); session.setAttribute("coordinator", coordinator ); } UseCaseController controller = coordinator.getActiveController(); controller.update( request ); if( controller.completed() ) { coordinator.removeController( controller ); } if( !controller.exclusive() ) { // if the active controller will allow a slight diversion to make // another controller the active one, and one has been requested, // then place it on top of the stack. String controllerName = request.getParameter("controller"); if( controllerName != null ) { // inform the coordinator that a new controller has been requested coordinator.addController( controllerName ); controller = coordinator.getActiveController(); // check to see if we've been asked to reset the controller first String reset = request.getParameter("reset"); if( reset != null ) { if( reset.equals("true") ) controller.reset(); } // prime the new controller with any necessary parameters controller.update(request); } } int nextScreenId = controller.nextScreen(); session.setAttribute("nextScreen", new Integer(nextScreenId) ); // make this controller available to the JSPs as the default controller session.setAttribute("controller", controller ); // if this is the last act of this controller then remove it from the // coordinator's stack. But leave it in the session to process this last // request. forward( "/ControlTemplate.jsp", request, response ); } /** * Overidden handler for post requests. All post requests are handled by doGet(). * Overidden handler for post requests. .All post requests are handled by doGet(). * @param request incoming request for a page resource. * @param response outgoing response. */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { doGet(request, response); } /** * Private method for redirecting the request to a JSP resource. * Private method for redirecting the request to a JSP resource. Uses the * RequestDispatcher for the redirection instead sendRedirect which would result in a new * client request, and most likely a stack overflow in the virtual machine. * @param url the relative url to pass control to. * @param req the incoming http request. * @param resp the outgoing response. */ private void forward(String url, HttpServletRequest req, HttpServletResponse resp ) throws IOException, ServletException { // The pathname specified may be relative, although it cannot extend outside the // current servlet context. If the path begins with a "/" it is interpreted as // relative to the current context root. This method returns null if the servlet // container cannot return a RequestDispatcher. RequestDispatcher rd = req.getRequestDispatcher(url); rd.forward(req, resp); } }

The ControlTemplate's only responsibility is to define some static operationsbound in <%! ... %> blocksthat make it easy to define new screens. Included are the screen definitions files that use the defined operations in this file before obtaining the ScreenDefinition instance that will be used by the PresentationTemplate (Listing 13-3).

The template includes, via the @include directive, a ScreenDefintions.jsp file, which contains definitions for all the screens in the application. Each screen definition calls the operation defined in ControlTemplate. This operation packages the screen definition in a ScreenDefinition wrapper class and adds them to a static HashMap (Listing 13-4).

The HashMap stores all the screen definitions and uses their IDs as the key (Listing 13-5).

Listing 13-3 ControlTemplate.jsp source

<%@ page language="java" contentType="text/html" %> <%@ page import="com.numberstore.util.*" %> <%@ page import="java.util.HashMap" %> <%! private HashMap ScreenDefinitions = new HashMap(); private void defineScreen( int screenId, String url, String name, String content, String banner, String footer, String menu) { ScreenDefinition screen = new ScreenDefinition(); screen.setUrl( url ); screen.addTemplateParameter( "name", name ); screen.addTemplateParameter( "content", content ); screen.addTemplateParameter( "banner", banner ); screen.addTemplateParameter( "footer", footer ); screen.addTemplateParameter( "menu", menu ); ScreenDefinitions.put( new Integer(screenId), screen );} %> <%-- include screen definitions --%> <%@ include file="ScreenDefinitions.jsp" %> <% Integer screenId = (Integer) session.getAttribute("nextScreen"); ScreenDefinition screen = (ScreenDefinition) ScreenDefinitions.get( screenId ); %> <%@ include file="PresentationTemplate.jsp" %>

Listing 13-4 ScreenDefinitions.jsp source

<% // definitions defineScreen( (int) 0, "/home.scr", "Home", "home.jsp", "banner.jsp", "footer.jsp", "" ); defineScreen( (int) 1, "/catalog.scr", "Catalog", "catalog/catalog.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 2, "/category.scr", "Category", "catalog/category.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 3, "/product.scr", "Product Information", "catalog/product.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 4, "/cart.scr", "Shopping Cart", "catalog/cart.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); // order screens defineScreen( (int) 5, "/checkout.scr", "Shopping Cart Summary", "order/checkout.jsp", "banner.jsp", "order/order_footer.jsp", "" ); defineScreen( (int) 6, "/paymentInformation.scr", "Payment Information", "order/payment.jsp", "banner.jsp", "order/order_footer.jsp", "" ); defineScreen( (int) 7, "/paymentConfirmation.scr", "Payment Confirmation", "order/payment_summary.jsp", "banner.jsp", "order/order_footer.jsp", "" ); defineScreen( (int) 8, "/invoice.scr", "Invoice", "order/invoice.jsp", "order/invoice_banner.jsp", "", "" ); // tutorial screens defineScreen( (int) 9, "/addFractionLesson.scr", "Adding Fractions", "tutorials/fractions/lesson.html", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 10, "/addFractionTryIt.scr", "Adding Fractions Try It!", "tutorials/fractions/tryit.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 11, "/addFractionCheckWork.scr", "Adding Fractions Check Work", "tutorials/fractions/checkwork.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 12, "/addFractionTest.scr", "Adding Fractions Test", "tutorials/fractions/test.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 13, "/addFractionTestResults.scr", "Adding Fractions Test Results", "tutorials/fractions/testresults.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 14, "/addFractionCertificate.scr", "Adding Fractions Certificate", "tutorials/fractions/certificate.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 15, "/polynomialLesson.scr", "Graphing Polynomials Lesson", "tutorials/polynomial/lesson.html", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 16, "/polynomialGraphing.scr", "Polynomial Graphing", "tutorials/polynomial/graphing.html", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 17, "/polynomialTest.scr", "Polynomial Graphing Test", "tutorials/polynominal/test.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 18, "/polynomialTestResults.scr", "Polynomial Graphing Test Results", "tutorials/polynominal/testresults.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); defineScreen( (int) 19, "/polynomialCertificate.scr", "Polynomial Graphing Certificate", "tutorials/polynominal/certificate.jsp", "banner.jsp", "footer.jsp", "menu.jsp" ); %>

Listing 13-5 PresentationTemplate.jsp source

<% String banner = screen.getTemplateParameter( "banner" ); String content = screen.getTemplateParameter( "content" ); String footer = screen.getTemplateParameter( "footer" ); String menu = screen.getTemplateParameter( "menu" ); String screenName = screen.getTemplateParameter("name" ); %>

The Number Store - <%=screenName%>

<% if( !banner.equals("") ) { %> <% } %> <% if( !menu.equals("") ) { %> <% } %>

   

<% if( !footer.equals("") ) { %> <% } %>

Debug/Development Information

Screen Information

Query String: <%= request.getQueryString() %>

Screen: <%=screenName%>

Screen Id: <%=screenId%>

Screen URL: <%= screen.getUrl() %>

<% if( menu.equals("") ) { %> <% } else { %> <% } %> <% if( ! footer.equals("") ) { %> <% } %>
<a href="sourcecode.jsp?source=<%=banner%>" target="jspsource"><%=banner%></a> 
<a href="sourcecode.jsp?source=<%=content%>" target="jspsource"><%=content%></a> 
<a href="sourcecode.jsp?source=<%=menu%>" target="jspsource"><%=menu%></a>  <a href="sourcecode.jsp?source=<%=content%>" target="jspsource"><%=content%> </a> 
<a href="sourcecode.jsp?source=<%=footer%>" target="jspsource"><%=footer%></a> 
<%@ page import="java.util.Enumeration" %>

Session Attributes

<% for (Enumeration names = session.getAttributeNames(); names.hasMoreElements(); ) { String name = (String) names.nextElement(); Object value = session.getAttribute(name); %> <% } %>
<%=name%> <%=value.toString()%> 
<%@ page import="com.numberstore.util.*" %>

Controller stack

<% UseCaseCoordinator coordinator = (UseCaseCoordinator) session.getAttribute("coordinator"); UseCaseController controllers[] = (UseCaseController[]) coordinator.getActiveControllers(); for(int i=controllers.length-1; i>=0; i--) { UseCaseController controller = controllers[i]; int nextScreen = controller.nextScreen(); String instance = controller.toString(); boolean completed = controller.completed(); %> <% } %>
Instance nextScreen completed
<%=instance%> <%=nextScreen%> <%=completed%>

The presentation template is the main template for all outgoing pages. This JSP maintains the overall application look-and-feel. The presentation template gets the current screen object from the user session and uses it to determine which JSP components will be used to fill the known compartments.

The presentation template itself doesn't do much more than define major screen components. Any one of these compartments is filled by individual JSPs.

The home.jsp is used to fill the main compartment of the Home screen. Because the compartments in this application are organized with HTML table tags, the individual JSPs that fill compartments do not specify

, and tags (Listing 13-6).

Listing 13-6 The home.jsp source

<%@ page import="com.numberstore.catalog.*" %> <% ShopController controller = (ShopController) session.getAttribute("controller"); Product special = controller.getProductSpecial(); int specialCategoryId = special.getCategoryId(); int specialProductId = special.getId(); String specialName = special.getName(); String specialDescription = special.getDescription(); String specialPrice = special.priceString(); String specialUrl = response.encodeURL("product.scr?cat&prod100%">

Welcome to the Number Store!

This fictional eRetail store offers for virtual sale some of the best and most interesting numbers on the market today. The idea for the store is driven by two major factors. Firstly I needed a sample J2EE application with sufficient complexity to illustrate how to properly model it with UML and the Web Application Extension (WAE). Secondly, I needed something easy to build and something that didn't require a whole lot of additional software (EJB containers, databases, etc.).

A more detailed description of the architecture of this application can be found <a href="documentation/index.html" target="NumberStoreArch">here</a>.     <a href="<%=specialUrl%>">Today's Special: <%=specialName%></a>

<%=specialDescription%> <%= specialPrice %>

<a href="<%= response.encodeURL( ">">View our Catalog</a>

The general strategy for all JSP components is to first get the current controller from the session and query it for all the dynamic content that fills the page. This keeps all access to the business state from Web pages located at a single source.

Категории