Building the Forum Using Model 2 Architecture

 < Free Open Study > 



Model 2 architecture accepts requests through a controller component where it can service the request itself or delegate this task to some other component. The task of rendering the response is then dispatched to the appropriate view component. There are of course various ways in which this can be implemented. We're going to look at four of these design patterns:

  • Front Controller

  • Intercepting Filter

  • View Helper

  • Service to Worker

Using the Front Controller Pattern

Our current discussion forum has many views. System services (for example, data retrieval) and business logic are intermingled within the views, leading to a decrease in maintainability and a reduction in reusability. JSP pages are often developed by two types of people: web designers (who handle the look and feel of the site) and J2EE developers (who take care of the business logic). Placing too much Java code into the page usually makes it more difficult for web designers to modify the look and feel as they battle with Java code that they might not understand. For example, the logic that retrieves a Topic instance from the "database" based upon a topic id, or the business rule that says only the author of a response can delete that response.

The other feature of our current implementation is that all of the links to other pages are hard-coded within the pages themselves. Should we need to change the flow of the system in any way, we'll have to resort to modifying a number of JSP pages.

Ideally we would like to place all of this logic in a single location to help us ensure that this type of functionality is consistent, maintainable and extensible in the future. The front controller pattern can help us to achieve these goals. Here's how it fits into the Model 2 (programming-centric) architecture:

click to expand

This is exactly the same as the Model 2 architecture diagram that we saw towards the start of the chapter where a controller component is the entry point for web requests. The controller performs some processing of the request and may make changes to the underlying model. The task of rendering the response is then dispatched to a view component.

A front controller is also a good place in which to centralize services such as error handling and logging. Centralizing system services and the flow between pages has many benefits as it moves business logic and other system level service code out of JSP scriptlets, back into reusable components, therefore promoting reusability of functionality across different types of requests, as we'll be seeing shortly.

There are various strategies by which a front controller can be implemented, with the typical strategy focusing on the use of a servlet. There are several reasons why this role is better implemented using a servlet than a JSP. As we've noted before, outputting content back to the client is not an ideal use for a servlet because it involves writing lots of print statements - effectively tying together the content and the logic. JSP pages, on the other hand, are much better suited to delivering content, because they are written as content containing small bits of logic wherever necessary. As a controller component doesn't actually deliver any content itself, implementing it as a JSP results in a page containing no content - just the logic required to process the requests. For this reason, a servlet is the preferred strategy.

Of course centralizing all of this functionality can lead to a large, bloated controller component that has responsibilities for the entire web application. There are a number of ways in which this problem can be solved, one of which is to have several front controllers, each responsible for particular area of the site. For example, an e-commerce site might have one controller responsible for servicing all requests related to products and another to service all payment requests. Another solution is to use the Command and Controller implementation strategy we will discuss shortly.

Implementing the Front Controller Pattern

For the moment, here is an example skeleton implementation of the front controller pattern.

    package forum;    import java.io.IOException;    import javax.servlet.*;    import javax.servlet.http.*;    public class FrontController extends HttpServlet {      protected void processRequest(HttpServletRequest req,                                    HttpServletResponse res)                                    throws ServletException, IOException {        RequestDispatcher dispatcher =          getServletContext().getRequestDispatcher("name of view component");        dispatcher.forward(req, res);      }      protected void doGet(HttpServletRequest req, HttpServletResponse res)          throws ServletException, IOException {        processRequest(req, res);      }      protected void doPost(HttpServletRequest req, HttpServletResponse res)          throws ServletException, IOException {        processRequest(req, res);      }    } 

In this case, the front controller is simply an extension of HttpServlet with default implementations of the doGet() and doPost() methods that delegate processing of the request to another method called processRequest(). This is done to ensure that no matter how the request is made, the front controller will service it.

We've left out the majority of the body of the processRequest() method, but essentially a front controller will perform some processing that is associated with a request, and then dispatch to a view component to render the response. The view components are typically JSP pages. Once the controller has completed performing its business logic, it can dispatch to a JSP page through the RequestDispatcher.

