Previous Section  < Day Day Up >  Next Section

12.3 Dealing with Struts Applications and JSF

The Apache Struts framework is probably the web application framework most widely used today; many people wonder how JSF and Struts fit together, or if JSF in fact replaces Struts. I like to look at it from two different angels: how JSF may be used for existing Struts applications and which technologies to pick for a new project.

Before we look at the different options, a brief review of the main differences in how the two technologies separate application logic and presentation logic makes it easier to come to an informed decision. Figure 12-5 illustrates the main interactions.

Figure 12-5. Architecture of Struts versus architecture of JSF
figs/Jsf_1205.gif

The main components in a typical Struts application are the Struts servlet, application logic implemented as Struts Action subclasses accessing business logic objects implemented as regular Java classes, and JSP pages for presentation. All requests are targeted to the Struts servlet, which delegates to a specific Action subclass identified by parts of the URL. The Action subclass does its thing by pulling input from the request data and saving objects representing the result in the request or session scope. It then returns a value that tells the Struts servlet which JSP page to use for the response. The Struts servlet invokes the selected JSP page, which renders a response based on the result objects.

In a typical JSF application, the main players are the JSF framework classes, JSF views containing components (often created by JSP pages), and event handlers implemented as action methods in application logic classes. The components are bound to properties in the application logic or business logic objects, and the event handlers operate on the business logic objects.

Ignoring the details, the difference that takes some time to get used to is the request processing control flow. Struts calls on the application logic to do its thing first, and then invokes a separate presentation piece that turns the result into user interface widgets (i.e., renders the response). JSF first calls on user interface widgets (the view components) to fire events. Event handlers invoke the application logic, and then the widgets render themselves with their new values.

12.3.1 Using the Struts-Faces Integration Package

If you have a large application developed with Struts, using all the latest bells and whistles, such as the validation framework, modules, Struts-Tiles, and so on, you probably want to stick to Struts, at least for now. A Struts-Faces integration package being developed by the Struts community lets you replace the action elements from the Struts and JSTL tag libraries in the JSP pages with actions from the JSF tag libraries representing the more powerful JSF components. You can do this a few pages at a time if you can't do it all at once, because the integration package supports a mixture of the old-style pages and pages with JSF components.

Based on the early access version that's available at the time of this writing, all it takes are a few configuration changes, such as replacing the standard Struts RequestProcessor class with one that can handle both Struts and JSF requests, and using a few new Struts action elements that are part of the integration tag library. The Struts-Faces integration package takes care of all the details needed to tie things together by plugging in custom versions of a few JSF classes, so requests to be processed by Struts Action subclasses are passed on to Struts, and JSF requests are processed by JSF.

For more information about the Struts-Faces integration package, keep an eye on the Struts home page at http://www.Jakarta.apache.org/struts/.

12.3.2 Migrating a Struts Application to JSF

Struts 1.1 and JSF 1.0 are definitely not equivalent in terms of features, and they aren't intended to be. Struts focuses on the Controller part of the MVC triage, with features like declarative navigation and validation, which are more sophisticated than what you find in JSF 1.0. JSF, on the other hand, focuses on the user interface, providing an event-driven component model similar to what's commonly used for standalone GUI applications, while Struts is more or less ignorant about how the user interface is developed.

That said, there's a lot of overlap between the two; converting a Struts application that doesn't use the more advanced Struts features to a pure JSF application is an option to consider. To get a feel for what it takes to convert a Struts application to JSF, let's look at a Struts application that I developed for my book JavaServer Pages (O'Reilly). It's a simple billboard service, where employees can post messages related to different projects they are involved with. An employee can customize the application to show only messages about the projects she's interested in. Figure 12-6 shows the three application screens.

Figure 12-6. Billboard application screens
figs/Jsf_1206.gif

This application uses application-controlled authentication with a custom login page. The main screen has a form where the user can select projects of interest and a list of matching messages. It also has a link to the third screen, where new messages can be posted, and a logout link.

Figure 12-7 shows all application classes.

