Programming Jakarta Struts, 2nd Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Jakarta Struts, 2nd Edition [Electronic resources] - نسخه متنی

Chuck Cavaness

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








9.3 Controller Extension Points


The next set of possible extension
points is within the controller layer. Some of these have been
mentioned briefly in previous chapters, but they're
repeated here for completeness.


9.3.1 Extending the ActionServlet Class


In earlier versions of the Struts
framework, it was almost a given that an application needed to extend
the ActionServlet class because most of the
controller functionality, excluding the Action
class behavior, was present in this class. With Struts 1.1, this is
no longer true. However, there are still a few good reasons why you
might need to extend the ActionServlet class.

As was pointed out in Chapter 5, the
initialization routines that are invoked when a Struts application is
first launched reside in the ActionServlet class.
If you need to modify the way the framework initializes itself, this
is one of the places to do so. To extend the
ActionServlet class, just create a subclass of
org.apache. struts.action.ActionServlet. You can
then override the method or methods that you need to function
differently. Once this is done, you need to modify the deployment
descriptor so that the Struts application will use your custom
ActionServlet:

<servlet>
<servlet-name>storefront</servlet-name>
<servlet-class>
com.oreilly.struts.storefront.framework.ExtendedActionServlet
</servlet-class>
</servlet>

Most of the runtime request-processing behavior has been moved to the
RequestProcessor class in Struts 1.1. If you need
to customize the manner in which your Struts application processes a
request, see the next section.


9.3.2 Extending the RequestProcessor Class


If you need to override functionality
within the RequestProcessor class, you must let
the framework know that it should use your customized version rather
than the default. You can make the framework aware of your
specialized RequestProcessor by modifying the
configuration file for the Struts application. If your configuration
file doesn't already have a
controller element within it,
you'll need to add one (there are several attributes
that can be configured within the controller
elementsee Chapter 4 for more details):

<controller 
contentType="text/html;charset=UTF-8"
debug="3"
locale="true"
nocache="true"
processorClass="com.oreilly.struts.framework.CustomRequestProcessor"/>

The processorClass attribute allows you to specify
the fully qualified Java class name of your specialized
RequestProcessor. The Struts framework will create
an instance of your specialized RequestProcessor
at startup and use it to process all of the requests for the
application. Because each application module can have its own Struts
configuration file, you can specify a different
RequestProcessor for each module.


9.3.2.1 Using the processPreprocess( ) method

There are many methods that can be overridden within the
RequestProcessor class. One of the methods that
was designed with extension in mind is the
processPreprocess()
method. This method is called for each request. By default, it does
nothing with the requests, but you can use this method in various
ways to change the default request-processing behavior. The
processPreprocess() method looks like this:

protected boolean processPreprocess( HttpServletRequest request,
HttpServletResponse response ){
return (true);
}

This method is called early in the request-processing stage, before
the ActionForm is called and before the
execute() method is called on the
Action object. By default, the
processPreprocess() method returns
true, which tells the
RequestProcessor to continue processing the
request. However, you can override this method and perform some type
of conditional logic, and if the request should not be processed any
further, you can return false. When
processPreprocess() returns
false, the RequestProcessor
stops processing the request and simply returns from the
doPost() or doGet() call.
Consequently, it's up to you to programmatically
forward or redirect the request within the
processPreprocess() method.

Example 9-3 illustrates this approach. This example
checks to see whether the request is from a local host. If so, it
returns true and the request continues. If the
request is from any other host, the request is redirected to the
unauthorized access page and the method returns
false.


Example 9-3. Using the processPreprocess( ) method

protected boolean processPreprocess( HttpServletRequest request,
HttpServletResponse response ){
boolean continueProcessing = true;
// Get the name of the remote host and log it
String remoteHost = request.getRemoteHost( );
log.info( "Request from host: " + remoteHost );
// Make sure the host is from one that you expect
if ( remoteHost == null || !remoteHost.startsWith( "127.") ){
// Not the localhost, so don't allow the host to access the site
continueProcessing = false;
ForwardConfig config = appConfig.findForwardConfig("Unauthorized");
try{
response.sendRedirect( config.getPath( ) );
}catch( Exception ex ){
log.error( "Problem sending redirect from processPreprocess( )" );
}
}
return continueProcessing;
}

