Author: germuska Date: Fri Apr 15 09:33:24 2005 New Revision: 161497 URL: http://svn.apache.org/viewcvs?view=rev&rev=161497 Log: Add the ability to FormBeanConfig to create an ActionForm based on an ActionContext instead of a Servlet, hiding a dependency on the Servlet API.
This enables the elimination of o.a.s.chain.commands.AbstractCreateForm and it's subclass in the o.a.s.c.c.servlet package. These are deprecated, but will be removed before the first Struts 1.3 release, but are preserved temporarily to smooth the transition for users who have customized the default chain-config.xml file. (In that file, "org.apache.struts.chain.commands.servlet.CreateActionForm" should be changed to "org.apache.struts.chain.commands.CreateActionForm". Change chain-config.xml to use new version of CreateActionForm. Add convenience method to ActionContextBase to findOrCreateForm given a form name and scope name, since it contains all the necessary referents to achieve that. Add logger property to ActionContextBase, with default initialization strategy. Create generic command which can use configuration parameters to lookup or create an ActionForm and place it in the ActionContext under a specified key, so that a subsequent command can prepopulate fields. Centralize logic to determine whether a given ActionForm instance can be "re-used" for a given FormBeanConfig as an instance method of FormBeanConfig. Adjust RequestUtils to use newly centralized logic. Added: struts/core/trunk/src/share/org/apache/struts/chain/commands/CreateActionForm.java (with props) struts/core/trunk/src/share/org/apache/struts/chain/commands/generic/CopyFormToContext.java (with props) struts/core/trunk/src/test/org/apache/struts/chain/commands/generic/TestCopyFormToContext.java (with props) Modified: struts/core/trunk/conf/share/chain-config.xml struts/core/trunk/src/share/org/apache/struts/chain/commands/AbstractCreateActionForm.java struts/core/trunk/src/share/org/apache/struts/chain/commands/servlet/CreateActionForm.java struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContext.java struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContextBase.java struts/core/trunk/src/share/org/apache/struts/config/FormBeanConfig.java struts/core/trunk/src/share/org/apache/struts/util/RequestUtils.java Modified: struts/core/trunk/conf/share/chain-config.xml URL: http://svn.apache.org/viewcvs/struts/core/trunk/conf/share/chain-config.xml?view=diff&r1=161496&r2=161497 ============================================================================== --- struts/core/trunk/conf/share/chain-config.xml (original) +++ struts/core/trunk/conf/share/chain-config.xml Fri Apr 15 09:33:24 2005 @@ -141,7 +141,7 @@ <!-- Create (if needed) the ActionForm for this request --> <command - className="org.apache.struts.chain.commands.servlet.CreateActionForm"/> + className="org.apache.struts.chain.commands.CreateActionForm"/> <!-- Populate the ActionForm for this request --> Modified: struts/core/trunk/src/share/org/apache/struts/chain/commands/AbstractCreateActionForm.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/chain/commands/AbstractCreateActionForm.java?view=diff&r1=161496&r2=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/chain/commands/AbstractCreateActionForm.java (original) +++ struts/core/trunk/src/share/org/apache/struts/chain/commands/AbstractCreateActionForm.java Fri Apr 15 09:33:24 2005 @@ -32,6 +32,9 @@ * * @author Craig R. McClanahan * @version $Id$ + * @deprecated This class no longer needs to be abstract. Use + * <code>org.apache.struts.chain.commands.CreateActionForm</code> instead. + * This will be removed BEFORE a full Struts 1.3.0 release. */ public abstract class AbstractCreateActionForm extends ActionCommandBase { Added: struts/core/trunk/src/share/org/apache/struts/chain/commands/CreateActionForm.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/chain/commands/CreateActionForm.java?view=auto&rev=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/chain/commands/CreateActionForm.java (added) +++ struts/core/trunk/src/share/org/apache/struts/chain/commands/CreateActionForm.java Fri Apr 15 09:33:24 2005 @@ -0,0 +1,105 @@ +/* + * Copyright 2003,2004 The Apache Software Foundation. + * + * Licensed 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.chain.commands; + + +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.struts.action.ActionForm; +import org.apache.struts.chain.contexts.ActionContext; +import org.apache.struts.chain.contexts.ServletActionContext; +import org.apache.struts.config.ActionConfig; +import org.apache.struts.config.FormBeanConfig; + + +/** + * <p>Create (if necessary) and cache a form bean for this request.</p> + * + * @version $Id$ + */ +public class CreateActionForm extends ActionCommandBase { + + + // ------------------------------------------------------ Instance Variables + + + private static final Log log = + LogFactory.getLog(CreateActionForm.class); + + // ---------------------------------------------------------- Public Methods + + + /** + * <p>Create (if necessary) and cache a form bean for this request.</p> + * + * @param context The <code>Context</code> for the current request + * + * @return <code>false</code> so that processing continues + */ + public boolean execute(ActionContext actionCtx) throws Exception { + + // Is there a form bean associated with this ActionConfig? + ActionConfig actionConfig = actionCtx.getActionConfig(); + String name = actionConfig.getName(); + if (name == null) { + actionCtx.setActionForm(null); + return (false); + } + + log.trace("Look up form-bean " + name); + + // Look up the corresponding FormBeanConfig (if any) + FormBeanConfig formBeanConfig = + actionConfig.getModuleConfig().findFormBeanConfig(name); + if (formBeanConfig == null) { + log.warn("No FormBeanConfig found in module " + + actionConfig.getModuleConfig().getPrefix() + + " under name " + name); + actionCtx.setActionForm(null); + return (false); + } + + Map scope = actionCtx.getScope(actionConfig.getScope()); + + ActionForm instance = null; + instance = (ActionForm) scope.get(actionConfig.getAttribute()); + + // Can we recycle the existing instance (if any)? + if (!formBeanConfig.canReuse(instance)) { + instance = formBeanConfig.createActionForm(actionCtx); + } + + // TODO Remove this when ActionForm no longer directly depends on ActionServlet + if (actionCtx instanceof ServletActionContext) { + // The servlet property of ActionForm is transient, so + // ActionForms which are restored from a serialized state + // need to have their servlet restored. + ServletActionContext sac = (ServletActionContext) actionCtx; + instance.setServlet(sac.getActionServlet()); + } + + actionCtx.setActionForm(instance); + + scope.put(actionConfig.getAttribute(), instance); + + return (false); + + } + +} Propchange: struts/core/trunk/src/share/org/apache/struts/chain/commands/CreateActionForm.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/core/trunk/src/share/org/apache/struts/chain/commands/CreateActionForm.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/core/trunk/src/share/org/apache/struts/chain/commands/generic/CopyFormToContext.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/chain/commands/generic/CopyFormToContext.java?view=auto&rev=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/chain/commands/generic/CopyFormToContext.java (added) +++ struts/core/trunk/src/share/org/apache/struts/chain/commands/generic/CopyFormToContext.java Fri Apr 15 09:33:24 2005 @@ -0,0 +1,203 @@ +/* + * $Id$ + * + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.chain.commands.generic; + +import org.apache.struts.action.ActionForm; +import org.apache.struts.chain.commands.ActionCommandBase; +import org.apache.struts.chain.contexts.ActionContext; +import org.apache.struts.chain.contexts.ActionContextBase; +import org.apache.struts.config.ActionConfig; + +/** + * <p>Subclass this command and configure it as part of a per-forward chain to + * perform any necessary pre-population or other preparation for a form before + * control is dispatched to the view layer.</p> + * + * @version $Id$ + */ +public class CopyFormToContext extends ActionCommandBase { + + // ------------------------------------------------------ Instance Variables + + /** + * <p>The name of a form bean as configured in a <code>struts-config.xml</code> file + * for this module. </p> + + * <p> + * Either <code>actionPath</code> or both this and <code>scope</code> are required + * configuration properties.</p> + */ + private String formName = null; + + /** + * <p>The name of a scope, such as "request" or "session" in which + * the form to be prepared will be placed for reference by the view and + * other parts of Struts.</p> + + * <p> + * Either <code>actionPath</code> or both this and <code>formName</code> are required + * configuration properties.</p> + */ + private String scope = null; + + /** + * <p>The path of an <code><action></code> mapping as configured in a <code>struts-config.xml</code> file + * for this module. This action will be looked up, and its <code>name</code> and + * <code>scope</code> values will be used as if those values were configured directly + * in this instance's <code>formName</code> and <code>scope</code> properties.</p> + + * <p>Either <code>this</code> or both <code>scope</code> and <code>formName</code> are required + * configuration properties.</p> + */ + private String actionPath = null; + + /** + * The context key under which the form which was looked up will be stored. Defaults + * to "actionForm" but may be overridden in cases where the "request" ActionForm must + * be preserved. + */ + private String toKey = ActionContextBase.ACTION_FORM_KEY; + + // ------------------------------------------------------ Properties + public String getActionPath() { + return this.actionPath; + } + public void setActionPath(String actionPath) { + this.actionPath = actionPath; + } + public String getFormName() { + return this.formName; + } + public void setFormName(String formName) { + this.formName = formName; + } + public String getScope() { + return this.scope; + } + public void setScope(String scope) { + this.scope = scope; + } + public String getToKey() { + return this.toKey; + } + public void setToKey(String toKey) { + this.toKey = toKey; + } + + + // ------------------------------------------------------ + + /** + * <p>Look up an ActionForm instance based on the configured properties of this command and + * copy it into the <code>Context</code>. After this command successfully + * executes, an ActionForm instance will exist in the specified scope + * and will be available, for example for backing fields in an HTML form. It + * will also be in the <code>ActionContext</code> available for another command + * to do prepopulation of values or other preparation.</p> + */ + public boolean execute(ActionContext actionContext) throws Exception { + + ActionForm form = findOrCreateForm(actionContext); + if (isEmpty(getToKey())) { + throw new IllegalStateException("Property 'toKey' must be defined."); + } + actionContext.put(getToKey(), form); + return false; + } + + /** + * <p>Based on the properties of this command and the given <code>ActionContext</code>, + * find or create an ActionForm instance for preparation.</p> + * + * @param context + * @return + * @throws IllegalAccessException + * @throws InstantiationException + */ + protected ActionForm findOrCreateForm(ActionContext context) throws IllegalAccessException, InstantiationException { + String effectiveFormName = null; + String effectiveScope = null; + if ( !(isEmpty(this.getActionPath())) ) { + + ActionConfig actionConfig = context.getModuleConfig().findActionConfig(this.getActionPath()); + + if (actionConfig == null) { + throw new IllegalArgumentException("No ActionConfig found for path " + this.getActionPath()); + } + + effectiveFormName = actionConfig.getName(); + effectiveScope = actionConfig.getScope(); + + } else { + effectiveFormName = this.getFormName(); + effectiveScope = this.getScope(); + } + if (isEmpty(effectiveScope) || isEmpty(effectiveFormName)) { + throw new IllegalStateException("Both scope [" + + effectiveScope + + "] and formName [" + + effectiveFormName + + "] must be defined."); + } + + return findOrCreateForm(context, effectiveFormName, effectiveScope); + } + + /** + * Actually find or create an instance of ActionForm configured under + * the form-bean-name <code>effectiveFormName</code>, looking in + * in the <code>ActionContext's</code> scope as identified by <code>effectiveScope</code>. + * If a form is created, it will also be stored in that scope. + * + * <p><b>NOTE:</b> This specific method depends on the instance of + * <code>ActionContext</code> which is passed being a subclass of + * <code>ActionContextBase</code>, which implements the utility method + * <code>findOrCreateActionForm</code>. + * </p> + * + * @param ctx + * @param effectiveFormName + * @param effectiveScope + * @return + * @throws IllegalAccessException + * @throws InstantiationException + * @throws IllegalArgumentException + */ + protected ActionForm findOrCreateForm(ActionContext ctx, String effectiveFormName, String effectiveScope) throws IllegalAccessException, InstantiationException, IllegalArgumentException { + ActionContextBase context; + try { + context = (ActionContextBase) ctx; + } catch (ClassCastException e) { + throw new IllegalStateException("ActionContext [" + ctx + "] must be subclass of ActionContextBase"); + } + ActionForm form = context.findOrCreateActionForm(effectiveFormName, effectiveScope); + if (form == null) { + throw new IllegalArgumentException("No form found under scope [" + + effectiveScope + + "] and formName [" + + effectiveFormName + + "]"); + } + return form; + } + private boolean isEmpty(String test) { + return test == null || test.trim().length() == 0; + } +} + Propchange: struts/core/trunk/src/share/org/apache/struts/chain/commands/generic/CopyFormToContext.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/core/trunk/src/share/org/apache/struts/chain/commands/generic/CopyFormToContext.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Modified: struts/core/trunk/src/share/org/apache/struts/chain/commands/servlet/CreateActionForm.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/chain/commands/servlet/CreateActionForm.java?view=diff&r1=161496&r2=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/chain/commands/servlet/CreateActionForm.java (original) +++ struts/core/trunk/src/share/org/apache/struts/chain/commands/servlet/CreateActionForm.java Fri Apr 15 09:33:24 2005 @@ -27,6 +27,11 @@ import org.apache.struts.config.ActionConfig; import org.apache.struts.config.FormBeanConfig; +/** + * @deprecated This class and its parent are no longer necessary. Use + * <code>org.apache.struts.chain.commands.CreateActionForm</code> instead. + * This will be removed BEFORE a full Struts 1.3.0 release. + */ public class CreateActionForm extends org.apache.struts.chain.commands.AbstractCreateActionForm { Modified: struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContext.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContext.java?view=diff&r1=161496&r2=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContext.java (original) +++ struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContext.java Fri Apr 15 09:33:24 2005 @@ -95,6 +95,16 @@ Map getRequestScope(); /** + * Return the Map representing the scope identified by <code>scopeName</code>. + * Implementations should support at minimum the names associated with the constants + * <code>APPLICATION_SCOPE</code>, <code>SESSION_SCOPE</code>, and + * <code>REQUEST_SCOPE</code>, but are permitted to support others as well. + * @param scopeName + * @return + */ + Map getScope(String scopeName); + + /** * <p>Return a <code>Map</code> of parameters submitted by the user * as part of this request.</p> * Modified: struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContextBase.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContextBase.java?view=diff&r1=161496&r2=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContextBase.java (original) +++ struts/core/trunk/src/share/org/apache/struts/chain/contexts/ActionContextBase.java Fri Apr 15 09:33:24 2005 @@ -21,17 +21,19 @@ import java.util.Map; import org.apache.commons.chain.Context; -import org.apache.struts.action.ActionMessages; -import org.apache.struts.util.MessageResources; import org.apache.commons.chain.impl.ContextBase; -import org.apache.struts.util.TokenProcessor; -import org.apache.struts.chain.Constants; +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.ActionMessages; +import org.apache.struts.chain.Constants; import org.apache.struts.config.ActionConfig; +import org.apache.struts.config.FormBeanConfig; import org.apache.struts.config.ForwardConfig; import org.apache.struts.config.ModuleConfig; -import org.apache.struts.action.Action; - +import org.apache.struts.util.MessageResources; +import org.apache.struts.util.TokenProcessor; /** @@ -75,12 +77,15 @@ public static final String TRANSACTION_TOKEN_KEY = "TRANSACTION_TOKEN_KEY"; public static final String TOKEN_KEY = "TOKEN_KEY"; + + protected TokenProcessor token = null; - TokenProcessor token = null; - + private Log logger = null; + public ActionContextBase(Context context) { super(context); token = TokenProcessor.getInstance(); + logger = LogFactory.getLog(this.getClass()); } public ActionContextBase() { @@ -101,7 +106,13 @@ public abstract Map getSessionScope(); - + public Map getScope(String scopeName) { + if (REQUEST_SCOPE.equals(scopeName)) return this.getRequestScope(); + if (SESSION_SCOPE.equals(scopeName)) return this.getSessionScope(); + if (APPLICATION_SCOPE.equals(scopeName)) return this.getApplicationScope(); + throw new IllegalArgumentException("Invalid scope: " + scopeName); + } + // ------------------------------- // General Struts properties // ------------------------------- @@ -334,5 +345,76 @@ public Locale getLocale() { return (Locale) this.get(LOCALE_KEY); } + + // ------------------------------- + // Convenience Methods: these are not part of the formal ActionContext API, + // but are likely to be commonly useful. + // ------------------------------- + /** + * <p>Return the currently configured commons-logging <code>Log</code> instance.</p> + * @return + */ + public Log getLogger() { + return this.logger; + } + + /** + * <p>Set the commons-logging <code>Log</code> instance which should be used to log messages. + * This is initialized at instantiation time but may be overridden. Be advised not to set the value + * to null, as <code>ActionContextBase</code> uses the logger for some of its own operations.</p> + */ + public void setLogger(Log logger) { + this.logger = logger; + } + + /** + * Using this <code>ActionContext</code>'s default <code>ModuleConfig</code>, return an existing + * <code>ActionForm</code> in the specified scope, or create a new one and add it to the specified scope. + * @param formName + * @param scopeName + * @return + * @throws IllegalAccessException + * @throws InstantiationException + * @see findOrCreateActionForm(String, String, ModuleConfig) + */ + public ActionForm findOrCreateActionForm(String formName, String scopeName) throws IllegalAccessException, InstantiationException { + return this.findOrCreateActionForm(formName, scopeName, this.getModuleConfig()); + } + + /** + * <p>In the context of the given <code>ModuleConfig</code> and this <code>ActionContext</code>, + * look for an existing <code>ActionForm</code> in the specified scope. If one is found, return + * it; otherwise, create a new instance, add it to that scope, and then return it.</p> + * @param formName + * @param scopeName + * @return + * @throws IllegalAccessException + * @throws InstantiationException + */ + public ActionForm findOrCreateActionForm(String formName, String scopeName, ModuleConfig moduleConfig) throws IllegalAccessException, InstantiationException { + Map scope = this.getScope(scopeName); + + ActionForm instance = null; + FormBeanConfig formBeanConfig = moduleConfig.findFormBeanConfig(formName); + + if (formBeanConfig == null) { + throw new IllegalArgumentException("No form config found under " + formName + " in module " + moduleConfig.getPrefix() ); + } + + instance = (ActionForm) scope.get(formName); + // Can we recycle the existing instance (if any)? + if (instance != null) { + getLogger().trace("Found an instance in scope " + scopeName + "; test for reusability"); + if (formBeanConfig.canReuse(instance)) { + return instance; + } + } + + ActionForm form = formBeanConfig.createActionForm(this); + scope.put(formName, form); + return form; + } + + } Modified: struts/core/trunk/src/share/org/apache/struts/config/FormBeanConfig.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/config/FormBeanConfig.java?view=diff&r1=161496&r2=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/config/FormBeanConfig.java (original) +++ struts/core/trunk/src/share/org/apache/struts/config/FormBeanConfig.java Fri Apr 15 09:33:24 2005 @@ -27,10 +27,15 @@ import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.MutableDynaClass; import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.struts.action.DynaActionForm; import org.apache.struts.action.DynaActionFormClass; import org.apache.struts.action.ActionServlet; import org.apache.struts.action.ActionForm; +import org.apache.struts.chain.commands.util.ClassUtils; +import org.apache.struts.chain.contexts.ActionContext; +import org.apache.struts.chain.contexts.ServletActionContext; import org.apache.struts.validator.BeanValidatorForm; import org.apache.struts.util.RequestUtils; @@ -46,7 +51,7 @@ public class FormBeanConfig implements Serializable { - + private static final Log log = LogFactory.getLog(FormBeanConfig.class); // ----------------------------------------------------- Instance Variables @@ -296,6 +301,11 @@ * <p>Create and return an <code>ActionForm</code> instance appropriate * to the information in this <code>FormBeanConfig</code>.</p> * + * <p>Although this method is not formally deprecated yet, where possible, the + * form which accepts an <code>ActionContext</code> as an argument is preferred, + * to help sever direct dependencies on the Servlet API. As the ActionContext becomes + * more familiar in Struts, this method will almost certainly be deprecated.</p> + * * @param servlet The action servlet * @return ActionForm instance * @exception IllegalAccessException if the Class or the appropriate @@ -345,7 +355,69 @@ } + /** + * <p>Create and return an <code>ActionForm</code> instance appropriate + * to the information in this <code>FormBeanConfig</code>.</p> + * <p><b>NOTE:</b> If the given <code>ActionContext</code> is not of type + * <code>ServletActionContext</code> (or a subclass), then the form which is + * returned will have a null <code>servlet</code> property. Some of + * the subclasses of <code>ActionForm</code> included in Struts will + * later throw a <code>NullPointerException</code> in this case. + * </p> + * <p>TODO: Find a way to control this direct dependency on the Servlet API.</p> + * + * @param context The ActionContext. + * @return ActionForm instance + * @exception IllegalAccessException if the Class or the appropriate + * constructor is not accessible + * @exception InstantiationException if this Class represents an abstract + * class, an array class, a primitive type, or void; or if instantiation + * fails for some other reason + */ + public ActionForm createActionForm(ActionContext context) + throws IllegalAccessException, InstantiationException { + + ActionServlet actionServlet = null; + if (context instanceof ServletActionContext) { + ServletActionContext saContext = (ServletActionContext) context; + actionServlet = saContext.getActionServlet(); + } + return createActionForm(actionServlet); + } + /** + * Is the given <code>ActionForm</code> instance suitable for use as an + * alternative to calling this <code>FormBeanConfig</code> instance's + * <code>createActionForm</code> method. + * @param form + * @return + */ + public boolean canReuse(ActionForm form) { + if (form != null) { + if (this.getDynamic()) { + String className = + ((DynaBean) form).getDynaClass().getName(); + if (className.equals(this.getName())) { + log.debug("Can reuse existing instance (dynamic)"); + return (true); + } + } else { + try { + Class configClass = + ClassUtils.getApplicationClass + (this.getType()); + if (configClass.isAssignableFrom(form.getClass())) { + log.debug("Can reuse existing instance (non-dynamic)"); + return (true); + } + } catch (Exception e) { + log.debug("Error testing existing instance for reusability; just create a new instance", e); + } + } + } + return false; + } + /** * Add a new <code>FormPropertyConfig</code> instance to the set associated * with this module. Modified: struts/core/trunk/src/share/org/apache/struts/util/RequestUtils.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/share/org/apache/struts/util/RequestUtils.java?view=diff&r1=161496&r2=161497 ============================================================================== --- struts/core/trunk/src/share/org/apache/struts/util/RequestUtils.java (original) +++ struts/core/trunk/src/share/org/apache/struts/util/RequestUtils.java Fri Apr 15 09:33:24 2005 @@ -33,7 +33,6 @@ import javax.servlet.http.HttpSession; import org.apache.commons.beanutils.BeanUtils; -import org.apache.commons.beanutils.DynaBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.Globals; @@ -172,13 +171,8 @@ ActionForm instance = lookupActionForm(request, attribute, mapping.getScope()); // Can we recycle the existing form bean instance (if there is one)? - try { - if (instance != null && canReuseActionForm(instance, config)) { - return (instance); - } - } catch(ClassNotFoundException e) { - log.error(servlet.getInternal().getMessage("formBean", config.getType()), e); - return (null); + if (instance != null && config.canReuse(instance)) { + return (instance); } return createActionForm(config, servlet); @@ -207,55 +201,6 @@ } return (instance); - } - - /** - * <p>Determine whether <code>instance</code> of <code>ActionForm</code> is - * suitable for re-use as an instance of the form described by - * <code>config</code>.</p> - * @param instance an instance of <code>ActionForm</code> which was found, - * probably in either request or session scope. - * @param config the configuration for the ActionForm which is needed. - * @return true if the instance found is "compatible" with the type required - * in the <code>FormBeanConfig</code>; false if not, or if <code>instance</code> - * is null. - * @throws ClassNotFoundException if the <code>type</code> property of - * <code>config</code> is not a valid Class name. - */ - private static boolean canReuseActionForm(ActionForm instance, FormBeanConfig config) - throws ClassNotFoundException - { - if (instance == null) { - return (false); - } - - boolean canReuse = false; - String formType = null; - String className = null; - - if (config.getDynamic()) { - className = ((DynaBean) instance).getDynaClass().getName(); - canReuse = className.equals(config.getName()); - formType = "DynaActionForm"; - } else { - Class configClass = applicationClass(config.getType()); - className = instance.getClass().getName(); - canReuse = configClass.isAssignableFrom(instance.getClass()); - formType = "ActionForm"; - } - - if (log.isDebugEnabled()) { - log.debug( - " Can recycle existing " - + formType - + " instance " - + "of type '" - + className - + "'?: " - + canReuse); - log.trace(" --> " + instance); - } - return (canReuse); } /** Added: struts/core/trunk/src/test/org/apache/struts/chain/commands/generic/TestCopyFormToContext.java URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/test/org/apache/struts/chain/commands/generic/TestCopyFormToContext.java?view=auto&rev=161497 ============================================================================== --- struts/core/trunk/src/test/org/apache/struts/chain/commands/generic/TestCopyFormToContext.java (added) +++ struts/core/trunk/src/test/org/apache/struts/chain/commands/generic/TestCopyFormToContext.java Fri Apr 15 09:33:24 2005 @@ -0,0 +1,190 @@ +/* + * $Id$ + * + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.chain.commands.generic; + +import junit.framework.TestCase; + +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.DynaActionForm; +import org.apache.struts.chain.contexts.MockActionContext; +import org.apache.struts.config.ActionConfig; +import org.apache.struts.config.FormBeanConfig; +import org.apache.struts.config.FormPropertyConfig; +import org.apache.struts.config.impl.ModuleConfigImpl; +import org.apache.struts.mock.MockFormBean; + +/** + * @version $Id$ + */ +public class TestCopyFormToContext extends TestCase { + + private static final String POST_EXECUTION_CONTEXT_KEY = "afterTest"; + private MockActionContext context = null; + + public static void main(String[] args) { + junit.textui.TestRunner.run(TestCopyFormToContext.class); + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + context = new MockActionContext(); + + ModuleConfigImpl moduleConfig = new ModuleConfigImpl("/"); + context.setModuleConfig(moduleConfig); + + FormBeanConfig fooFBC = new FormBeanConfig(); + fooFBC.setName("foo"); + fooFBC.setType("org.apache.struts.mock.MockFormBean"); + moduleConfig.addFormBeanConfig(fooFBC); + + FormBeanConfig barFBC = new FormBeanConfig(); + barFBC.setName("bar"); + barFBC.setType("org.apache.struts.action.DynaActionForm"); // use a different type so we can verify lookups better + FormPropertyConfig fpc = new FormPropertyConfig(); + fpc.setName("property"); + fpc.setType("java.lang.String"); + fpc.setInitial("test"); + barFBC.addFormPropertyConfig(fpc); + moduleConfig.addFormBeanConfig(barFBC); + + ActionConfig testActionConfig = new ActionConfig(); + testActionConfig.setPath("/Test"); + testActionConfig.setName("foo"); + testActionConfig.setScope("request"); + moduleConfig.addActionConfig(testActionConfig); + + moduleConfig.freeze(); // otherwise, ActionConfigMatcher will be null and we'll get an NPE... + } + + public void testLookupByNameAndRequestScope() throws Exception { + CopyFormToContext command = new CopyFormToContext(); + String formName = "foo"; + command.setFormName(formName); + command.setScope("request"); + command.setToKey(POST_EXECUTION_CONTEXT_KEY); + + assertNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getRequestScope().get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getSessionScope().get(POST_EXECUTION_CONTEXT_KEY)); + + command.execute(context); + + assertNotNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + assertNotNull(context.getRequestScope().get(formName)); + assertNull(context.getSessionScope().get(formName)); + + assertSame(context.get(POST_EXECUTION_CONTEXT_KEY), context.getRequestScope().get(formName)); + ActionForm theForm = (ActionForm) context.get(POST_EXECUTION_CONTEXT_KEY); + assertTrue(theForm instanceof MockFormBean); + } + + public void testLookupByActionPath() throws Exception { + CopyFormToContext command = new CopyFormToContext(); + command.setActionPath("/Test"); + command.setToKey(POST_EXECUTION_CONTEXT_KEY); + + assertNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getRequestScope().get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getSessionScope().get(POST_EXECUTION_CONTEXT_KEY)); + + command.execute(context); + + assertNotNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + String formName = "foo"; + assertNotNull(context.getRequestScope().get(formName)); + assertNull(context.getSessionScope().get(formName)); + + assertSame(context.get(POST_EXECUTION_CONTEXT_KEY), context.getRequestScope().get(formName)); + ActionForm theForm = (ActionForm) context.get(POST_EXECUTION_CONTEXT_KEY); + assertTrue(theForm instanceof MockFormBean); + } + + public void testLookupByNameAndSessionScope() throws Exception { + CopyFormToContext command = new CopyFormToContext(); + String formName = "bar"; + command.setFormName(formName); + command.setScope("session"); + command.setToKey(POST_EXECUTION_CONTEXT_KEY); + + assertNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getRequestScope().get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getSessionScope().get(POST_EXECUTION_CONTEXT_KEY)); + + command.execute(context); + + assertNotNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getRequestScope().get(formName)); + assertNotNull(context.getSessionScope().get(formName)); + + assertSame(context.get(POST_EXECUTION_CONTEXT_KEY), context.getSessionScope().get(formName)); + ActionForm theForm = (ActionForm) context.get(POST_EXECUTION_CONTEXT_KEY); + assertTrue(theForm instanceof DynaActionForm); + + DynaActionForm dForm = (DynaActionForm) theForm; + assertEquals("test", dForm.get("property")); + } + + public void testExceptionHandlingWithNullFormName() throws Exception { + CopyFormToContext command = new CopyFormToContext(); + String formName = "bar"; + // skip setting form name to test exception + // command.setFormName(formName); + command.setScope("session"); + command.setToKey(POST_EXECUTION_CONTEXT_KEY); + + assertNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getRequestScope().get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getSessionScope().get(POST_EXECUTION_CONTEXT_KEY)); + + try { + command.execute(context); + fail("Execution should throw an exception when form name is not set."); + } catch (IllegalStateException e) { + ; // expected. + } + + } + + + public void testExceptionHandlingWithNullEverything() throws Exception { + CopyFormToContext command = new CopyFormToContext(); + String formName = "bar"; + // skip setting form name to test exception + // command.setFormName(formName); + // command.setScope("session"); + // command.setToKey(POST_EXECUTION_CONTEXT_KEY); + + assertNull(context.get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getRequestScope().get(POST_EXECUTION_CONTEXT_KEY)); + assertNull(context.getSessionScope().get(POST_EXECUTION_CONTEXT_KEY)); + + try { + command.execute(context); + fail("Execution should throw an exception when no properties are set."); + } catch (IllegalStateException e) { + ; // expected. + } + + } + + +} + Propchange: struts/core/trunk/src/test/org/apache/struts/chain/commands/generic/TestCopyFormToContext.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/core/trunk/src/test/org/apache/struts/chain/commands/generic/TestCopyFormToContext.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]