Many times you will find that two Actions are very similar but need one
small behavior to change. One good way to handle this is to subclass one
Action from the other and change the behavior that way. Though, in the
case of an Action, the behavior may buried in the perform (or execute)
method. It may also not really seem worth a hotspot method of its own. 

DEFINITION hotspot - also called flexible points or extension points,
hotspots are locations where code may added to customize a framework.
Hotspots (the hotspot subsystem) describe the different characteristics
of each application that can supported by the framework. In essence,
Hotspots represent the problems that a framework solves. Many
object-orientated frameworks consist of a kernel subsystem and a hotspot
subsystem. [Braga et al]
<http://www.almaden.ibm.com/cs/people/fontoura/papers/wmf98.pdf>

Meanwhile, the method signature is locked, and adding another parameter
to change the behavior is not trivial. 

For an Action, another way to create a hotspot is through the
ActionMappings. From an architectural perspective, the ActionMappings
are a ~red-hot~ extension point in the framework. Virtually every Action
in an application is designed to be extended through the ActionMappings
-- most often though use of the ActionForwards provided by the mappings. 

The vast majority of Actions include "success" or "failure" forwards or
something very much like them. The Action is often coded to use these as
static parameters. Part of the Action's API contract is that it expects
such forwards to exist either locally or globally. If they don't --
white screen: the browser returns a null response. 

But we're writing dynamic applications here, and the forwards can
treated as dynamic parameters too. 

One common circumstance is checking for the cancel button. Some Actions
need to do this, others do not. We could cut-and-paste the code around,
but that's not the best road to reuse. 

In practice, if the cancel button is pressed, the Action needs someplace
to go anyway. So, what if the Action were to check to see if it had a
"cancel" forward? If so, it can check for the cancel button, and return
the forward if the button was pressed. If there is not a cancel forward,
or the cancel button was not pressed, the Action can just go about its
business. 

Here's some code that does just that:

forward = mapping.findForward("cancel");
if ((forward!=null) && (isCancelled(request))) {
           // Post token error message
           ActionErrors errors = new  ActionErrors();
           errors.add(ActionErrors.GLOBAL_ERROR,
                new ActionError("error.cancel"));
           saveErrors(request,errors);
           return (forward);
}

First, it checks to see if a "cancel" ActionForward has been defined. If
an appropriate ActionForward has been defined (!=null) and the request
was in fact cancelled, a message is posted and the "cancel"
ActionForward is returned, ending the method. 

If you put something like this in a base Action for your application,
when you define a "cancel" forward that leads to another action, you may
need to define it as a redirect. 

      <forward
            name="cancel"
           redirect="true"
           path="/do/Menu"/>

The redirect will clear the cancel state. If the other action is
checking for cancel too, this will keep you from falling into a loop. Of
course, if the other Action doesn't check for cancel, then using
redirect isn't important. But code like this gets the most reuse when
it's provided through a base Action that your other Actions subclass. 

Another good example is whether to use a transaction token with an
operation. The token code is not complex, but it would nice if we did
not have to copy and paste it in wherever we needed it. It would nicer
still if we could look at the configuration file and tell whether a
Action wanted a token or not. 

            // Check for missing token 
        forward = mapping.findForward("getToken");
        if ((null!=forward) && (!isTokenValid(request))) {
           // Post token error message
           ActionErrors errors = new ActionErrors();
           errors.add(ActionErrors.GLOBAL_ERROR,
                new ActionError("error.token"));
           saveErrors(request,errors);
            return (forward);
        }
        if (null!=forward) {
        // reset to guard against duplicate request
            resetToken(request);
        }

So, if the ActionMapping for an Action using this code includes a
"token" ActionForward, 

     <forward
        name="getToken"
        path="/pages/error.jsp"/>

and the token is missing or invalid, it will forward to the "getToken" 
page instead of processing the rest of the Action. If the was a "token"
forward, but it was valid, the code resets the token so it can't used
again. 

To close the loop, we can include a "setToken" forward to tell an Action
to create a new token. 

          // Check for save token directive (do this last)
        forward = mapping.findForward("setToken");
        if (null!=forward) saveToken(request);

Though, this is really a kludge, since the ActionForward URI would never
used, and so we are misusing its role in the framework. A better
approach would to extend ActionMappings to tell the Action whether to
create a token or not, 

        if (myMapping.setToken()) saveToken(request);

but that's more surgery than we can squeeze into a tip =:0) 

HTH, Ted.

Struts Tips are released weekly on the MVC-Programmers List. 
To subscribe, visit BaseBean Engineering <http://www.basebeans.com>. 

Archives of the past columns are available at JGuru 
<http://jguru.com/faq/subtopic.jsp?topicID=893704>

About Ted. Ted Husted is an active Struts Committer. He also 
moderates the Struts mailing list and the JGuru Struts FAQ.

Copyright Ted Husted 2002. All rights reserved. 

###
_______________________________________________
MVC-Programmers mailing list
[EMAIL PROTECTED]
http://www.basebeans.com:8081/mailman/listinfo/mvc-programmers

Reply via email to