Date: 2004-09-18T20:21:15
Editor: SteveRaeburn <[EMAIL PROTECTED]>
Wiki: Apache Struts Wiki
Page: StrutsCatalogSimpleDispatchAction
URL: http://wiki.apache.org/struts/StrutsCatalogSimpleDispatchAction
no comment
Change Log:
------------------------------------------------------------------------------
@@ -1,436 +1 @@
-StrutsCatalog: Niall has made this such difficult page to read I have put a new
presentation of the ideas at StrutsCatalogDispatchUntil.
-
-'''You can use a dispatch action without requiring configuration of a parameter
attribute in struts-config.xml.'''
-
-
-'''Assume that you have ''button'' code not unlike:'''
-
-'''PAY ATTENTION TO THE OCCURENCE OF "method." IN THE FOLLOWING CODE.'''
-
-'''LINKS:'''
-{{{
-<a href='registration.do?method.update=update'>UPDATE</a>
-<a href='registration.do?method.delete=delete'>DELETE</a>
-}}}
-'''FORM SUBMITS:'''
-{{{
-<input type='submit' name='method.update' value='update'>UPDATE
-<input type='submit' name='method.delete' value='delete'>DELETE
-}}}
-'''IMAGES:'''
-{{{
-<input type='image' name='method.update' src='fix.gif'>
-<input type='image' name='method.delete' src='nuke.gif'>
-
-}}}
-
-'''or the equivalent, in the various Struts' image taglibs:'''
-
-'''Now, how do we know which image has been clicked or which command has been clicked
and, additionally, have the methods automatically generated? The answer has been
complicated and costly in the past. Here is a simple way to achieve everything at a
low cost and with great flexibility and freedom.'''
-{{{
-public abstract class SimpleDispatchAction
- extends Action {
- protected Class clazz = this.getClass();
- protected static Log log =
LogFactory.getLog(SimpleDispatchAction.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 execute(ActionMapping mapping,
- ActionForm form,
- HttpServletRequest request,
- HttpServletResponse response)
- throws Exception {
- String name = getMethodName(request);
- if ("execute".equals(name) || "perform".equals(name)){
- // Prevent recursive calls
- String message = messages.getMessage("dispatch.recursive", mapping.getPath());
- log.error(message);
- throw new ServletException(message);
- }
- return dispatchMethod(mapping, form, request, response, name);
- }
-
- protected ActionForward dispatchMethod(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(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(this, 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) {
- String methodName = null;
- String buttonValue = null;
- String parameter = mapping.getParameter();
- if((parameter != null) && (parameter.startsWith("method."))) {
- methodName = parameter.replaceFirst("method.","");
- } else {
- Enumeration enum = request.getParameterNames();
- while(enum.hasMoreElements()) {
- buttonValue = (String)enum.nextElement();
- if(buttonValue.startsWith("method.")) {
- methodName = buttonValue.replaceFirst("method.","");
- if(methodName.endsWith(".x")) {
- methodName = methodName.replaceFirst(".x","");
- } else if(buttonValue.endsWith(".y")) {
- methodName = methodName.replaceFirst(".y","");
- }
- break;
- }
- }
- }
- return methodName;
- }
-
- protected Method getMethod(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));
- log.error(message);
- throw new ServletException(message);
- }
-
- protected ActionForward cancelled(ActionMapping mapping,
- ActionForm form,
- HttpServletRequest request,
- HttpServletResponse response)
- throws Exception {
- return null;
- }
-}
-
-}}}
-
-'''Use methods like the following in your subclass:'''
-{{{
- public ActionForward update(ActionMapping mapping,
- ActionForm form,
- HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException {
- // do update
- return mapping.findForward("success");
- }
-
- public ActionForward delete(ActionMapping mapping,
- ActionForm form,
- HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, ServletException {
- // do delete
- return mapping.findForward("success");
- }
-}}}
-
-'''So, just mine (retrieve, fetch) the value of the [name].x request parameter for a
generic solution that covers links, form submits, images, and so on without requiring
struts-config.xml use and providing auto method calling via reflection. If you have
further questions, or don't understand the last sentence, reread the beginning of this
hurried presentation and ''see'' StrutsCatalogMultipleImageTagsSimplified .'''
-
-'''Please note that if you like configuring struts-config.xml and like
!MappingDispatchAction, !SimpleDispatchAction does everything that class does and
more.'''
-
-
-
-'''Following a suggestion of Hubert Rabago, I have constructed a utility class
!DispatchUtil so that you can get the same result by using'''
-{{{
-return new !DispatchUtil.dispatch(this,mapping,form,request,response);
-}}}
-'''in your action execute class, and, of course, providing the related actions. That
class follows. Hubert is talking about putting something into !RequestUtils for
struts that does the same thing. Importantly, you can jettison all of
!DispatchAction, !LookupDispatchAction and !MappingDispatchAction if you use this.
You will have to change your action mapping struts-config.xml file for
!MappingDispatchAction (you can get rid of those multiple entries and just use one)
and will have to add .x where required in your submit, link and file buttons.'''
-
-{{{
-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 {
- Class clazz = action.getClass();
- String name = getMethodName(request,mapping);
- if ("execute".equals(name) || "perform".equals(name)){
- // 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, name);
- }
-
- 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 parameter = mapping.getParameter();
-
- if((parameter != null) && (parameter.startsWith("method."))) {
- methodName = parameter.replaceFirst("method.","");
- } else {
- Enumeration enum = request.getParameterNames();
- while(enum.hasMoreElements()) {
- buttonValue = (String)enum.nextElement();
- if(buttonValue.startsWith("method.")) {
- methodName = buttonValue.replaceFirst("method.","");
- if(methodName.endsWith(".x")) {
- methodName = methodName.replaceFirst(".x","");
- } else if(buttonValue.endsWith(".y")) {
- methodName = methodName.replaceFirst(".y","");
- }
- break;
- }
- }
- }
- return methodName;
- }
-
- 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);
- }
-
- protected ActionForward cancelled(ActionMapping mapping,
- ActionForm form,
- HttpServletRequest request,
- HttpServletResponse response)
- throws Exception {
- return null;
- }
-}
-}}}
-
-
-
-'''Michael !McGrady'''
[EMAIL PROTECTED]
-
-
-
-
-
-
-----
-
-Seems to me that most of the !SimpleDispatchAction duplicates whats already in the
!DispatchAction class. If we re-factored !DispatchAction so that the parameter
retrieval was moved into a new getParameter() method then all that would be needed to
achieve what you want is a flavour that overrides the getParameter()/getMethodName()
methods.
-
-
-
-
-Something along the lines of ...
-
-{{{
-public abstract class SimpleDispatchAction extends DispatchAction {
-
- protected String getParameter(ActionMapping mapping,
- ActionForm form,
- HttpServletRequest request,
- HttpServletResponse response) {
-
- return mapping.getParameter();
-
- }
-
- protected String getMethodName(ActionMapping mapping,
- ActionForm form,
- HttpServletRequest request,
- HttpServletResponse response,
- String parameter) {
-
- if((parameter != null) && (parameter.endsWith(".x"))) {
- methodName = parameter.substring(0,parameter.indexOf('.'));
- } else {
- Enumeration enum = request.getParameterNames();
- while(enum.hasMoreElements()) {
- buttonValue = (String)enum.nextElement();
- if(buttonValue.endsWith(".x")) {
- methodName = buttonValue.substring(0,buttonValue.indexOf(".x"));
- }
- }
- }
- return methodName;
- }
-
-}
-
-}}}
-
-'''Niall Pemberton'''
-
-----
-
-'''Thanks, Niall, HOWEVER'''
-
-This is fundametally mistaken, Niall, and rests on an important misconception. The
data and the logic of !SimpleDispatchAction differ from all of !DispatchAction,
!LookupDispatchAction, and !MappingDispatchAction. The error here is thinking that
just because they all get the name of the method to call that anything else is just a
mere difference in internal logic. That is not the case. Using one instead of the
other will break the code in lots of ways, and there are huge diffences between these
classes. None of the data used for the struts existing dispatch classes is used to
determine the method in the !SimpleDispatchAction. For those interested in this
approach, I suggest you read the exchanges on the struts-dev apache list.
!SimpleDispatchAction certainly should not subclass !DispatchAction. That just piles
unwanted and useless code bloat into !SimpleDispatchAction.
-
-'''Michael !McGrady'''
-
-----
-
-On this we differ. I think the version of !SimpleDispatchAction I posted does exactly
the same as you're version (caveat requires slight modification to !DispatchAction)
but does't bloat the code base by duplicating the majority of whats already in
!DispatchAction. Fundamentally all these things, including !SimpleDispatchAcion, do
the same thing (i.e. use reflection to execute a specified method) and only differ
slightly code-wise in how they determine the method name.
-
-On the suggestion of factoring out all this code into !RequestUtils, I have the
following comments:
-
- * All these static utility classes provide a headache because you can't override
their static methods if you want to modify their behaviour. !RequestUtils is just such
a case - if !RequestUtils was refactored to be just a facade to a bean (in the way
!BeanUtils has been) where you can set a custom instance to use then it would resolve
this. But as it stands these kind of things are a pain - especially in this kind of
case where alot of the code is the same and you just want to change small parts of the
behaviour.
-
- * Currently !DispatchAction only does the reflection to get the methods once and
then caches them in a Map - moving this into !RequestUtils would mean it would have to
use reflection each time the method is called.
-
-I do like Hubert's suggestion factoring the code out of Action altogether and not
having to inherit from a specific super class. I have put together a !ActionDispatcher
class and attached the code to a bug here:
-
- [http://issues.apache.org/bugzilla/show_bug.cgi?id=31270 Bug 31270]
-
-'''Niall Pemberton'''
-
-----
-'''Unfortunately, Niall, you assumed for some reason I was talking about your code.
I was not talking about your code. Notice that I did not subclass DispatchAction in
SimpleDispatchAction so there could have been no code bloat. In fact, I did not
subclass DispatchAction because I don't like the code bloat in that Action. I was
talking about your understanding of this class. I wish you would slow down and see
what is going on before making all these decisions. I suppose calmer reflection will
eventually prevail but presently you are doing things while clearly misapprehending
what is going on with this class. You might take some pause at that statement, since
I am the author of the class in the frist instance. However, as I said, time will
tell. I think I know my class alright.'''
-
-----
-
- * I did notice that your SimpleDispatchAction didn't subclass DispatchAction and my
comment about code bloat is just the fact that you have duplicated almost everything
in DispatchAction in your SimpleDispatchAction rather than sub-classing it and
re-using the code. The only ''real'' difference is in the contents of the
getMethodName() method. Comparing the SimpleDispatchAction methods to DispatchAction:
-
- You are not getting the differences, Niall, please see below. All the methods
you mention are related to the reflection part of DispatchAction and the names and
code were kept simply to assist. Getting a method from a method name is not exactly
unique code. The difference is in how the method name is obtained. Now, does that
seem to be any sort of a difference to you? It is as if you are reading this as an
English major rather than as a logician. You are counting words instead of seeing the
abstraction. The patterns are different, Niall. Completely so. The reflection is
the same. DispatchAction is not there because of any special use of reflection, and
neither is my code, so they share that. This is something that is otiose.
-
- * execute() - same code '''except''' SimpleDispatchAction doesn't handle
cancelled and doesn't retrieve a parameter from the action mapping.
- * unspecified() - identical to DispatchAction
- * cancelled() - identical to DispatchAction
- * dispatchMethod() - identical to DispatchAction
- * getMethod() - identical to DispatchAction
-
-
-
- * I don't see any 'bloat' in DispatchAction and think you're talking nonsense.
-
- Well, since DispatchAction could be a subclass of SimpleDispatchAction without
adding any functionality and yet has other methods and other requirements should mean
something to someone. And, since the subclasses of DispatchAction are also in the
same category, that should also mean something. You focus on the logic in determining
the name of the request parameter and compare it to the logic of determining the value
of the request parameter, Niall, and you cannot seem to grasp the huge difference this
makes. It is like you are thinking that adding 0 to a number system is just not all
that important. You don't, that is, get it, and your comments show you don't.
-
- * I'm not making any decisions - just taking part in a debate - so I don't
understand that comment.
-
- * Seems to me that its you thats misapprehending whats going on and I can't
understand why you can't see that SimpleDispatchAction is ''almost'' identical to
DispatchAction.
-
-'''Niall Pemberton'''
-
-----
-
-The almost identical part, Niall, is just in the use of the getMethod(...)
functionality once a value for method is obtained. That is all. The rest is so
different they simply are not comparable. You don't see that, I think.
-
-Obviously I saw everything you are talking about, so you cannot think you are telling
me something. I wish you could see what I am trying to show you.
-
-'''MichaelG McGrady'''
-
-
+Page content moved: See StrutsCatalogDispatchUntil.
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]