At this point, you may have a question. If the controller is to be responsible for handling all requests, how does it know what the request is and therefore how to handle it?

The Command and Controller Strategy

In the Command and Controller strategy the logic to handle each specific request is moved out into a separate component. This architecture is illustrated below:

click to expand

Each of these extra components represents a specific command (or action), and the component encapsulates the logic to perform that action.

The front controller delegates the handling of requests to the appropriate command component, which may modify the state of the underlying model. Once the command component has completed its work, the controller again dispatches the task of rendering the response to the appropriate view component. But how does the controller know which command component to use? We'll tackle this issue in a moment.

Action Classes

The first thing we need to do to implement the Command and Controller strategy is to define the interface between the controller and action components. We can do this either by creating an interface or an abstract class. For the purposes of our example, we'll define an abstract class that all actions in our web application must extend (this means that we have a single place in which to add common functionality for all actions in the future):

    package forum;    import javax.servlet.http.*;    public abstract class Action { 

Subclasses will implement their specific business and processing logic in the process() method. To ensure that the action classes have access to the same environment and information as servlets, the process() method takes a reference to the HTTP Request and Response objects in the same way that the processRequest() method in the FrontController class did. Once the processing has been completed, the action can return a string to identify the view component to which the controller should dispatch to next:

      public abstract String process(HttpServletRequest request,        HttpServletResponse response);    } 

Defining the interface between the controller and the action components helps to decouple them from each other, which means that we can change the controller or the actions without affecting the other.

The next stage is to put together the logic that will figure out what the request is and delegate the processing to the appropriate action component.

Communicating the Type of Request

There are a number of ways that the type of request can be communicated to the controller servlet, most of which are focused around passing parameters to the servlet over HTTP (in a similar way to how we pass the topic id to the view-topic.jsp page). The problem with sending additional parameters to indicate the type of request is that the URLs need to be written carefully - after all, it is easy to misspell parameter names and get the query string syntax wrong.