The most
important thing to note in this example is that even though the
processPreprocess() method is returning
false, it's still up to the
method to take care of redirecting the request. Returning
false just lets the
RequestProcessor know that it
doesn't need to continue. The controller
doesn't do anything with the request;
that's the responsibility of the
processPreprocess() method.


The manner in which Example 9-3 specifies the path
in the sendRedirect() method prevents us from
having to hardcode the URI of the
unauthorized_access.jsp resource. This way, if
the actual page for the forward changes, this method will not have to
be modified.

There are other ways that you can perform the same logic without
using the processPreprocess() method. One
alternative is to use a
servlet filter (part of the Servlet 2.3
API). Filters allow you to inspect the request before it ever reaches
the Struts controller. However, there are two problems to be aware of
with filters.

First, because filters are part of the 2.3 API, you will not be able
to use them if you are using a servlet container that supports only
2.2. Second, because the filter inspects the request very early in
the processing stage, filters don't have easy access
to the Struts API. This makes it hard to look up
ActionForwards or anything else that you might
normally use in the processPreprocess() method.
In fact, because the Struts controller hasn't even
seen the request at the time the filter inspects it, the controller
hasn't had a chance to select the proper application
module.


9.3.3 Extending the Base Action Class


There have been several places in
previous chapters where I've mentioned a technique
of creating a base Action that extends the Struts
Action class and then using it as a superclass for
other actions. One of the reasons for doing this is that in many
applications, there is common logic that must be implemented by most
of the Action classes. Letting this
Action superclass contain most of this code
eliminates the redundancy and keeps the size and complexity of the
other action classes manageable. Example 9-4
provides a very simple version of a base Action
class that performs this type of behavior.


Example 9-4. A base Action class

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Iterator;
import javax.servlet.http.*;
import org.apache.struts.action.*;
// Non Struts Imports
import com.oreilly.struts.storefront.framework.util.IConstants;
import com.oreilly.struts.storefront.framework.exceptions.*;
import com.oreilly.struts.storefront.framework.UserContainer;
import com.oreilly.struts.storefront.service.IStorefrontService;
/**
* An abstract Action class that all Storefront Action classes can extend.
*/
abstract public class StorefrontBaseAction extends Action {
/**
* The default execute( ) method that all actions must implement.
*/
public ActionForward execute( ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response ) throws Exception{
// It just calls a worker method that contains the real execute logic
return executeAction( mapping,form,request,response,getUserContainer(request));
}
/**
* The actual do work method that must be overridden by the subclasses.
*/
abstract public ActionForward executeAction( ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response,
UserContainer userContainer )
throws Exception;
// This super Action is also a good place to put common utility methods
public boolean isLoggedIn( HttpServletRequest request ){
UserContainer container = getUserContainer(request);
return ( container != null && container.getUserView( ) != null );
}
/**
* Retrieve the UserContainer for the user tied to the request.
*/
protected UserContainer getUserContainer(HttpServletRequest request) {
HttpSession session = request.getSession( );
UserContainer userContainer =
(UserContainer)session.getAttribute(IConstants.USER_CONTAINER_KEY);
// Create a UserContainer if one doesn't exist already
if(userContainer == null) {
userContainer = new UserContainer( );
userContainer.setLocale(request.getLocale( ));
session.setAttribute(IConstants.USER_CONTAINER_KEY, userContainer);
}
return userContainer;
}
}

The StorefrontBaseAction
class shown in Example 9-4 illustrates how you can
use a base Action to perform repetitive behavior
so that all of the subclasses need not perform it themselves.

Suppose, for example, that all of your Action
classes needed to obtain the UserContainer for the
current user and use some information within it (e.g., user ID or
security permissions). One approach is to force all of the
Action classes to obtain the
UserContainer on their own, handle the situation
when there isn't one, and so on. An alternate, more
manageable approach is to put that behavior in a super
Action and pass the
UserContainer to the subclasses.

As Example 9-4 shows, the
StorefrontBaseAction implements the
execute() method, but inside that method, it gets
an instance of a UserContainer and passes it as an
argument to the executeAction() method. Each
subclass implements the abstract executeAction()
method and has the UserContainer passed in,
instantiated, and guaranteed not to be null. This is only a trivial
example of what you can do. Any behavior that all actions need to
perform is a candidate for being implemented in the
Action superclass, so that when the time comes to
modify the implementation, only the behavior in the superclass needs
to change.


    / 181