Date: 2004-09-18T12:47:51
   Editor: MichaelMcGrady <[EMAIL PROTECTED]>
   Wiki: Apache Struts Wiki
   Page: StrutsCatalogDispatchUtil
   URL: http://wiki.apache.org/struts/StrutsCatalogDispatchUtil

   no comment

New Page:

##language:en
== Universal Button Utility for Struts ==

There is a persistent problem of handling multiple buttons, and this is especially a 
problem when the buttons are images.  Struts has provided a solution for this with 
DispatchAction and its progeny.  There are various other solutions.  I have provided 
one at StrutsCatalogImageTagUtil.  I have actually provided two solutions there. The 
following solution is, I think, superior in every way.  What it does is to merge the 
best parts of StrutsCataglogImageTagUtil and DispatchAction.  

This is a solution for Struts.  If you want a solution independent of Struts, again, 
please see StrutsCatalogImageTagUtil.  The principle reason for this class is the 
problem with <input type='image' name='whatever'> tags in HTML.  Those tag send the 
values in the name attribute as ''whatever.x=9'' and ''whatever.y=26''.  
DispatchAction and its progeny provide a melange of solutions but no common way to 
deal with ''<a href='whatever.do'>'', ''<input type='submit'>'', ''<input 
type='image'>'', ''<input type='file'>'', or whatever.  This class, DispatchUtil does 
that.  If you prefer to use a subclass, just provide the functionality in this class 
in a class extending Action.  Herbert Rabago came up with the idea of providing the 
functionality in a utility class and I really like that idea.  So, here we go, after a 
caveat.

== Caveat Lector ==

For those who have used MappedDispatchAction and LookupDispatchAction, this solution 
will do everything that you are doing. You have to understand, however, which many 
people seem to have a really, really, really hard time doing, that there is a 
fundamental shift in how the logic in this class, DispatchUtil and its prior class 
SimpleDispatchAction work.  There are those who want to say the equivalent of "0 is 
just another number, so there is no big change here".  I cannot continue to try and 
awake them from their dogmatic slumbers.  Let me just say for the last time that the 
change from mining, getting, obtaining, etc. the name from the name/value request 
parameter pair is a significant change that makes DispatchUtil lighter, more flexible 
and less coupled in every respect.  People continue to argue and to insist that this 
class won't work as coded when it is doing fine in actual sites.

=== DispatchUtil ===
{{{
package com.crackwillow.struts.util.dispatch;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;

public class DispatchUtil {
  protected static    Log              log      = 
LogFactory.getLog(DispatchUtil.class);
  protected static    MessageResources messages = MessageResources.getMessageResources 
("org.apache.struts.actions.LocalStrings");
  protected           HashMap          methods  = new HashMap();
  protected           Class []         types    = { ActionMapping.class,
                                                    ActionForm.class,
                                                    HttpServletRequest.class,
                                                    HttpServletResponse.class };

  public ActionForward dispatch(Action action,
                                ActionMapping mapping,
                                ActionForm form,
                                HttpServletRequest request,
                                HttpServletResponse response)
                        throws Exception {
    String methodName  = getMethodName(request,mapping);
    Class  clazz       = action.getClass();
    if ("execute".equals(methodName) || "perform".equals(methodName)){
      // Prevent recursive calls
      String message = messages.getMessage("dispatch.recursive", mapping.getPath());
      log.error(message);
      throw new ServletException(message);
    }
    return dispatchMethod(action,clazz,mapping, form, request, response, methodName);
  }

  protected ActionForward dispatchMethod(Action action,
                                         Class clazz,
                                         ActionMapping mapping,
                                         ActionForm form,
                                         HttpServletRequest request,
                                         HttpServletResponse response,
                                         String name)
                        throws Exception {
    if (name == null) {
      return this.unspecified(mapping, form, request, response);
    }

    Method method = null;

    try {
      method = getMethod(clazz,name);
    } catch(NoSuchMethodException nsme) {
      String message = messages.getMessage("dispatch.method", mapping.getPath(), name);
      log.error(message, nsme);
      throw nsme;
    }

    ActionForward forward = null;

    try {
      Object args[] = { mapping, form, request, response };
      forward = (ActionForward)method.invoke(action, args);
    } catch(ClassCastException cce) {
      String message = messages.getMessage("dispatch.return", mapping.getPath(), name);
      log.error(message, cce);
      throw cce;
    } catch(IllegalAccessException iae) {
      String message = messages.getMessage("dispatch.error", mapping.getPath(), name);
      log.error(message, iae);
      throw iae;
    } catch(InvocationTargetException ite) {
      Throwable t = ite.getTargetException();
      if (t instanceof Exception) {
        throw ((Exception) t);
      } else {
        String message = messages.getMessage("dispatch.error", mapping.getPath(), 
name);
        log.error(message, ite);
        throw new ServletException(t);
      }
    }
    return (forward);
  }

  protected static String getMethodName(HttpServletRequest request,  ActionMapping 
mapping) {
    String methodName  = null;
    String buttonValue = null;
    String paramProperty = mapping.getParameter();
    if((paramProperty != null)) {
      methodName = paramProperty.substring(0,paramProperty.indexOf('.'));
    } else {
      Enumeration enum = request.getParameterNames();
      while(enum.hasMoreElements()) {
        buttonValue = (String)enum.nextElement();
        StdOut.log("log.dispatch","DispatchUtil buttonValue = " + buttonValue);
        if(buttonValue.indexOf(".dispatch") >= 0) {
          methodName = buttonValue;
          break;
        }
      }
    }
    return methodName.substring(0,methodName.indexOf('.'));
  }

  protected Method getMethod(Class clazz,String name)
      throws NoSuchMethodException {
    synchronized(methods) {
      Method method = (Method) methods.get(name);

      if (method == null) {
        method = clazz.getMethod(name, types);
        methods.put(name, method);
      }

      return (method);
    }
  }

  protected ActionForward unspecified(ActionMapping mapping,
                                      ActionForm form,
                                      HttpServletRequest request,
                                      HttpServletResponse response)
                        throws Exception {
    String message = messages.getMessage( "dispatch.parameter", mapping.getPath(), 
getMethodName(request,mapping));
    log.error(message);

    throw new ServletException(message);
  }
}
}}} 

=== Some Explanation for Mapping and Lookup Dispatch Action Users===

All this means for you MappingDispatchAction and LookupDispatchAction users is that 
where you used some value for the method in your action-mapping such as update, you 
now have to use update.dispatch.  If you override the method getMethodName( . . . ) 
and, instead of ''methodName = 
paramProperty.substring(0,paramProperty.indexOf('.'));'' use ''methodName = 
paramProperty'', you won't have to make any changes.  If you want to avoid the use of 
struts-config.xml altogether, you can.  DispatchUtil really replaces not only 
MappingDispatchAction, LookupDispatchAction and DispatchAction but also the necessity 
those classes occasioned of using the parameter property of ActionMapping.

=== Future Expansion ===

I put this up quickly to get rid of the mess that had been made of a previous 
submission.  I will add to this Monday, Sept. 19th, if not sooner.  Weddings this 
weekend.

=== Links ===

Struts

== Critics Corner ==

If you don't like what is said or presented, and you want to advance your ideas, 
please don't make this presentation a mess by doing it here.  Please be respectful and 
provide a link here to your ideas or initiate another space.  Multiple authors 
fundamentally in disagreement make a page unreadable.  This page had to be moved from 
StrutsCatalogSimpleDispatchAction because of such problems.  Please don't repeat them 
here.  I love to read them but I  love to keep things readable too.  Thank you.

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to