diff --git a/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java b/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java
index c1151b254..477135f48 100644
--- a/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java
+++ b/apps/showcase/src/main/java/org/apache/struts2/showcase/integration/GangsterForm.java
@@ -6,8 +6,9 @@ import org.apache.struts.action.ActionErrors;
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 @@ public class GangsterForm extends ActionForm {
* @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;
}
diff --git a/apps/showcase/src/main/resources/struts-integration.xml b/apps/showcase/src/main/resources/struts-integration.xml
index 552d054e1..e6fdbe647 100644
--- a/apps/showcase/src/main/resources/struts-integration.xml
+++ b/apps/showcase/src/main/resources/struts-integration.xml
@@ -10,6 +10,10 @@
org.apache.struts2.showcase.integration.GangsterForm
+ gangsterForm
+
+
+ /org/apache/struts/validator/validator-rules.xml,/WEB-INF/validation.xml
@@ -18,7 +22,7 @@
-
+
diff --git a/apps/showcase/src/main/webapp/WEB-INF/validation.xml b/apps/showcase/src/main/webapp/WEB-INF/validation.xml
new file mode 100644
index 000000000..284975364
--- /dev/null
+++ b/apps/showcase/src/main/webapp/WEB-INF/validation.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java b/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java
index b9dbe5178..bc92f1e5f 100644
--- a/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java
+++ b/integration/src/main/java/org/apache/struts2/s1/ActionFormValidationInterceptor.java
@@ -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 @@ import com.opensymphony.xwork2.interceptor.ScopedModelDriven;
*/
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 @@ public class ActionFormValidationInterceptor extends AbstractInterceptor {
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;
+ }
+
}
diff --git a/integration/src/main/java/org/apache/struts2/s1/LegacyAction.java b/integration/src/main/java/org/apache/struts2/s1/Struts1Action.java
similarity index 91%
rename from integration/src/main/java/org/apache/struts2/s1/LegacyAction.java
rename to integration/src/main/java/org/apache/struts2/s1/Struts1Action.java
index f4ef5da31..99d817e37 100644
--- a/integration/src/main/java/org/apache/struts2/s1/LegacyAction.java
+++ b/integration/src/main/java/org/apache/struts2/s1/Struts1Action.java
@@ -55,11 +55,12 @@ import com.opensymphony.xwork2.interceptor.ScopedModelDriven;
*
Most everything else...
*
*/
-public class LegacyAction extends DefaultActionSupport implements ScopedModelDriven {
+public class Struts1Action extends DefaultActionSupport implements ScopedModelDriven {
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 @@ public class LegacyAction extends DefaultActionSupport implements ScopedModelDri
// 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();
@@ -126,4 +127,12 @@ public class LegacyAction extends DefaultActionSupport implements ScopedModelDri
public void setClassName(String className) {
this.className = className;
}
+
+ public String getScopeKey() {
+ return scopeKey;
+ }
+
+ public void setScopeKey(String key) {
+ this.scopeKey = key;
+ }
}
diff --git a/integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java b/integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java
similarity index 96%
rename from integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java
rename to integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java
index 533bac735..29bfe2841 100644
--- a/integration/src/main/java/org/apache/struts2/s1/StrutsFactory.java
+++ b/integration/src/main/java/org/apache/struts2/s1/Struts1Factory.java
@@ -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.Configuration;
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 @@ public class StrutsFactory {
assert packageName != null;
return new WrapperModuleConfig(this, configuration.getPackageConfig(packageName));
}
-
+
/**
* Create a Struts 1.x ActionMapping from an XWork ActionConfig.
*
diff --git a/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java b/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java
index 0fa749365..48214481d 100644
--- a/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java
+++ b/integration/src/main/java/org/apache/struts2/s1/WrapperActionMapping.java
@@ -39,6 +39,7 @@ class WrapperActionMapping extends ActionMapping {
private ActionConfig delegate;
private String actionPath;
+ private String attribute;
private Struts1Factory strutsFactory;
public WrapperActionMapping(Struts1Factory factory, ActionConfig delegate) {
@@ -135,11 +136,11 @@ class WrapperActionMapping extends ActionMapping {
}
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() {
diff --git a/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java b/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java
new file mode 100644
index 000000000..9146c870d
--- /dev/null
+++ b/integration/src/main/java/org/apache/struts2/s1/WrapperMessageResources.java
@@ -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;
+ }
+
+}
diff --git a/integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java b/integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java
similarity index 96%
rename from integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java
rename to integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java
index e08930710..e1441b53d 100644
--- a/integration/src/test/java/org/apache/struts2/s1/StrutsFactoryTest.java
+++ b/integration/src/test/java/org/apache/struts2/s1/Struts1FactoryTest.java
@@ -23,22 +23,22 @@ import com.opensymphony.xwork2.config.entities.PackageConfig;
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 @@ public class StrutsFactoryTest extends TestCase {
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 @@ public class StrutsFactoryTest extends TestCase {
// 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);