Another mechanism would be to use a string that represented the type of request that we wanted to perform and pass this to the servlet as additional path information. We already know how mappings can be defined between a servlet and a URI via the web.xml file. An example of this is shown below which defines a mapping between our FrontController Servlet and the URI /controller/*:

    <?xml version="1.0" encoding="ISO-8859-1"?>    <!DOCTYPE web-app        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"        "http://java.sun.com/dtd/web-app_2_3.dtd">    <web-app>      <servlet>        <servlet-name>FrontController</servlet-name>        <servlet-class>forum.FrontController</servlet-class>      </servlet>      <servlet-mapping>        <servlet-name>FrontController</servlet-name>        <url-pattern>/controller/*</url-pattern>      </servlet-mapping>    </web-app> 

Now we can make calls to the controller by appending controller/ViewTopic?id=0 to our URL. Here, ViewTopic is the additional path information (also known as path info) and represents the type of request we are making. The query string can be used as usual, with id=0 providing the parameters for our specific request to view a topic. This is much cleaner than adding additional parameters to indicate the type of request. Instead, this information is now part of the URL that we use. With this side of the request figured out, how does the controller know what do, and where does this logic reside?

What we need to do here is to map the string that we obtain from the additional path info onto a specific action instance. Once again there are many strategies for this, with the most flexible being externalizing this mapping, through an XML file for example. Other examples here include hard coding the mapping inside the controller, or perhaps using a properties file.

For simplicity, in our example we'll build a separate component in which this mapping will be encapsulated. In a production application, hard coding such configuration information means that changes require the appropriate Java class to be modified, recompiled, and redeployed. In other words, it decreases the maintainability. However, hard coding this information into a separate component will suffice for our simple example and we'll call this class our ActionHelper. The mapping information is stored in a HashMap. Here's the code listing for this class:

    package forum;    import java.util.HashMap;    public class ActionHelper {      private static HashMap actions = new HashMap();      static {        actions.put("ViewTopic", "forum.ViewTopicAction");        actions.put("Login", "forum.LoginAction");        actions.put("Logout", "forum.LogoutAction");        actions.put("NewResponse", "forum.NewResponseAction");        actions.put("ProcessNewResponse", "forum.ProcessNewResponseAction");        actions.put("DeleteResponse", "forum.DeleteResponseAction");      } 

The ActionHelper class effectively maintains a mapping between request names (or types) and the fully qualified class names of the classes that can process the requests. Given the name of a request, the static getAction() method returns an instance of an Action class that can be used for processing:

      public static Action getAction(String name) {        Action action = null;        try {          Class c = Class.forName((String)actions.get(name));          action = (Action)c.newInstance();        } catch (Exception e) {          e.printStackTrace();        }        return action;      }    } 

The next step is to plug all of this into the processRequest() method of our FrontController as this will be the single entry point for all requests in our web application:

    package forum;    import java.io.IOException;    import javax.servlet.*;    import javax.servlet.http.*;    public class FrontController extends HttpServlet {      protected void processRequest(HttpServletRequest req,                                    HttpServletResponse res)                                    throws ServletException, IOException { 

Here we find which action should be used:

        String actionName = req.getPathInfo().substring(1); 

Now we use the helper class to locate the action:

        Action action = ActionHelper.getAction(actionName); 

The next step is to process the action, so that we can find out which view to show the user next:

        String nextView = action.process(req, res); 

Finally we redirect to the appropriate view:

        RequestDispatcher dispatcher =           getServletContext().getRequestDispatcher(nextView);        dispatcher.forward(req, res);      }      protected void doGet(HttpServletRequest req, HttpServletResponse res)          throws ServletException, IOException {        processRequest(req, res);      }      protected void doPost(HttpServletRequest req, HttpServletResponse res)          throws ServletException, IOException {        processRequest(req, res);      }    } 

With the code to lookup actions and process requests encapsulated elsewhere, our controller servlet is fairly minimal. In fact all of the components that we have implemented as part of the Command and Controller strategy have been fairly lightweight. This is good from both a maintainability and reusability perspective as small components are generally easier to maintain (and bug fix) and are also more likely to be reusable elsewhere. The framework that we've built is also extensible because new request handlers (actions) can easily be built and added to the system.

The Apache Struts Framework

The implementation of the Front Controller pattern presented here is fairly simple and straightforward but there are many improvements that can be made. One such example is externalizing the mapping between action names and the Action classes that are going to service the request. Although such work isn't particularly complex, it is still time consuming nonetheless. For this reason, there are number of third-party frameworks available for developers to use as a starting point for their web applications. Once such example is Struts from the Jakarta Project (http://jakarta.apache.org).

Struts is an open source framework providing an implementation of not only a front controller, but a complete implementation (or framework) of the Model 2 architecture. The key components are similar to those presented here, including for example, a controller servlet (called ActionServlet), Action classes in which to place functionality for processing a request and ActionBean classes in which to encapsulate data coming in from a request. In addition to this, Struts also contains a comprehensive collection of JSP tag libraries for the easy assembly of HTML based forms and JSP pages in general.

One of the most important features of Struts is that pretty much everything about it is externally configured through the struts-config.xml file. This includes the mappings between action names and Action classes (called ActionMappings) and also a concept called ActionForwards.

For the simple example presented here, the Action classes return a string representing the URI of the JSP that should be displayed next. In Struts, however, the Action classes return a symbolic name (wrapped up in an ActionForward instance) representing which view component should be displayed next. As an example, consider a login form containing a username and password. On submission of the form, the login is either successful or not. In this case, the Action class could return symbolic names such as success or failure. These symbolic names are then configured in the struts-config.xml file and it's here that the physical URI to the appropriate JSP is specified. This is a very powerful feature and is yet another way in which the flow and structure of the web application can be taken out of the code to increase maintainability. After all, if the structure of the site changes, only the configuration file need to change.

A full explanation of Struts is beyond the scope of this book, but a more detailed study of this framework can be found in the following titles: Professional JSP, 2nd Edition (ISBN 1-861004-95-8) and Professional JSP Site Design (ISBN 1-861005-51-2).

Now let's get back on track. With the design and framework of a Model 2-based discussion forum in place, let's take a look at how the forum can be implemented once again in Model 2 architecture.

Viewing the Responses

Previously, to view the responses to a topic we made a direct request to the view-topic.jsp page, passing the id of the specific topic. At the top of this page was a JSP scriptlet that found this parameter and used it to lookup the appropriate Topic instance for use further down the page when building up the table of responses. One of the benefits of the Command and Controller strategy is that we can move this sort of code out of the JSP and back into a reusable component. With this in mind, we can implement the action to view a topic:

    package forum;    import javax.servlet.http.*;    public class ViewTopicAction extends Action {      public String process(HttpServletRequest request,          HttpServletResponse response) { 

In the process() method, we first get the id parameter and use it to look up the appropriate Topic instance:

         String id = request.getParameter("id");         Topic topic = Topics.getTopic(Integer.parseInt(id)); 

Now that we have the topic, we place it in the request ready for use on the view:

         request.setAttribute("topic", topic);         return "/view-topic.jsp";       }    } 

We've literally just moved the code into an Action subclass. Once the Topic instance has been located, we then need to make this available to the page in order that it can build the table of responses. To do this we use the HTTP Request object as a temporary storage area for this object:

         request.setAttribute("topic", topic); 

Using the HTTP request (or even the session) is a useful and frequently used mechanism for passing information between the various components within web applications. This works between two servlets, two JSP pages or a mixture of the two.

On completion of this action, the controller will dispatch control to the view-topic.jsp page on which we can use the standard <jsp:useBean/> action to locate the object again. The remainder of the page remains unchanged:

    <jsp:useBean   scope="request"/>    <p>    <h2>Topic : <%= topic.getTitle() %></h2>    </p> 

Following this we just build the table of responses as before.

The final step in using this is to ensure that we change all direct links to the view-topic JSP into links to the controller, not forgetting to include the name of the action that we wish to perform.

    /controller/ViewTopic?id=0 

Although this particular action is fairly small, it does illustrate the point that we raised earlier about enhanced maintainability and reusability. We can now make use of this action elsewhere in our application rather than coding a scriptlet on a JSP. This means that should we wish to modify this behavior, we'll know where to find it and the modifications will be reflected elsewhere in our application automatically.

Processing the Login

In a similar way, we can encapsulate the logic associated with processing the user's login into an Action class, LoginAction:

    package forum;    import javax.servlet.http.*;    public class LoginAction extends Action {      public String process(HttpServletRequest request,          HttpServletResponse response) {        String id = request.getParameter("id");        String password = request.getParameter("password");        String view;        if (Users.exists(id, password)) {          request.getSession().setAttribute("user", Users.getUser(id));          view = "/index.jsp";        } else {          view = "/login.jsp";        }        return view;      }    } 

As you can see, we've literally moved the code from the process-login.jsp page. This page didn't actually contain any presentation at all in the page-centric implementation and this is another good reason for moving the code into a small reusable component. It's not only easier to find this code to maintain it in the future, but JSP pages are really suited to rendering responses rather than being containers for lots of Java code.

To ensure that our action gets called, we again need to change any references to the process-login.jsp page, including the action attribute of HTML form tags:

    <form action="controller/Login" method="post"> 

Adding a New Response

We've already seen how the view-topic.jsp page has changed: the short scriptlet at the top of the page has been removed. The new-response.jsp page is effectively the same with an additional HTML form underneath the table of responses. For this reason and rather than recoding the logic to lookup the appropriate topic, we can extend the ViewTopicAction class helping to ensure that the functionality is consistent.

    package forum;    import javax.servlet.http.*;    public class NewResponseAction extends ViewTopicAction {      public String process(HttpServletRequest request,          HttpServletResponse response) {        super.process(request, response);        return "/new-response.jsp";      }    } 

Although a simple example, this shows how the action components can be reused across different types of requests. Here, we are using the same functionality but dispatching to a different view.

Processing a New Response

The logic to add a new response from the HTML form can also be placed within an Action class as illustrated next.

    package forum;    import javax.servlet.http.*;    public class ProcessNewResponseAction extends Action {      public String process(HttpServletRequest request,          HttpServletResponse response) {        String text = request.getParameter("text");        int id = Integer.parseInt(request.getParameter("id"));        User user = (User)request.getSession().getAttribute("user");        Topic topic = Topics.getTopic(id);        topic.add(new Response(user, text));        return "/controller/ViewTopic?background-color:d9d9d9">}    } 

Instead of dispatching to a JSP, we've asked that the request be forwarded onto the ViewTopic action. This is a useful way of chaining actions together and again shows the ability to reuse them across different types of requests.

Deleting an Existing Response

The final action that we have in our web application is the ability to delete an existing response. Once again we can move the business logic associated with this into an Action component:

    package forum;    import javax.servlet.http.*;    public class DeleteResponseAction extends Action {      public String process(HttpServletRequest request,          HttpServletResponse response) {        int topicId = Integer.parseInt(request.getParameter("topic"));        int responseId = Integer.parseInt(request.getParameter("response"));        User user = (User)request.getSession().getAttribute("user");        Topic topic = Topics.getTopic(topicId);        Response res = topic.getResponse(responseId); 

Only the original author of a response can delete it, and for this reason the DeleteResponseAction contains the appropriate logic to make this check. However, what it (or the other actions) doesn't check is whether the user is actually logged on. We'll deal with this issue in the next section.

         if (res.getUser().equals(user)) {           // yes, so delete it           topic.remove(res);         }         return "/controller/ViewTopic?background-color:d9d9d9">}    } 

Using the Intercepting Filter Pattern

Checking that the user is logged on is another common piece of functionality that would be an ideal candidate to encapsulate into a reusable component. We could of course wrap this verification up into a small component and call it from the appropriate actions. However, what we really want is for unauthenticated requests to be redirected to the login page. Let's look at how the Intercepting Filter pattern can help.

A Closer Look at User Verification in the Discussion Forum

One of the features that our discussion board doesn't support is anonymous response to topics. To ensure that this doesn't happen, our current implementation uses a small snippet of code at the top of those pages that require a user to be logged in:

    <%      if (session.getAttribute("user") == null) {        // no, so redirect them to the login page      }    %> 

We've seen this already, and all it does is check that the user is logged in before allowing them to see the page that they requested. If the user isn't logged in yet, we simply forward them on to the login page.

The problem with this approach is that this code is scattered throughout the views (pages) in our application. This means that we might not be entirely sure which views contain the code. Subsequently, some pages that should have this code may not, and if we need to update the verification code we need to modify every page that contains it.

One way around this problem is to use intercepting filters. Intercepting filters are a useful way of performing pre-processing on requests before they are handled by the components in our web application. If we extend the architecture yet further, we can insert an intercepting filter to pre-process the requests:

click to expand

The types of tasks that we might want an intercepting filter to perform could be:

  • Checking the client type so that the appropriate type of site can be served up

  • Checking whether the user has a valid session

  • Checking that the user is logged in

In our discussion forum we can use an intercepting filter to ensure that any requests to specific pages are authenticated. Having identified that an intercepting filter is a useful component to have in our web application, how do we go about building one?

Implementing an Intercepting Filter

In a previous chapter we introduced filters, a new feature of the Servlet 2.3 specification. Using the same techniques as we described in that chapter, we can build a simple filter, and define a handful of mappings specifying those URLs on which we wish it to operate.

First of all, let's start with the filter class itself. We'll call it AuthenticationFilter:

    package forum;    import java.io.IOException;    import javax.servlet.*;    import javax.servlet.http.*;    public class AuthenticationFilter implements Filter {      private FilterConfig filterConfig;      public void init(FilterConfig filterConfig) throws ServletException {        this.filterConfig = filterConfig;      }      public void doFilter(ServletRequest req, ServletResponse res,                       FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest)req;        HttpServletResponse response = (HttpServletResponse)res; 

In the doFilter() method, we check to see if the current user logged in. If not, we redirect them to the login page:

        if (request.getSession().getAttribute("user") == null) {          RequestDispatcher dispatcher =            filterConfig.getServletContext().getRequestDispatcher("/login.jsp");          dispatcher.forward(request, response);        } else {          chain.doFilter(request, response);        }      }      public void destroy() {      }    } 

What is important here is that the functionality associated with this filter is almost identical to the scriptlet of code that we used in our Model 1 version of the discussion forum. All it does is check that the user is logged in and redirect them to the login.jsp page if this is not the case. One of the goals of OO is to encapsulate reusable functionality and that's exactly what we've done here. We've moved this logic out of the views and back towards the front of the request handling process.

With the filter built, we now need to plug it into our web application. As we've seen before, this is achieved through the web application deployment descriptor - the web.xml file:

    <?xml version="1.0" encoding="ISO-8859-1"?>    <!DOCTYPE web-app        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"        "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">    <web-app>      <filter>        <filter-name>AuthenticationFilter</filter-name>        <filter-class>forum.AuthenticationFilter</filter-class>      </filter>      <filter-mapping>        <filter-name>AuthenticationFilter</filter-name>        <url-pattern>/controller/NewResponse</url-pattern>      </filter-mapping>      <filter-mapping>        <filter-name>AuthenticationFilter</filter-name>        <url-pattern>/controller/ProcessNewResponse</url-pattern>      </filter-mapping>      <filter-mapping>        <filter-name>AuthenticationFilter</filter-name>        <url-pattern>/controller/DeleteResponse</url-pattern>      </filter-mapping>      <servlet>        <servlet-name>FrontController</servlet-name>        <servlet-class>forum.FrontController</servlet-class>      </servlet>      <servlet-mapping>        <servlet-name>FrontController</servlet-name>        <url-pattern>/controller/*</url-pattern>      </servlet-mapping>    </web-app> 

We've already defined that all URLs starting /controller/ are directed to our FrontController servlet. Alongside this we define a filter called AuthenticationFilter and tell our web application to use the AuthenticationFilter class from the forum package. Once this has been setup, we can specify the set of URLs that trigger the filter. In our application, we need to pre-process all requests coming in for the following actions.

  • NewResponse

  • ProcessNewResponse

  • DeleteResponse

This is a very powerful and flexible mechanism, because we can easily reconfigure our filter should the security requirements of our application change.

The patterns that we have just discussed cover much of our example application. Although this book is about servlets, a chapter about web application design wouldn't be complete without a quick look at a few more patterns:

  • View Helper

  • Service to Worker

Using the View Helper Pattern

The View Helper pattern is a way of taking logic embedded in the view (for example scriptlets inside a JSP) and wrapping it up as reusable functionality for use on other view components.

Advantages of the View Helper Pattern

In the same way that we've tried to move much of the functionality away from the view components and back towards the front of the request handling cycle, there still remains some functionality in the views. A good example would be the JSP scriptlets inside the view-topic.jsp page that determine whether a particular response has been deleted and should be hidden.

The purpose of the view component is to present information back to the user. All of the business processing associated with the request should have been performed by this stage, leaving the view component to perform any logic specifically related to presenting the information. While we can include business logic in a JSP page via scriptlets, we have already noted that this is not a wise approach because the code in the scriptlet is not in a very reusable form.

To solve this problem, this type of functionality can be moved into helper components and subsequently reused across the web application. On an implementation level, these helpers could be built as any of the following:

  • JavaBeans - for use in servlet/JSP views

  • Custom tags - for use in JSP views

In fact we've already seen an example of the View Helper pattern in the view-topic.jsp page. The ViewTopicAction looks up the appropriate Topic instance and places it into the HTTP Request object, ready for the JSP to find it. To recap, here's the code for the process() method in the ViewTopicAction:

      public String process(HttpServletRequest request,                            HttpServletResponse response) {        String id = request.getParameter("id");        Topic topic = Topics.getTopic(Integer.parseInt(id));        request.setAttribute("topic", topic);        return "/view-topic.jsp";      } 

To make use of the information that this action places into the HTTP request, we might have implemented this lookup in view-topic.jsp using a simple JSP scriptlet as follows:

    <%      Topic topic = (Topic)request.getAttribute("topic");    %> 

Instead, however, we decided to use the <jsp:useBean/> action in view-topic.jsp:

    <jsp:useBean   scope="request"/> 

This is just one such example, and it just happens that in this situation the helper is already written for us. What is important is that we are removing as much logic from the view components in our web application, and placing it inside reusable components.

Another example includes the JSP scriptlet that is used to iterate over a collection of Response objects in the view-topic.jsp page:

      <%        Iterator it = topic.getResponses().iterator();        Response res;        while (it.hasNext()) {          res = (Response)it.next();          if (!res.isDeleted()) {      %>            <tr>              <td><%= res.getUser().getId() %></td>              <td><%= res.getText() %><br><br></td>              <td valign="top" align="center">                [ <a href="controller/DeleteResponse?                  topic=<%= topic.getId() %>&response=<%= res.getId() %>">                  Delete</a> ]              </td>            </tr>      <%          }        }      %> 

In this example there is much more Java code and therefore much more that can go wrong. Ideally we would like to be able to reuse this type of functionality on other pages that build up the content by iterating over a collection of Java objects. In this situation, a JSP custom tag could be used as a view helper as follows:

      <forum:iterate                      className="forum.Response"                     collection="<%= topic.getResponses() %>">        <%          if (!res.isDeleted()) {        %>            <tr>              <td><%= res.getUser().getId() %></td>              <td><%= res.getText() %><br><br></td>              <td valign="top" align="center">                [ <a href="controller/DeleteResponse?                  topic=<%= topic.getId() %>&response=<%= res.getId() %>">                  Delete</a> ]              </td>            </tr>        <%          }        %>      </forum:iterate> 

Here, the Java code responsible for performing the iteration has been moved inside a custom tag, moving it away from the page and resulting in a much cleaner JSP containing much less Java code than before. In doing this, we have created a reusable view helper that can be used on other pages in the web application.

Although the discussion of how such a custom tag might be implemented is beyond the scope of this chapter, the full source code for it is provided with the downloadable source code for the book.

The type of functionality that this pattern is applicable to includes:

  • Retrieval of data (for example getting data from the model to display)

  • Logic related to presentation of data but not to formatting (for instance determining whether a particular piece of data should be displayed)

  • General presentation layer-related logic (such as iterating over a collection of data items)

Using the Service to Worker Pattern

The final pattern we will consider is Service to Worker, which can be described as a "macro pattern". It's really just a combination of a front controller pattern, along with views and their helpers.

This pattern describes the architecture of our discussion forum, where we've wrapped up a large amount of the processing and presentation logic into reusable components. In other words, we're delegating the task off to worker components as illustrated in the following diagram:

click to expand

As with our discussion forum, the controller delegates processing to the actions, which in turn perform processing associated with servicing the request. These actions may modify the underlying model. The controller then dispatches the request to the next view component, which will in turn use the underlying model and helper components (such as JavaBeans and/or custom tags) to build and render the response.

Each of the patterns that we've looked at focuses on one particular area of development:

  • Front Controller - a centralized place for logic associated with servicing requests; here views are responsible only for the presentation of information to the user

  • Intercepting Filter - a centralized place for intercepting requests, for example to allow or disallow them

  • View Helper - wraps up reusable functionality used by the views and moves unnecessary logic away from them

Although each of these does address a particular problem, using them in isolation means that only that particular problem will be solved. In the case of our discussion forum, using the front controller in isolation may still mean that the view components contain unnecessary Java code embedded inside the JSP pages. On the other hand, using view helpers in isolation means that the pages will contain unnecessary business logic required to process the request.

The Service to Worker pattern is a combination of all of these patterns and ensures that each of the problem areas is addressed. This means that the logic associated with processing a request is centralized, while the logic associated with presenting information is also centralized, and reusable.

As we can now see from our discussion forum, we not only have a more structured system, but one that is easily maintainable. If we encounter a bug, it shouldn't take us long to track it down. We also have a system that is extensible. The addition of new functionality simply entails adding new actions into our request-handling framework. There is now much more scope for reusability of the components within in our application and this again improves the maintainability.



 < Free Open Study > 



Professional Java Servlets 2.3
Professional Java Servlets 2.3
ISBN: 186100561X
EAN: 2147483647
Year: 2006
Pages: 130

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net