Author: mrdon Date: Sun Sep 24 23:54:57 2006 New Revision: 449584 URL: http://svn.apache.org/viewvc?view=rev&rev=449584 Log: Added support for ActionForms that use Commons Validator WW-1454
Added: struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/validation.xml struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Action.java - copied, changed from r449583, struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/LegacyAction.java struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java - copied, changed from r449583, struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java - copied, changed from r449583, struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java Removed: struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/LegacyAction.java struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java Modified: struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java struts/struts2/trunk/apps/showcase/src/main/resources/struts-integration.xml struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java Modified: struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java?view=diff&rev=449584&r1=449583&r2=449584 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java (original) +++ struts/struts2/trunk/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java Sun Sep 24 23:54:57 2006 @@ -6,8 +6,9 @@ import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; +import org.apache.struts.validator.ValidatorForm; -public class GangsterForm extends ActionForm { +public class GangsterForm extends ValidatorForm { private String name; private String age; @@ -26,11 +27,12 @@ * @see org.apache.struts.action.ActionForm#validate(org.apache.struts.action.ActionMapping, javax.servlet.http.HttpServletRequest) */ @Override - public ActionErrors validate(ActionMapping arg0, HttpServletRequest arg1) { - ActionErrors errors = new ActionErrors(); + public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { + ActionErrors errors = super.validate(mapping, request); if (name == null || name.length() == 0) { errors.add("name", new ActionMessage("The name must not be blank")); } + return errors; } Modified: struts/struts2/trunk/apps/showcase/src/main/resources/struts-integration.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/resources/struts-integration.xml?view=diff&rev=449584&r1=449583&r2=449584 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/resources/struts-integration.xml (original) +++ struts/struts2/trunk/apps/showcase/src/main/resources/struts-integration.xml Sun Sep 24 23:54:57 2006 @@ -10,6 +10,10 @@ <interceptors> <interceptor name="gangsterForm" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"> <param name="className">org.apache.struts2.showcase.integration.GangsterForm</param> + <param name="name">gangsterForm</param> + </interceptor> + <interceptor name="gangsterValidation" class="org.apache.struts2.s1.ActionFormValidationInterceptor"> + <param name="pathnames">/org/apache/struts/validator/validator-rules.xml,/WEB-INF/validation.xml</param> </interceptor> <interceptor-stack name="integration"> @@ -18,7 +22,7 @@ <interceptor-ref name="model-driven"/> <interceptor-ref name="actionForm-reset"/> <interceptor-ref name="basicStack"/> - <interceptor-ref name="actionForm-validation"/> + <interceptor-ref name="gangsterValidation"/> <interceptor-ref name="workflow"/> </interceptor-stack> </interceptors> Added: struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/validation.xml URL: http://svn.apache.org/viewvc/struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/validation.xml?view=auto&rev=449584 ============================================================================== --- struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/validation.xml (added) +++ struts/struts2/trunk/apps/showcase/src/main/webapp/WEB-INF/validation.xml Sun Sep 24 23:54:57 2006 @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> + +<!DOCTYPE form-validation PUBLIC + "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN" + "http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd"> + +<form-validation> + +<!-- + This is a minimal Validator form file with a couple of examples. +--> + + <global> + + <!-- An example global constant + <constant> + <constant-name>postalCode</constant-name> + <constant-value>^\d{5}\d*$</constant-value> + </constant> + end example--> + + </global> + + <formset> + + <!-- An example form --> + <form name="gangsterForm"> + <field + property="age" + depends="required"> + <msg name="required" key="The age is required" resource="false"/> + </field> + </form> + + </formset> + +</form-validation> Modified: struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java?view=diff&rev=449584&r1=449583&r2=449584 ============================================================================== --- struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java (original) +++ struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java Sun Sep 24 23:54:57 2006 @@ -18,15 +18,36 @@ package org.apache.struts2.s1; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.UnavailableException; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.validator.ValidatorResources; +import org.apache.struts.Globals; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionServlet; +import org.apache.struts.config.ModuleConfig; +import org.apache.struts.validator.ValidatorPlugIn; import org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsException; import org.apache.struts2.dispatcher.Dispatcher; +import org.apache.struts2.util.ServletContextAware; +import org.xml.sax.SAXException; +import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.TextProvider; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.interceptor.ScopedModelDriven; @@ -37,8 +58,52 @@ */ public class ActionFormValidationInterceptor extends AbstractInterceptor { + private String pathnames; + private boolean stopOnFirstError; + private boolean initialized = false; + + private static final Log log = LogFactory.getLog(ActionFormValidationInterceptor.class); + + /** + * Delimitter for Validator resources. + */ + private final static String RESOURCE_DELIM = ","; + + /** + * Initializes the validation resources + */ + private void initResources(ServletContext servletContext) { + if (pathnames != null) { + ActionContext ctx = ActionContext.getContext(); + try { + + ValidatorResources resources = this.loadResources(servletContext); + + + String prefix = ctx.getActionInvocation().getProxy().getNamespace(); + + + servletContext.setAttribute(ValidatorPlugIn.VALIDATOR_KEY + prefix, resources); + + servletContext.setAttribute(ValidatorPlugIn.STOP_ON_ERROR_KEY + '.' + + prefix, + (this.stopOnFirstError ? Boolean.TRUE : Boolean.FALSE)); + } catch (Exception e) { + throw new StrutsException( + "Cannot load a validator resource from '" + pathnames + "'", e); + } + } + } + @Override public String intercept(ActionInvocation invocation) throws Exception { + // Lazy load the resources because the servlet context isn't available at init() time + synchronized (this) { + if (!initialized) { + initResources(ServletActionContext.getServletContext()); + initialized = true; + } + } Object action = invocation.getAction(); @@ -47,13 +112,114 @@ ScopedModelDriven modelDriven = (ScopedModelDriven) action; Object model = modelDriven.getModel(); if (model != null) { + HttpServletRequest req = ServletActionContext.getRequest(); Struts1Factory strutsFactory = new Struts1Factory(Dispatcher.getInstance().getConfigurationManager().getConfiguration()); ActionMapping mapping = strutsFactory.createActionMapping(invocation.getProxy().getConfig()); - HttpServletRequest req = ServletActionContext.getRequest(); - ActionErrors errors = ((ActionForm)model).validate(mapping, req); + ModuleConfig moduleConfig = strutsFactory.createModuleConfig(invocation.getProxy().getConfig().getPackageName()); + req.setAttribute(Globals.MODULE_KEY, moduleConfig); + req.setAttribute(Globals.MESSAGES_KEY, new WrapperMessageResources((TextProvider)invocation.getAction())); + + mapping.setAttribute(modelDriven.getScopeKey()); + + ActionForm form = (ActionForm) model; + form.setServlet(new ActionServlet(){ + public ServletContext getServletContext() { + return ServletActionContext.getServletContext(); + } + }); + ActionErrors errors = form.validate(mapping, req); strutsFactory.convertErrors(errors, action); } } return invocation.invoke(); } + + /** + * Initialize the validator resources for this module. + * + * @throws IOException if an input/output error is encountered + * @throws ServletException if we cannot initialize these resources + */ + protected ValidatorResources loadResources(ServletContext ctx) + throws IOException, ServletException { + if ((pathnames == null) || (pathnames.length() <= 0)) { + return null; + } + + StringTokenizer st = new StringTokenizer(pathnames, RESOURCE_DELIM); + + List urlList = new ArrayList(); + ValidatorResources resources = null; + try { + while (st.hasMoreTokens()) { + String validatorRules = st.nextToken().trim(); + + if (log.isInfoEnabled()) { + log.info("Loading validation rules file from '" + + validatorRules + "'"); + } + + URL input = + ctx.getResource(validatorRules); + + // If the config isn't in the servlet context, try the class + // loader which allows the config files to be stored in a jar + if (input == null) { + input = getClass().getResource(validatorRules); + } + + if (input != null) { + urlList.add(input); + } else { + throw new ServletException( + "Skipping validation rules file from '" + + validatorRules + "'. No url could be located."); + } + } + + int urlSize = urlList.size(); + String[] urlArray = new String[urlSize]; + + for (int urlIndex = 0; urlIndex < urlSize; urlIndex++) { + URL url = (URL) urlList.get(urlIndex); + + urlArray[urlIndex] = url.toExternalForm(); + } + + resources = new ValidatorResources(urlArray); + } catch (SAXException sex) { + log.error("Skipping all validation", sex); + throw new StrutsException("Skipping all validation because the validation files cannot be loaded", sex); + } + return resources; + } + + /** + * @return the pathnames + */ + public String getPathnames() { + return pathnames; + } + + /** + * @param pathnames the pathnames to set + */ + public void setPathnames(String pathNames) { + this.pathnames = pathNames; + } + + /** + * @return the stopOnFirstError + */ + public boolean isStopOnFirstError() { + return stopOnFirstError; + } + + /** + * @param stopOnFirstError the stopOnFirstError to set + */ + public void setStopOnFirstError(boolean stopOnFirstError) { + this.stopOnFirstError = stopOnFirstError; + } + } Copied: struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Action.java (from r449583, struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/LegacyAction.java) URL: http://svn.apache.org/viewvc/struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Action.java?view=diff&rev=449584&p1=struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/LegacyAction.java&r1=449583&p2=struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Action.java&r2=449584 ============================================================================== --- struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/LegacyAction.java (original) +++ struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Action.java Sun Sep 24 23:54:57 2006 @@ -55,11 +55,12 @@ * <li>Most everything else...</li> * </ul> */ -public class LegacyAction extends DefaultActionSupport implements ScopedModelDriven<ActionForm> { +public class Struts1Action extends DefaultActionSupport implements ScopedModelDriven<ActionForm> { private ActionForm actionForm; private String className; private boolean validate; + private String scopeKey; public String execute() throws Exception { ActionContext ctx = ActionContext.getContext(); @@ -73,7 +74,7 @@ // We should call setServlet() here, but let's stub that out later - StrutsFactory strutsFactory = new StrutsFactory(Dispatcher.getInstance().getConfigurationManager().getConfiguration()); + Struts1Factory strutsFactory = new Struts1Factory(Dispatcher.getInstance().getConfigurationManager().getConfiguration()); ActionMapping mapping = strutsFactory.createActionMapping(actionConfig); HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); @@ -125,5 +126,13 @@ */ public void setClassName(String className) { this.className = className; + } + + public String getScopeKey() { + return scopeKey; + } + + public void setScopeKey(String key) { + this.scopeKey = key; } } Copied: struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java (from r449583, struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java) URL: http://svn.apache.org/viewvc/struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java?view=diff&rev=449584&p1=struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java&r1=449583&p2=struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java&r2=449584 ============================================================================== --- struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java (original) +++ struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java Sun Sep 24 23:54:57 2006 @@ -15,7 +15,6 @@ * limitations under the License. * */ - package org.apache.struts2.s1; import com.opensymphony.xwork2.*; @@ -23,22 +22,27 @@ import com.opensymphony.xwork2.config.entities.ActionConfig; import com.opensymphony.xwork2.config.entities.ResultConfig; import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig; + +import org.apache.struts.Globals; import org.apache.struts.action.*; import org.apache.struts.config.*; import java.util.Iterator; import java.util.Arrays; +import java.util.Map; + +import javax.servlet.ServletContext; /** * Provides conversion methods between the Struts Action 1.x and XWork * classes. */ -public class StrutsFactory { +public class Struts1Factory { private Configuration configuration; - public StrutsFactory(Configuration config) { + public Struts1Factory(Configuration config) { this.configuration = config; } @@ -53,7 +57,7 @@ assert packageName != null; return new WrapperModuleConfig(this, configuration.getPackageConfig(packageName)); } - + /** * Create a Struts 1.x ActionMapping from an XWork ActionConfig. * Modified: struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java?view=diff&rev=449584&r1=449583&r2=449584 ============================================================================== --- struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java (original) +++ struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java Sun Sep 24 23:54:57 2006 @@ -39,6 +39,7 @@ private ActionConfig delegate; private String actionPath; + private String attribute; private Struts1Factory strutsFactory; public WrapperActionMapping(Struts1Factory factory, ActionConfig delegate) { @@ -135,11 +136,11 @@ } public String getAttribute() { - throw new UnsupportedOperationException("NYI"); + return attribute; } public void setAttribute(String attribute) { - throw new UnsupportedOperationException("Not implemented - immutable"); + this.attribute = attribute; } public String getForward() { Added: struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java?view=auto&rev=449584 ============================================================================== --- struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java (added) +++ struts/struts2/trunk/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java Sun Sep 24 23:54:57 2006 @@ -0,0 +1,44 @@ +/* + * $Id$ + * Copyright 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.struts2.s1; + +import java.util.Locale; + +import org.apache.struts.util.MessageResources; + +import com.opensymphony.xwork2.TextProvider; + +/** + * Wraps the Struts 1 message resources, delegating to Struts 2 resources + */ +public class WrapperMessageResources extends MessageResources { + + private TextProvider textProvider; + + public WrapperMessageResources(TextProvider provider) { + super(null, null, true); + this.textProvider = provider; + } + + @Override + public String getMessage(Locale locale, String key) { + String msg = textProvider.getText(key); + return msg; + } + +} Copied: struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java (from r449583, struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java) URL: http://svn.apache.org/viewvc/struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java?view=diff&rev=449584&p1=struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java&r1=449583&p2=struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java&r2=449584 ============================================================================== --- struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java (original) +++ struts/struts2/trunk/integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java Sun Sep 24 23:54:57 2006 @@ -23,22 +23,22 @@ import com.opensymphony.xwork2.config.entities.ResultConfig; /** - * Test of StrutsFactory, which creates Struts 1.x wrappers around XWork config objects. + * Test of Struts1Factory, which creates Struts 1.x wrappers around XWork config objects. */ -public class StrutsFactoryTest extends TestCase { +public class Struts1FactoryTest extends TestCase { private static final String PACKAGE_NAME = "org/apache/struts2/s1"; - protected StrutsFactory factory = null; + protected Struts1Factory factory = null; protected Configuration config; - public StrutsFactoryTest(String name) throws Exception { + public Struts1FactoryTest(String name) throws Exception { super(name); } public static void main(String args[]) { - junit.textui.TestRunner.run(StrutsFactoryTest.class); + junit.textui.TestRunner.run(Struts1FactoryTest.class); } /** @@ -49,7 +49,7 @@ ConfigurationProvider provider = new StrutsXmlConfigurationProvider(PACKAGE_NAME + "/test-struts-factory.xml", true); manager.addConfigurationProvider(provider); config = manager.getConfiguration(); - factory = new StrutsFactory(config); + factory = new Struts1Factory(config); } /** @@ -137,7 +137,6 @@ // These methods are currently not implemented -- replace as functionality is added. assertNYI(mapping, "getInputForward", null); - assertNYI(mapping, "getAttribute", null); assertNYI(mapping, "getForward", null); assertNYI(mapping, "getInclude", null); assertNYI(mapping, "getInput", null);