Figure 12-7. Billboard application classes
figs/Jsf_1207.gif

Access control is implemented by an AccessControlFilter class, which is a filter that processes requests for URLs with /protected as a path element, which is true for all requests except for authentication and the logout requests. The filter looks for an object in the session scope that serves as proof for successful user authentication. If it finds the object, the filter lets the request pass through to the requested resource; otherwise, it forwards the request to the login screen. A context listener creates an instance of an EmployeeRegistryBean that interfaces a database with user information and a NewsBean that represents an in-memory message store, and saves references to both instances in the application scope.

The rest of the application logic is implemented as four Struts Action subclasses: AuthenticateAction, LogoutAction, StoreMsgAction, and UpdateProfileAction. The AuthenticateAction uses the EmployeeRegistryBean to verify that a username/password combination is valid. If it is, it gets an EmployeeBean that represents the user from the registry and places it in the session as proof of successful authentication. The LogoutAction invalidates the session. The StoreMsgAction stores a new message—represented by a NewsItemBean instance—in the message store represented by the NewsBean instance. The UpdateProfileAction updates the list of projects of interest in the EmployeeBean representing the current user and saves the updated bean in the EmployeeRegistryBean. The Action subclasses handle navigation between the screens by returning Struts ActionForward instances, declared as global forwards in the Struts configuration file.

All source code for the Struts version of this application is included with the examples for JavaServer Pages, available at http://www.hansbergsten.com/, in case you want to compare it to the JSF version.

To convert an application like this to JSF, you must do three things: convert the Struts Action subclasses to beans with action methods and properties, update the JSP pages to use JSF components bound to properties in the beans, and define JSF navigation rules matching the Struts global forward declarations. In this particular case, I also replaced the context listener with managed bean declarations, but whether that makes sense depends on the application. You should be able to reuse all model beans without modification.

I also modified the EmployeeRegistryBean to use a Map as its data source instead of a real database, to keep things simple. The content of the registry is configured in the faces-config.xml file, and the examples application defines one employee with the username hans and the password secret, in case you want to try it out.

Converting the Struts Action subclasses is by far the most time consuming activity, but it's very straightforward. Example 12-10 shows the original Struts StoreMsgAction class and Example 12-11 shows the JSF version of the same class.

Example 12-10. Struts StoreMsgAction class
package com.ora.jsp.servlets;



import java.io.*;

import java.sql.*;

import javax.servlet.*;

import javax.servlet.http.*;

import com.ora.jsp.beans.emp.*;

import org.apache.struts.action.*;



public class UpdateProfileAction extends Action {



    public ActionForward perform(ActionMapping mapping, 

        ActionForm form, HttpServletRequest request, 

        HttpServletResponse response) throws IOException, ServletException {



        if (request.getMethod( ).equals("POST")) {

            String[] projects = request.getParameterValues("projects");

            if (projects == null) {

                projects = new String[0];

            }

            HttpSession session = request.getSession( );

            EmployeeBean emp = 

                (EmployeeBean) session.getAttribute("validUser");

            emp.setProjects(projects);

            EmployeeRegistryBean empReg = (EmployeeRegistryBean) 

                getServlet( ).getServletContext( ).getAttribute("empReg");

            try {

                empReg.saveEmployee(emp);

            }

            catch (SQLException e) {

                throw new ServletException("Database error", e);

            }

        }

        ActionForward nextPage = mapping.findForward("main");

        return nextPage;

    }

}
Example 12-11. JSF StoreMsgAction class
package com.mycompany.messages;



public class StoreMsgAction {

    private NewsBean newsBean;

    private EmployeeBean validUser;

    private String category;

    private String msg;

    private String requestMethod;



    public void setNewsBean(NewsBean newsBean) {

        this.newsBean = newsBean;

    }



    public void setValidUser(EmployeeBean validUser) {

        this.validUser = validUser;

    }



    public String getCategory( ) {

        return category;

    }



    public void setCategory(String category) {

        this.category = category;

    }



    public String getMsg( ) {

        return msg;

    }



