Author: pbenedict Date: Sun Dec 28 21:12:58 2008 New Revision: 729832 URL: http://svn.apache.org/viewvc?rev=729832&view=rev Log: STR-3168: Refactor out method resolution, set mapping parameter default to execute, delete static utils, replace tabs with spaces
Added: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMethodResolver.java (with props) struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/MethodResolver.java (with props) struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMethodResolver.java (with props) Removed: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletDispatchUtils.java Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/chain/commands/ExecuteDispatcher.java struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractDispatcher.java struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractEventMappingDispatcher.java struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMappingDispatcher.java struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractParameterDispatcher.java struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletEventMappingDispatcher.java struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMappingDispatcher.java struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletParameterDispatcher.java Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/chain/commands/ExecuteDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/chain/commands/ExecuteDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/chain/commands/ExecuteDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/chain/commands/ExecuteDispatcher.java Sun Dec 28 21:12:58 2008 @@ -51,65 +51,85 @@ * @see ClassUtils#getApplicationInstance(String) */ protected Dispatcher createDispatcher(String type, ActionContext context) throws Exception { - log.info("Initializing dispatcher of type: " + type); - return (Dispatcher) ClassUtils.getApplicationInstance(type); + log.info("Initializing dispatcher of type: " + type); + return (Dispatcher) ClassUtils.getApplicationInstance(type); } public boolean execute(ActionContext context) throws Exception { - // Skip processing if the current request is not valid - Boolean valid = context.getFormValid(); - if ((valid == null) || !valid.booleanValue()) { - return CONTINUE_PROCESSING; - } + // Skip processing if the current request is not valid + Boolean valid = context.getFormValid(); + if ((valid == null) || !valid.booleanValue()) { + return CONTINUE_PROCESSING; + } // Skip processing if no action is specified if (context.getAction() == null) { return CONTINUE_PROCESSING; } - // Skip processing if no dispatcher type specified - String dispatcherType = getDispatcherType(context); - if (dispatcherType == null) { - return CONTINUE_PROCESSING; - } - - // Obtain (or create) the dispatcher cache - String cacheKey = Constants.DISPATCHERS_KEY + context.getModuleConfig().getPrefix(); - Map dispatchers = (Map) context.getApplicationScope().get(cacheKey); - if (dispatchers == null) { - dispatchers = new HashMap(); - context.getApplicationScope().put(cacheKey, dispatchers); - } - - // Lookup (or create) the dispatch instance - Dispatcher dispatcher = null; - synchronized (dispatchers) { - ActionConfig actionConfig = context.getActionConfig(); - String actionType = actionConfig.getType(); - dispatcher = (Dispatcher) dispatchers.get(actionType); - if (dispatcher == null) { - dispatcher = createDispatcher(dispatcherType, context); - dispatchers.put(actionType, dispatcher); - } - } - - // Dispatch - Object result = dispatcher.dispatch(context); - processDispatchResult(result, context); + // Skip processing if no dispatcher type specified + String dispatcherType = getDispatcherType(context); + if (dispatcherType == null) { + dispatcherType = defaultDispatcherType; + if (dispatcherType == null) { + return CONTINUE_PROCESSING; + } + } + + // Obtain (or create) the dispatcher cache + String cacheKey = Constants.DISPATCHERS_KEY + context.getModuleConfig().getPrefix(); + Map dispatchers = (Map) context.getApplicationScope().get(cacheKey); + if (dispatchers == null) { + dispatchers = new HashMap(); + context.getApplicationScope().put(cacheKey, dispatchers); + } + + // Lookup (or create) the dispatch instance + Dispatcher dispatcher = null; + synchronized (dispatchers) { + ActionConfig actionConfig = context.getActionConfig(); + String actionType = actionConfig.getType(); + dispatcher = (Dispatcher) dispatchers.get(actionType); + + if (dispatcher == null) { + dispatcher = createDispatcher(dispatcherType, context); + dispatchers.put(actionType, dispatcher); + } + } - return CONTINUE_PROCESSING; + // Dispatch + Object result = dispatcher.dispatch(context); + processDispatchResult(result, context); + + return CONTINUE_PROCESSING; } + /** + * Retrieves the fully-qualified dispatcher class name to instantiate when + * an action mapping does not specify a dispatcher. + * + * @return the default dispatcher type (fully-qualified class name) + * @see #getDispatcherType(ActionContext) + * @see #setDefaultDispatcherType(String) + */ public final String getDefaultDispatcherType() { - return defaultDispatcherType; + return defaultDispatcherType; } + /** + * Retrieves the fully-qualified class name of the dispatcher to instantiate + * for the specified context. + * + * @param context the current action context + * @return the class name or <code>null</code> + * @see #getDefaultDispatcherType() + */ protected String getDispatcherType(ActionContext context) { - String dispatcherType = null; - if (context != null) { - dispatcherType = context.getActionConfig().getDispatcher(); - } - return (dispatcherType != null) ? dispatcherType : defaultDispatcherType; + String dispatcherType = null; + if (context != null) { + dispatcherType = context.getActionConfig().getDispatcher(); + } + return dispatcherType; } /** @@ -134,37 +154,43 @@ * @see ActionMapping#findRequiredForward(String) */ protected void processDispatchResult(Object result, ActionContext context) { - // Null means the response was handled directly - if (result == null) { - return; - } - - // A forward is the classical response - if (result instanceof ForwardConfig) { - context.setForwardConfig((ForwardConfig) result); - return; - } - - // String represents the name of a forward - ActionConfig actionConfig = context.getActionConfig(); - ActionMapping mapping = ((ActionMapping) actionConfig); - if (result instanceof String) { - context.setForwardConfig(mapping.findRequiredForward((String) result)); - return; - } - - // Select success if no return signature - if (result instanceof Void) { - context.setForwardConfig(mapping.findRequiredForward(Action.SUCCESS)); - return; - } + // Null means the response was handled directly + if (result == null) { + return; + } - // Unknown result type - throw new IllegalStateException("Unknown dispatch return type: " + result.getClass().getName()); + // A forward is the classical response + if (result instanceof ForwardConfig) { + context.setForwardConfig((ForwardConfig) result); + return; + } + + // String represents the name of a forward + ActionConfig actionConfig = context.getActionConfig(); + ActionMapping mapping = ((ActionMapping) actionConfig); + if (result instanceof String) { + context.setForwardConfig(mapping.findRequiredForward((String) result)); + return; + } + + // Select success if no return signature + if (result == void.class) { + context.setForwardConfig(mapping.findRequiredForward(Action.SUCCESS)); + return; + } + + // Unknown result type + throw new IllegalStateException("Unknown dispatch return type: " + result.getClass().getName()); } + /** + * Stores the optional default dispatcher type (fully-qualified class name). + * + * @param defaultDispatcherType the dispatcher type or <code>null</code> + * @see #getDefaultDispatcherType() + */ public final void setDefaultDispatcherType(String defaultDispatcherType) { - this.defaultDispatcherType = defaultDispatcherType; + this.defaultDispatcherType = defaultDispatcherType; } } Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractDispatcher.java Sun Dec 28 21:12:58 2008 @@ -34,7 +34,8 @@ import org.apache.commons.logging.LogFactory; /** - * TODO + * This abstract class is the stock template for {...@link Dispatcher} + * implementations. * * @version $Rev$ * @since Struts 1.4 @@ -57,11 +58,9 @@ public static final String CANCELLED_METHOD_NAME = "cancelled"; /** - * The name of the <code>unspecified</code> method. - * - * @see #unspecified(ActionContext) + * The name of the <code>execute</code> method. */ - public static final String UNSPECIFIED_METHOD_NAME = "unspecified"; + public static final String EXECUTE_METHOD_NAME = "execute"; /** * The message resources for this package. @@ -81,58 +80,71 @@ */ private transient final HashMap methods; + private final MethodResolver methodResolver; + /** - * Construct a new dispatcher. + * Constructs a new dispatcher with the specified method resolver. + * + * @param methodResolver the method resolver */ - public AbstractDispatcher() { - log = LogFactory.getLog(getClass()); - methods = new HashMap(); + public AbstractDispatcher(MethodResolver methodResolver) { + this.methodResolver = methodResolver; + log = LogFactory.getLog(getClass()); + methods = new HashMap(); } /** * Constructs the arguments that will be passed to the dispatched method. + * The construction is delegated to the method resolver instance. * * @param context the current action context * @param method the target method of this dispatch - * * @return the arguments array - * @see #dispatchMethod(ActionContext, Method, String) - */ - protected abstract Object[] buildMethodArguments(ActionContext context, Method method); + * @throws IllegalStateException if the method does not have a supported + * signature + * @see MethodResolver#buildArguments(ActionContext, Method) + */ + Object[] buildMethodArguments(ActionContext context, Method method) { + Object[] args = methodResolver.buildArguments(context, method); + if (args == null) { + throw new IllegalStateException("Unsupported method signature: " + method.toString()); + } + return args; + } public Object dispatch(ActionContext context) throws Exception { - // Resolve the method name; fallback to default if necessary - String methodName = resolveMethodName(context); - if ((methodName == null) || "".equals(methodName)) { - methodName = getDefaultMethodName(); - } - - // Ensure there is a specified method name to invoke. - // This may be null if the user hacks the query string. - if (methodName == null) { - return unspecified(context); - } - - // Identify the method object to dispatch - Method method; - try { - method = getMethod(context, methodName); - } catch (NoSuchMethodException e) { - // The log message reveals the offending method name... - String path = context.getActionConfig().getPath(); - String message = messages.getMessage(MSG_KEY_MISSING_METHOD_LOG, path, methodName); - log.error(message, e); - - // ...but the exception thrown does not - // See r383718 (XSS vulnerability) - String userMsg = messages.getMessage(MSG_KEY_MISSING_METHOD, path); - NoSuchMethodException e2 = new NoSuchMethodException(userMsg); - e2.initCause(e); - throw e2; - } + // Resolve the method name; fallback to default if necessary + String methodName = resolveMethodName(context); + if ((methodName == null) || "".equals(methodName)) { + methodName = getDefaultMethodName(); + } + + // Ensure there is a specified method name to invoke. + // This may be null if the user hacks the query string. + if (methodName == null) { + return unspecified(context); + } + + // Identify the method object to dispatch + Method method; + try { + method = getMethod(context, methodName); + } catch (NoSuchMethodException e) { + // The log message reveals the offending method name... + String path = context.getActionConfig().getPath(); + String message = messages.getMessage(MSG_KEY_MISSING_METHOD_LOG, path, methodName); + log.error(message, e); + + // ...but the exception thrown does not + // See r383718 (XSS vulnerability) + String userMsg = messages.getMessage(MSG_KEY_MISSING_METHOD, path); + NoSuchMethodException e2 = new NoSuchMethodException(userMsg); + e2.initCause(e); + throw e2; + } - // Invoke the named method and return its result - return dispatchMethod(context, method, methodName); + // Invoke the named method and return its result + return dispatchMethod(context, method, methodName); } /** @@ -146,10 +158,10 @@ * @see #buildMethodArguments(ActionContext, Method) */ protected final Object dispatchMethod(ActionContext context, Method method, String name) throws Exception { - Action target = context.getAction(); - String path = context.getActionConfig().getPath(); - Object[] args = buildMethodArguments(context, method); - return invoke(target, method, args, path); + Action target = context.getAction(); + String path = context.getActionConfig().getPath(); + Object[] args = buildMethodArguments(context, method); + return invoke(target, method, args, path); } /** @@ -158,22 +170,22 @@ * @see #getMethod(ActionContext, String) */ final void flushMethodCache() { - synchronized (methods) { - methods.clear(); - } + synchronized (methods) { + methods.clear(); + } } /** * Retrieves the name of the method to fallback upon if no method name can * be resolved. The default implementation returns - * {...@value #UNSPECIFIED_METHOD_NAME}. + * {...@link #EXECUTE_METHOD_NAME}. * * @return the fallback method name; can be <code>null</code> * @see #resolveMethodName(ActionContext) - * @see #UNSPECIFIED_METHOD_NAME + * @see #EXECUTE_METHOD_NAME */ protected String getDefaultMethodName() { - return UNSPECIFIED_METHOD_NAME; + return EXECUTE_METHOD_NAME; } /** @@ -189,23 +201,23 @@ * @see #flushMethodCache() */ protected final Method getMethod(ActionContext context, String methodName) throws NoSuchMethodException { - synchronized (methods) { - // Key the method based on the class-method combination - StringBuffer keyBuf = new StringBuffer(100); - keyBuf.append(context.getAction().getClass().getName()); - keyBuf.append(":"); - keyBuf.append(methodName); - String key = keyBuf.toString(); - - Method method = (Method) methods.get(key); - - if (method == null) { - method = resolveMethod(context, methodName); - methods.put(key, method); - } + synchronized (methods) { + // Key the method based on the class-method combination + StringBuffer keyBuf = new StringBuffer(100); + keyBuf.append(context.getAction().getClass().getName()); + keyBuf.append(":"); + keyBuf.append(methodName); + String key = keyBuf.toString(); + + Method method = (Method) methods.get(key); + + if (method == null) { + method = resolveMethod(context, methodName); + methods.put(key, method); + } - return method; - } + return method; + } } /** @@ -220,24 +232,28 @@ * @throws Exception if the dispatch fails with an exception */ protected final Object invoke(Object target, Method method, Object[] args, String path) throws Exception { - try { - return method.invoke(target, args); - } catch (IllegalAccessException e) { - String message = messages.getMessage(MSG_KEY_DISPATCH_ERROR, path); - log.error(message + ":" + method.getName(), e); - throw e; - } catch (InvocationTargetException e) { - // Rethrow the target exception if possible so that the - // exception handling machinery can deal with it - Throwable t = e.getTargetException(); - if (t instanceof Exception) { - throw (Exception) t; - } else { - String message = messages.getMessage(MSG_KEY_DISPATCH_ERROR, path); - log.error(message + ":" + method.getName(), e); - throw new Exception(t); - } - } + try { + Object retval = method.invoke(target, args); + if (method.getReturnType() == void.class) { + retval = void.class; + } + return retval; + } catch (IllegalAccessException e) { + String message = messages.getMessage(MSG_KEY_DISPATCH_ERROR, path); + log.error(message + ":" + method.getName(), e); + throw e; + } catch (InvocationTargetException e) { + // Rethrow the target exception if possible so that the + // exception handling machinery can deal with it + Throwable t = e.getTargetException(); + if (t instanceof Exception) { + throw (Exception) t; + } else { + String message = messages.getMessage(MSG_KEY_DISPATCH_ERROR, path); + log.error(message + ":" + method.getName(), e); + throw new Exception(t); + } + } } /** @@ -250,11 +266,10 @@ * @param context the current action context * @return <code>true</code> if the request is cancelled; otherwise * <code>false</code> - * @see org.apache.struts.taglib.html.CancelTag */ protected boolean isCancelled(ActionContext context) { - Boolean cancelled = context.getCancelled(); - return (cancelled != null) && cancelled.booleanValue(); + Boolean cancelled = context.getCancelled(); + return (cancelled != null) && cancelled.booleanValue(); } /** @@ -270,7 +285,9 @@ * @see #getMethod(ActionContext, String) * @see #invoke(Object, Method, Object[], String) */ - protected abstract Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException; + Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException { + return methodResolver.resolveMethod(context, methodName); + } /** * Decides the method name that can handle the request. @@ -280,7 +297,7 @@ * @see #getDefaultMethodName() * @see #resolveMethod(ActionContext, String) */ - protected abstract String resolveMethodName(ActionContext context); + abstract String resolveMethodName(ActionContext context); /** * Services the case when the dispatch fails because the method name cannot @@ -294,10 +311,10 @@ * @see #resolveMethodName(ActionContext) */ protected Object unspecified(ActionContext context) throws Exception { - ActionConfig config = context.getActionConfig(); - String msg = messages.getMessage(MSG_KEY_UNSPECIFIED, config.getPath()); - log.error(msg); - throw new IllegalStateException(msg); + ActionConfig config = context.getActionConfig(); + String msg = messages.getMessage(MSG_KEY_UNSPECIFIED, config.getPath()); + log.error(msg); + throw new IllegalStateException(msg); } } Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractEventMappingDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractEventMappingDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractEventMappingDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractEventMappingDispatcher.java Sun Dec 28 21:12:58 2008 @@ -41,8 +41,8 @@ * <p> * The <em>default</em> key is purely optional, but encouraged when nothing * matches (such as the user pressing the enter key). If this is not specified - * and no parameters match the list of method keys, <code>null</code> is - * returned which means the <code>unspecified</code> method will be invoked. + * and no parameters match the list of method keys, the method resolution + * returns <code>null</code>. * <p> * The order of the parameters are guaranteed to be iterated in the order * specified. If multiple buttons were accidently submitted, the first match in @@ -60,6 +60,15 @@ protected static final String DEFAULT_METHOD_KEY = "default"; /** + * Constructs a new dispatcher with the specified method resolver. + * + * @param methodResolver the method resolver + */ + public AbstractEventMappingDispatcher(MethodResolver methodResolver) { + super(methodResolver); + } + + /** * Determines whether the specified method key is a submission parameter. * * @param context the current action context @@ -70,38 +79,40 @@ protected abstract boolean isSubmissionParameter(ActionContext context, String methodKey); protected final String resolveMethodName(ActionContext context) { - // Obtain the mapping parameter - String parameter = super.resolveMethodName(context); - assert parameter != null; - - // Parse it as a comma-separated list - StringTokenizer st = new StringTokenizer(parameter, ","); - String defaultMethodName = null; - - while (st.hasMoreTokens()) { - String methodKey = st.nextToken().trim(); - String methodName = methodKey; - - // The key can either be a direct method name or an alias - // to a method as indicated by a "key=value" signature - int equals = methodKey.indexOf('='); - if (equals > -1) { - methodName = methodKey.substring(equals + 1).trim(); - methodKey = methodKey.substring(0, equals).trim(); - } - - // Set the default if it passes by - if (methodKey.equals(DEFAULT_METHOD_KEY)) { - defaultMethodName = methodName; - } - - // Is it a match? - if (isSubmissionParameter(context, methodKey)) { - return methodName; - } - } + // Obtain the mapping parameter + String mappingParameter = super.resolveMethodName(context); + if (mappingParameter == null) { + return null; + } + + // Parse it as a comma-separated list + StringTokenizer st = new StringTokenizer(mappingParameter, ","); + String defaultMethodName = null; + + while (st.hasMoreTokens()) { + String methodKey = st.nextToken().trim(); + String methodName = methodKey; + + // The key can either be a direct method name or an alias + // to a method as indicated by a "key=value" signature + int equals = methodKey.indexOf('='); + if (equals > -1) { + methodName = methodKey.substring(equals + 1).trim(); + methodKey = methodKey.substring(0, equals).trim(); + } + + // Set the default if it passes by + if (methodKey.equals(DEFAULT_METHOD_KEY)) { + defaultMethodName = methodName; + } + + // Is it a match? + if (isSubmissionParameter(context, methodKey)) { + return methodName; + } + } - return defaultMethodName; + return defaultMethodName; } } Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMappingDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMappingDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMappingDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMappingDispatcher.java Sun Dec 28 21:12:58 2008 @@ -26,37 +26,77 @@ /** * This abstract class is a template for choosing the target method that is * named by the <code>parameter</code> attribute of the corresponding - * {...@link ActionMapping}. + * {...@link ActionMapping}. The attribute value, if not provided, defaults to + * <code>execute</code>. * * @version $Rev$ * @since Struts 1.4 */ public abstract class AbstractMappingDispatcher extends AbstractDispatcher { + private String defaultMappingParameter; + + /** + * Constructs a new dispatcher with the specified method resolver. + * + * @param methodResolver the method resolver + */ + public AbstractMappingDispatcher(MethodResolver methodResolver) { + super(methodResolver); + setDefaultMappingParameter(EXECUTE_METHOD_NAME); + } + + /** + * Retrieves the default mapping parameter value. This value is used by + * {...@link #resolveMethodName(ActionContext)} if the mapping did not provide + * a value. + * + * @return the mapping parameter value or <code>null</code> + * @see #setDefaultMappingParameter(String) + */ + protected final String getDefaultMappingParameter() { + return defaultMappingParameter; + } + /** * Resolves the method name by obtaining the <code>parameter</code> * attribute from the {...@link ActionMapping}. * * @param context {...@inheritdoc} - * @throws IllegalStateException if the parameter is absent + * @throws IllegalStateException if the parameter cannot be resolved * @return the parameter attribute value */ protected String resolveMethodName(ActionContext context) { - // Null out an empty string parameter - ActionMapping mapping = (ActionMapping) context.getActionConfig(); - String parameter = mapping.getParameter(); - if ("".equals(parameter)) { - parameter = null; - } - - // Parameter is required - if ((parameter == null)) { - String message = messages.getMessage(MSG_KEY_MISSING_MAPPING_PARAMETER, mapping.getPath()); - log.error(message); - throw new IllegalStateException(message); - } + // Null out an empty string parameter + ActionMapping mapping = (ActionMapping) context.getActionConfig(); + String parameter = mapping.getParameter(); + if ("".equals(parameter)) { + parameter = null; + } + + // Assign the default if the mapping did not provide a value + if (parameter == null) { + parameter = defaultMappingParameter; + } - return parameter; + // Parameter is required + if (parameter == null) { + String message = messages.getMessage(MSG_KEY_MISSING_MAPPING_PARAMETER, mapping.getPath()); + log.error(message); + throw new IllegalStateException(message); + } + + return parameter; + } + + /** + * Stores the new default mapping parameter value. + * + * @param defaultMappingParameter the parameter value + * @see #getDefaultMappingParameter() + */ + protected final void setDefaultMappingParameter(String defaultMappingParameter) { + this.defaultMappingParameter = defaultMappingParameter; } } Added: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMethodResolver.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMethodResolver.java?rev=729832&view=auto ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMethodResolver.java (added) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMethodResolver.java Sun Dec 28 21:12:58 2008 @@ -0,0 +1,91 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts.dispatcher; + +import org.apache.struts.chain.contexts.ActionContext; + +import java.lang.reflect.Method; + +/** + * This abstract class is the stock template for resolving methods for a + * {...@link Dispatcher}. The implementation handles methods that accept no + * arguments or only an {...@link ActionContext} instance: + * <ul> + * <li><code><i>return_type</i> execute()</code></li> + * <li><code><i>return_type</i> execute(ActionContext context)</code></li> + * </ul> + * + * @version $Rev$ + * @since Struts 1.4 + */ +public abstract class AbstractMethodResolver implements MethodResolver { + + /** + * The argument listing for a method without arguments. + * + * @see #EMPTY_ARGUMENT_TYPES + */ + protected static final Object[] EMPTY_ARGUMENTS = new Object[] {}; + + /** + * The type listing used for a method without arguments. + * + * @see #EMPTY_ARGUMENTS + */ + protected static final Class[] EMPTY_ARGUMENT_TYPES = new Class[] {}; + + /** + * The argument listing for a method accepting the {...@link ActionContext}. + */ + private static final Class[] ACTION_CONTEXT_ARGUMENT_TYPES = new Class[] { ActionContext.class }; + + public Object[] buildArguments(ActionContext context, Method method) { + Class[] parameterTypes = method.getParameterTypes(); + switch (parameterTypes.length) { + case 0: + return EMPTY_ARGUMENTS; + + case 1: + if (parameterTypes[0].isInstance(context)) { + return new Object[] { context }; + } + return null; + + default: + return null; + } + } + + public Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException { + Class actionClass = context.getAction().getClass(); + + // Does the method accept nothing? + try { + return actionClass.getDeclaredMethod(methodName, EMPTY_ARGUMENT_TYPES); + } catch (NoSuchMethodException e) { + // continue + } + + // Does the method accept the action context? + return actionClass.getDeclaredMethod(methodName, ACTION_CONTEXT_ARGUMENT_TYPES); + } + +} Propchange: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMethodResolver.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractMethodResolver.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractParameterDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractParameterDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractParameterDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/AbstractParameterDispatcher.java Sun Dec 28 21:12:58 2008 @@ -39,16 +39,25 @@ protected static final String DEFAULT_PARAMETER_NAME = "method"; /** + * Constructs a new dispatcher with the specified method resolver. + * + * @param methodResolver the method resolver + */ + public AbstractParameterDispatcher(MethodResolver methodResolver) { + super(methodResolver); + } + + /** * Retrieves the name of the parameter to fallback upon if the action * configuration did not set the <code>parameter</code> attribute. The - * default implementation returns {...@value #DEFAULT_PARAMETER_NAME}. + * default implementation returns {...@link #DEFAULT_PARAMETER_NAME}. * * @return the fallback parameter name; can be <code>null</code> * @see #DEFAULT_PARAMETER_NAME * @see #getParameter(ActionContext) */ protected String getDefaultParameterName() { - return DEFAULT_PARAMETER_NAME; + return DEFAULT_PARAMETER_NAME; } /** @@ -63,24 +72,24 @@ * @see #getDefaultParameterName() */ protected String getParameter(ActionContext context) { - String parameter = context.getActionConfig().getParameter(); - if ((parameter != null) && (parameter.trim().length() > 0)) { - return parameter; - } - return getDefaultParameterName(); + String parameter = context.getActionConfig().getParameter(); + if ((parameter != null) && (parameter.trim().length() > 0)) { + return parameter; + } + return getDefaultParameterName(); } protected final String resolveMethodName(ActionContext context) { - String parameter = getParameter(context); - if ("".equals(parameter)) { - parameter = null; - } - - if (parameter != null) { - return resolveParameterValue(context, parameter); - } + String parameter = getParameter(context); + if ("".equals(parameter)) { + parameter = null; + } + + if (parameter != null) { + return resolveParameterValue(context, parameter); + } - return null; + return null; } /** Added: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/MethodResolver.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/MethodResolver.java?rev=729832&view=auto ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/MethodResolver.java (added) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/MethodResolver.java Sun Dec 28 21:12:58 2008 @@ -0,0 +1,61 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts.dispatcher; + +import org.apache.struts.chain.contexts.ActionContext; + +import java.lang.reflect.Method; + +/** + * This interface defines a pluggable strategy for resolving a method and + * extracting its methods from an {...@link ActionContext}. A {...@link Dispatcher} + * may use the resolver to separate out its method resolution based on + * technology (such as servlets, portlets, etc.). + * + * @version $Rev$ + * @since Struts 1.4 + */ +public interface MethodResolver { + + /** + * Constructs the arguments that will be passed to the dispatched method. + * + * @param context the current action context + * @param method the target method of this dispatch + * + * @return the arguments array for the method, or <code>null</code> if the + * method does not match any supported signatures + * @see #resolveMethod(ActionContext, String) + */ + Object[] buildArguments(ActionContext context, Method method); + + /** + * Decides the appropriate method instance for the specified method name. + * Implementations may introspect for any desired method signature. + * + * @param context the current action context + * @param methodName the method name to use for introspection + * @return the method to invoke + * @throws NoSuchMethodException if an appropriate method cannot be found + */ + Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException; + +} \ No newline at end of file Propchange: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/MethodResolver.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/MethodResolver.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletEventMappingDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletEventMappingDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletEventMappingDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletEventMappingDispatcher.java Sun Dec 28 21:12:58 2008 @@ -25,8 +25,6 @@ import org.apache.struts.chain.contexts.ServletActionContext; import org.apache.struts.dispatcher.AbstractEventMappingDispatcher; -import java.lang.reflect.Method; - import javax.servlet.http.HttpServletRequest; /** @@ -59,8 +57,11 @@ private static final long serialVersionUID = 1L; - protected Object[] buildMethodArguments(ActionContext context, Method method) { - return ServletDispatchUtils.buildClassicExecuteArguments((ServletActionContext) context); + /** + * Constructs a new servlet event mapping dispatcher. + */ + public ServletEventMappingDispatcher() { + super(new ServletMethodResolver()); } /** @@ -68,12 +69,8 @@ * suffixes (.x/.y), the method name has been found. */ protected boolean isSubmissionParameter(ActionContext context, String methodKey) { - HttpServletRequest request = ((ServletActionContext) context).getRequest(); - return (request.getParameter(methodKey) != null) || (request.getParameter(methodKey + ".x") != null); - } - - protected Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException { - return ServletDispatchUtils.resolveClassicExecuteMethod(context, methodName); + HttpServletRequest request = ((ServletActionContext) context).getRequest(); + return (request.getParameter(methodKey) != null) || (request.getParameter(methodKey + ".x") != null); } } Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMappingDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMappingDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMappingDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMappingDispatcher.java Sun Dec 28 21:12:58 2008 @@ -24,8 +24,6 @@ import org.apache.struts.chain.contexts.ServletActionContext; import org.apache.struts.dispatcher.AbstractMappingDispatcher; -import java.lang.reflect.Method; - import javax.servlet.http.HttpServletResponse; /** @@ -39,16 +37,11 @@ * For example, a single action may manage a subscription process by defining * the following methods: * <ul> - * <li><code>public ActionForward create(ActionMapping mapping, ActionForm form, - * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> - * <li><code>public ActionForward delete(ActionMapping mapping, ActionForm form, - * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> - * <li><code>public ActionForward edit(ActionMapping mapping, ActionForm form, - * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> - * <li><code>public ActionForward list(ActionMapping mapping, ActionForm form, - * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> - * <li><code>public ActionForward save(ActionMapping mapping, ActionForm form, - * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> + * <li><code>public void create(ActionContext context)</code></li> + * <li><code>public void delete(ActionContext context)</code></li> + * <li><code>public String edit(ActionContext context)</code></li> + * <li><code>public ActionForward list(ActionContext context)</code></li> + * <li><code>public String save(ActionContext context)</code></li> * </ul> * for which a corresponding configuration would exist: * @@ -65,9 +58,9 @@ * dispatcher="org.apache.struts.dispatcher.servlet.ServletMappingDispatcher" * parameter="delete" * name="subscriptionForm" - * scope="request" - * input="/subscription.jsp"> + * scope="request"> * <forward path="/deletedSubscription.jsp"/> + * <forward name="input" path="/subscription.jsp" * </action> * * <action path="/editSubscription" @@ -90,9 +83,9 @@ * parameter="save" * name="subscriptionForm" * scope="request" - * validate="true" - * input="/editSubscription.jsp"> + * validate="true"> * <forward path="/savedSubscription.jsp"/> + * <forward name="input" path="/editSubscription.jsp" * </action> * </code></pre> * @@ -103,12 +96,11 @@ private static final long serialVersionUID = 1L; - protected Object[] buildMethodArguments(ActionContext context, Method method) { - return ServletDispatchUtils.buildClassicExecuteArguments((ServletActionContext) context); - } - - protected Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException { - return ServletDispatchUtils.resolveClassicExecuteMethod(context, methodName); + /** + * Constructs a new servlet mapping dispatcher. + */ + public ServletMappingDispatcher() { + super(new ServletMethodResolver()); } /** @@ -117,9 +109,9 @@ * @return always <code>null</code> since the response is handled directly */ protected Object unspecified(ActionContext context) throws Exception { - HttpServletResponse response = ((ServletActionContext) context).getResponse(); - response.sendError(HttpServletResponse.SC_NOT_FOUND); - return null; + HttpServletResponse response = ((ServletActionContext) context).getResponse(); + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return null; } } Added: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMethodResolver.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMethodResolver.java?rev=729832&view=auto ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMethodResolver.java (added) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMethodResolver.java Sun Dec 28 21:12:58 2008 @@ -0,0 +1,132 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts.dispatcher.servlet; + +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.chain.contexts.ActionContext; +import org.apache.struts.chain.contexts.ServletActionContext; +import org.apache.struts.config.ActionConfig; +import org.apache.struts.dispatcher.AbstractMethodResolver; + +import java.lang.reflect.Method; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * This class helps resolve methods with servlet-based signatures. The following + * examples illustrate the method signatures supported: + * <ul> + * <li><code><i>return_type</i> execute()</code></li> + * <li><code><i>return_type</i> execute(ActionContext context)</code></li> + * <li><code><i>return_type</i> execute(ServletActionContext context)</code></li> + * <li><code><i>return_type</i> execute(ActionMapping mapping, ActionForm form, + HttpServletRequest request, HttpServletResponse response)</code></li> + * </ul> + * + * @version $Rev$ + * @since Struts 1.4 + */ +public class ServletMethodResolver extends AbstractMethodResolver { + + /** + * The set of argument type classes for the classic reflected method call. + * These are the same for all calls, so calculate them only once. + * + * @see org.apache.struts.action.Action#execute(ActionMapping, ActionForm, + * javax.servlet.ServletRequest, javax.servlet.ServletResponse) + */ + private static final Class[] CLASSIC_EXECUTE_SIGNATURE = { ActionMapping.class, ActionForm.class, + HttpServletRequest.class, HttpServletResponse.class }; + + public Object[] buildArguments(ActionContext context, Method method) { + Object[] args = super.buildArguments(context, method); + if (args == null) { + Class[] parameterTypes = method.getParameterTypes(); + switch (parameterTypes.length) { + case 4: + return buildClassicArguments((ServletActionContext) context); + default: + break; + } + } + return args; + } + + /** + * Constructs the arguments to invoke the classic <code>execute</code> + * servlet signature from the specified context. + * + * @param context the current context + * @return the arguments array + * @throws NullPointerException if context is <code>null</code> + * @see #resolveClassicMethod(ActionContext, String) + */ + protected final Object[] buildClassicArguments(ServletActionContext context) { + ActionConfig mapping = context.getActionConfig(); + ActionForm form = context.getActionForm(); + HttpServletRequest request = context.getRequest(); + HttpServletResponse response = context.getResponse(); + return new Object[] { mapping, form, request, response }; + } + + /** + * Obtains the method instance with the classic <code>execute</code> + * servlet signature. + * + * @param context the current context + * @param methodName the method name to introspect + * @return the found method in the action + * @throws NullPointerException if context is <code>null</code> + * @throws NoSuchMethodException if the method does not exist + * @see #buildClassicArguments(ServletActionContext) + * @see org.apache.struts.action.Action#execute(ActionMapping, ActionForm, + * javax.servlet.ServletRequest, javax.servlet.ServletResponse) + */ + protected final Method resolveClassicMethod(ActionContext context, String methodName) throws NoSuchMethodException { + Class actionClass = context.getAction().getClass(); + return actionClass.getMethod(methodName, CLASSIC_EXECUTE_SIGNATURE); + } + + public Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException { + // First try to resolve anything the superclass supports + try { + return super.resolveMethod(context, methodName); + } catch (NoSuchMethodException e) { + // continue + } + + // Can the method accept the servlet action context? + if (context instanceof ServletActionContext) { + try { + Class actionClass = context.getAction().getClass(); + return actionClass.getDeclaredMethod(methodName, new Class[] { ServletActionContext.class }); + } catch (NoSuchMethodException e) { + // continue + } + } + + // Lastly, try the classical argument listing + return resolveClassicMethod(context, methodName); + } + +} Propchange: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMethodResolver.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletMethodResolver.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Modified: struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletParameterDispatcher.java URL: http://svn.apache.org/viewvc/struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletParameterDispatcher.java?rev=729832&r1=729831&r2=729832&view=diff ============================================================================== --- struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletParameterDispatcher.java (original) +++ struts/struts1/trunk/core/src/main/java/org/apache/struts/dispatcher/servlet/ServletParameterDispatcher.java Sun Dec 28 21:12:58 2008 @@ -20,13 +20,10 @@ */ package org.apache.struts.dispatcher.servlet; -import org.apache.struts.action.Action; import org.apache.struts.chain.contexts.ActionContext; import org.apache.struts.chain.contexts.ServletActionContext; import org.apache.struts.dispatcher.AbstractParameterDispatcher; -import java.lang.reflect.Method; - import javax.servlet.http.HttpServletResponse; /** @@ -49,20 +46,14 @@ * </code> * <p> * This example will use the value of the request parameter named "method" to - * pick the appropriate method, which must have the same signature (other than - * method name) of the standard - * {...@link Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, - * javax.servlet.ServletRequest, javax.servlet.ServletResponse) Action.execute} - * method. For example, you might have the following three methods in the same - * action: + * pick the appropriate method. For example, you might have the following three + * methods in the same action: * * <ul> * <li><code>public ActionForward delete(ActionMapping mapping, ActionForm form, * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> - * <li><code>public ActionForward insert(ActionMapping mapping, ActionForm form, - * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> - * <li><code>public ActionForward update(ActionMapping mapping, ActionForm form, - * HttpServletRequest request, HttpServletResponse response) throws Exception</code></li> + * <li><code>public void insert(ActionContext context)</code></li> + * <li><code>public void update(ServletActionContext context)</code></li> * </ul> * and call one of the methods with a URL like this: * <p> @@ -75,12 +66,11 @@ private static final long serialVersionUID = 1L; - protected Object[] buildMethodArguments(ActionContext context, Method method) { - return ServletDispatchUtils.buildClassicExecuteArguments((ServletActionContext) context); - } - - protected Method resolveMethod(ActionContext context, String methodName) throws NoSuchMethodException { - return ServletDispatchUtils.resolveClassicExecuteMethod(context, methodName); + /** + * Constructs a new servlet parameter dispatcher. + */ + public ServletParameterDispatcher() { + super(new ServletMethodResolver()); } /** @@ -91,8 +81,8 @@ * @return the servlet parameter value */ protected String resolveParameterValue(ActionContext context, String parameter) { - ServletActionContext servletContext = (ServletActionContext) context; - return (String) servletContext.getParam().get(parameter); + ServletActionContext servletContext = (ServletActionContext) context; + return (String) servletContext.getParam().get(parameter); } /** @@ -103,9 +93,9 @@ * @throws Exception if the error code fails to set */ protected Object unspecified(ActionContext context) throws Exception { - HttpServletResponse response = ((ServletActionContext) context).getResponse(); - response.sendError(HttpServletResponse.SC_NOT_FOUND); - return null; + HttpServletResponse response = ((ServletActionContext) context).getResponse(); + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return null; } }