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]