    public void setMsg(String msg) {

        this.msg = msg;

    }



    public void setRequestMethod(String requestMethod) {

        this.requestMethod = requestMethod;

    }



    public String store( ) {

        if ("POST".equals(requestMethod)) {

            NewsItemBean item = new NewsItemBean( );

            item.setCategory(category);

            item.setMsg(msg);

            item.setPostedBy(validUser.getFirstName( ) + " " +

                validUser.getLastName( ));

            newsBean.setNewsItem(item);

        }

        return "success";

    }

}

The main difference between the two versions is that properties are used to provide all data needed to perform the action for the JSF version, while the Struts version gets references to what it needs through servlet API calls. Real Struts applications often use an ActionForm to capture the form input and let the Action read its properties instead of using the servlet API. If you're used to this approach, you may recognize the JSF bean as a combination of the Struts ActionForm and Action classes. Another difference is that the JSF version doesn't extend any framework class or implement any framework interface.

The JSF version is declared as a request scope managed bean with the newsBean, validUser, and requestMethod properties initialized from value binding expressions in the bean declaration:

<faces-config>

  ...

  <managed-bean>

    <managed-bean-name>storeMsgAction</managed-bean-name>

    <managed-bean-class>

      com.mycompany.messages.StoreMsgAction

    </managed-bean-class>

    <managed-bean-scope>request</managed-bean-scope>

    <managed-property>

      <property-name>newsBean</property-name>

      <value>#{newsRegistry}</value>

    </managed-property>

    <managed-property>

      <property-name>validUser</property-name>

      <value>#{validUser}</value>

    </managed-property>

    <managed-property>

      <property-name>requestMethod</property-name>

      <value>#{facesContext.externalContext.request.method}</value>

    </managed-property>

  </managed-bean>

   ...

</faces-config>

The category and msg properties are bound to JSF components in the JSP page:

<%@ page contentType="text/html" %>

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>



<f:view>

  <html>

    <head>

      <title>Project Billboard</title>

    </head>

    <body bgcolor="white">



      <h:form>

        <table>

          <tr>

            <td>Project:</td>

            <td>

              <h:selectOneMenu value="#{storeMsgAction.category}">

                <f:selectItem itemLabel="JSF" itemValue="JSF" />

                <f:selectItem itemLabel="JSP" itemValue="JSP" />

                <f:selectItem itemLabel="Servlet" itemValue="Servlet" />

              </h:selectOneMenu>

            </td>

          </tr>

          <tr>

            <td colspan=2>

              <h:inputTextarea value="#{storeMsgAction.msg}" 

                cols="50" rows="10" />

            </td>

          </tr>

        </table>

        <h:commandButton value="Post Message" 

          action="#{storeMsgAction.store}" />

      </h:form>

    </body>

  </html>

</f:view>

You can find the source code for all the other classes in the example download, but all action classes follow the same pattern.

The other two types of changes—updating the JSP pages to use JSF components bound to properties in the beans and defining JSF navigation rules—are less dramatic and they are no different from what we've already discussed in the this book. If you're interested in the details for the billboard application, I suggest that you look at the JSP pages and the faces-config.xml file in the example download.

12.3.3 Picking the Right Technology for a New Application

If you're about to start the development of a new application, you may feel torn between all the possibilities. I don't have a crystal ball so I can't tell you what the future holds (and even if I did, I doubt it would help much), but based on what has happened with other Java web application technologies over the years, I'm pretty confident that JSF will continue to evolve and incorporate more of the features found in frameworks like Struts. It's also clear that there's broad industry support for JSF, so we're likely to see a lot of JSF implementations, development tools, component kits and add-ons, and training classes and books entering the market in 2004 and beyond.

I suggest that you look primarily for what you need from a framework for the first version of the application. If JSF covers those needs, I recommend that you go for a pure JSF application. If there are Struts features that you know you need for the first version, consider using the Struts-Faces integration package and pick the best parts from each technology.

    Previous Section  < Day Day Up >  Next Section