Merge pull request #659 from atlassian/WW-5284-clean-up-validator

WW-5284 Refactor ActionValidatorManager implementations
This commit is contained in:
Lukasz Lenart
2023-02-20 07:48:15 +01:00
committed by GitHub
4 changed files with 314 additions and 418 deletions
@@ -1,289 +0,0 @@
/*
* 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 com.opensymphony.xwork2.validator;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import static java.util.Collections.synchronizedMap;
public abstract class AbstractActionValidatorManager implements ActionValidatorManager {
/**
* The file suffix for any validation file.
*/
protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
protected final Map<String, List<ValidatorConfig>> validatorCache = synchronizedMap(new HashMap<>());
protected final Map<String, List<ValidatorConfig>> validatorFileCache = synchronizedMap(new HashMap<>());
private static final Logger LOG = LogManager.getLogger(AbstractActionValidatorManager.class);
protected ValidatorFactory validatorFactory;
protected ValidatorFileParser validatorFileParser;
protected FileManager fileManager;
protected boolean reloadingConfigs;
protected TextProviderFactory textProviderFactory;
@Inject
public void setValidatorFactory(ValidatorFactory fac) {
this.validatorFactory = fac;
}
@Inject
public void setValidatorFileParser(ValidatorFileParser parser) {
this.validatorFileParser = parser;
}
@Inject
public void setFileManagerFactory(FileManagerFactory fileManagerFactory) {
this.fileManager = fileManagerFactory.getFileManager();
}
@Inject(value = StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, required = false)
public void setReloadingConfigs(String reloadingConfigs) {
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
}
@Inject
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
this.textProviderFactory = textProviderFactory;
}
@Override
public void validate(Object object, String context) throws ValidationException {
validate(object, context, (String) null);
}
@Override
public void validate(Object object, String context, String method) throws ValidationException {
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
validate(object, context, validatorContext, method);
}
@Override
public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
validate(object, context, validatorContext, null);
}
@Override
public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
List<Validator> validators = getValidators(object.getClass(), context, method);
Set<String> shortcircuitedFields = null;
for (Validator validator : validators) {
try {
validator.setValidatorContext(validatorContext);
LOG.debug("Running validator: {} for object {} and method {}", validator, object, method);
FieldValidator fValidator = null;
String fullFieldName = null;
if (validator instanceof FieldValidator) {
fValidator = (FieldValidator) validator;
fullFieldName = fValidator.getValidatorContext().getFullFieldName(fValidator.getFieldName());
if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
LOG.debug("Short-circuited, skipping");
continue;
}
}
if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
// get number of existing errors
List<String> errs = null;
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
if (fieldErrors != null) {
errs = new ArrayList<>(fieldErrors);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> actionErrors = validatorContext.getActionErrors();
if (actionErrors != null) {
errs = new ArrayList<>(actionErrors);
}
}
validator.validate(object);
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting on field validation");
if (shortcircuitedFields == null) {
shortcircuitedFields = new TreeSet<>();
}
shortcircuitedFields.add(fullFieldName);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> errCol = validatorContext.getActionErrors();
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting");
break;
}
}
continue;
}
validator.validate(object);
} finally {
validator.setValidatorContext(null);
}
}
}
/**
* <p>This method 'collects' all the validator configurations for a given
* action invocation.</p>
*
* <p>It will traverse up the class hierarchy looking for validators for every super class
* and directly implemented interface of the current action, as well as adding validators for
* any alias of this invocation. Nifty!</p>
*
* <p>Given the following class structure:</p>
* <pre>
* interface Thing;
* interface Animal extends Thing;
* interface Quadraped extends Animal;
* class AnimalImpl implements Animal;
* class QuadrapedImpl extends AnimalImpl implements Quadraped;
* class Dog extends QuadrapedImpl;
* </pre>
*
* <p>This method will look for the following config files for Dog:</p>
* <pre>
* Animal
* Animal-context
* AnimalImpl
* AnimalImpl-context
* Quadraped
* Quadraped-context
* QuadrapedImpl
* QuadrapedImpl-context
* Dog
* Dog-context
* </pre>
*
* <p>Note that the validation rules for Thing is never looked for because no class in the
* hierarchy directly implements Thing.</p>
*
* @param clazz the Class to look up validators for.
* @param context the context to use when looking up validators.
* @param checkFile true if the validation config file should be checked to see if it has been
* updated.
* @param checked the set of previously checked class-contexts, null if none have been checked
* @return a list of validator configs for the given class and context.
*/
protected List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
List<ValidatorConfig> validatorConfigs = new ArrayList<>();
if (checked == null) {
checked = new TreeSet<>();
} else if (checked.contains(clazz.getName())) {
return validatorConfigs;
}
if (clazz.isInterface()) {
for (Class anInterface : clazz.getInterfaces()) {
validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
}
} else {
if (!clazz.equals(Object.class)) {
validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked));
}
}
// look for validators for implemented interfaces
for (Class anInterface1 : clazz.getInterfaces()) {
if (checked.contains(anInterface1.getName())) {
continue;
}
validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
}
checked.add(anInterface1.getName());
}
validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile));
}
checked.add(clazz.getName());
return validatorConfigs;
}
protected abstract List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile);
protected abstract List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile);
protected List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
List<ValidatorConfig> retList = Collections.emptyList();
URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
if ((checkFile && fileManager.fileNeedsReloading(fileUrl)) || !validatorFileCache.containsKey(fileName)) {
try (InputStream is = fileManager.loadFile(fileUrl)) {
if (is != null) {
retList = new ArrayList<>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
}
} catch (IOException e) {
LOG.error("Caught exception while closing file {}", fileName, e);
}
validatorFileCache.put(fileName, retList);
} else {
retList = validatorFileCache.get(fileName);
}
return retList;
}
}
@@ -22,10 +22,7 @@ import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
@@ -37,56 +34,9 @@ import java.util.List;
* @author Rainer Hermanns
* @author jepjep
*/
public class AnnotationActionValidatorManager extends AbstractActionValidatorManager {
private static final Logger LOG = LogManager.getLogger(AnnotationActionValidatorManager.class);
public class AnnotationActionValidatorManager extends DefaultActionValidatorManager {
@Override
public List<Validator> getValidators(Class clazz, String context) {
return getValidators(clazz, context, null);
}
@Override
public List<Validator> getValidators(Class clazz, String context, String method) {
String validatorKey = buildValidatorKey(clazz, context);
if (validatorCache.containsKey(validatorKey)) {
if (reloadingConfigs) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
}
} else {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
}
// get the set of validator configs
List<ValidatorConfig> cfgs = new ArrayList<>(validatorCache.get(validatorKey));
ValueStack stack = ActionContext.getContext().getValueStack();
// create clean instances of the validators for the caller's use
ArrayList<Validator> validators = new ArrayList<>(cfgs.size());
for (ValidatorConfig cfg : cfgs) {
if (method == null || method.equals(cfg.getParams().get("methodName"))) {
Validator validator = validatorFactory.getValidator(
new ValidatorConfig.Builder(cfg)
.removeParam("methodName")
.build());
validator.setValidatorType(cfg.getType());
validator.setValueStack(stack);
validators.add(validator);
}
}
return validators;
}
/**
* Builds a key for validators - used when caching validators.
*
* @param clazz the action.
* @param context context
* @return a validator key which is the class name plus context.
*/
protected String buildValidatorKey(Class clazz, String context) {
ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
ActionProxy proxy = invocation.getProxy();
@@ -98,20 +48,11 @@ public class AnnotationActionValidatorManager extends AbstractActionValidatorMan
sb.append(config.getPackageName());
sb.append("/");
}
// the key needs to use the name of the action from the config file,
// instead of the url, so wild card actions will have the same validator
// see WW-2996
// UPDATE:
// WW-3753 Using the config name instead of the context only for
// wild card actions to keep the flexibility provided
// by the original design (such as mapping different contexts
// to the same action and method if desired)
// UPDATE:
// WW-4536 Using NameVariablePatternMatcher allows defines actions
// with patterns enclosed with '{}', it's similar case to WW-3753
// WW-2996: key needs to use the name of the action from the config file, instead of the url,
// so wildcard actions will have the same validator
// WW-3753: Using the config name instead of the context only for wildcard actions to keep the flexibility
// provided by the original design (such as mapping different contexts to the same action and method if desired)
// WW-4536: Using NamedVariablePatternMatcher allows defines actions with patterns enclosed with '{}'
String configName = config.getName();
if (configName.contains(ActionConfig.WILDCARD) || (configName.contains("{") && configName.contains("}"))) {
sb.append(configName);
@@ -120,7 +61,6 @@ public class AnnotationActionValidatorManager extends AbstractActionValidatorMan
} else {
sb.append(context);
}
return sb.toString();
}
@@ -19,12 +19,29 @@
package com.opensymphony.xwork2.validator;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import static java.util.Collections.synchronizedMap;
/**
* <p>
@@ -43,9 +60,102 @@ import java.util.List;
* @author James House
* @author Rainer Hermanns
*/
public class DefaultActionValidatorManager extends AbstractActionValidatorManager {
public class DefaultActionValidatorManager implements ActionValidatorManager {
private final static Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
/**
* The file suffix for any validation file.
*/
protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
protected final Map<String, List<ValidatorConfig>> validatorCache = synchronizedMap(new HashMap<>());
protected final Map<String, List<ValidatorConfig>> validatorFileCache = synchronizedMap(new HashMap<>());
private static final Logger LOG = LogManager.getLogger(DefaultActionValidatorManager.class);
protected ValidatorFactory validatorFactory;
protected ValidatorFileParser validatorFileParser;
protected FileManager fileManager;
protected boolean reloadingConfigs;
protected TextProviderFactory textProviderFactory;
@Inject
public void setValidatorFactory(ValidatorFactory fac) {
this.validatorFactory = fac;
}
@Inject
public void setValidatorFileParser(ValidatorFileParser parser) {
this.validatorFileParser = parser;
}
@Inject
public void setFileManagerFactory(FileManagerFactory fileManagerFactory) {
this.fileManager = fileManagerFactory.getFileManager();
}
@Inject(value = StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, required = false)
public void setReloadingConfigs(String reloadingConfigs) {
this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
}
@Inject
public void setTextProviderFactory(TextProviderFactory textProviderFactory) {
this.textProviderFactory = textProviderFactory;
}
@Override
public void validate(Object object, String context) throws ValidationException {
validate(object, context, (String) null);
}
@Override
public void validate(Object object, String context, String method) throws ValidationException {
ValidatorContext validatorContext = new DelegatingValidatorContext(object, textProviderFactory);
validate(object, context, validatorContext, method);
}
@Override
public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
validate(object, context, validatorContext, null);
}
/**
* Builds a key for validators - used when caching validators.
*
* @param clazz the action.
* @param context context
* @return a validator key which is the class name plus context.
*/
protected String buildValidatorKey(Class clazz, String context) {
return clazz.getName() + "/" + context;
}
protected Validator getValidatorFromValidatorConfig(ValidatorConfig config, ValueStack stack) {
Validator validator = validatorFactory.getValidator(config);
validator.setValidatorType(config.getType());
validator.setValueStack(stack);
return validator;
}
@Override
public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
String validatorKey = buildValidatorKey(clazz, context);
if (!validatorCache.containsKey(validatorKey)) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
} else if (reloadingConfigs) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
}
ValueStack stack = ActionContext.getContext().getValueStack();
List<ValidatorConfig> configs = validatorCache.get(validatorKey);
List<Validator> validators = new ArrayList<>();
for (ValidatorConfig config : configs) {
if (method == null || method.equals(config.getParams().get("methodName"))) {
validators.add(getValidatorFromValidatorConfig(config, stack));
}
}
return validators;
}
@Override
public synchronized List<Validator> getValidators(Class clazz, String context) {
@@ -53,54 +163,189 @@ public class DefaultActionValidatorManager extends AbstractActionValidatorManage
}
@Override
public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
String validatorKey = buildValidatorKey(clazz, context);
public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
List<Validator> validators = getValidators(object.getClass(), context, method);
Set<String> shortcircuitedFields = null;
if (validatorCache.containsKey(validatorKey)) {
if (reloadingConfigs) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
for (Validator validator : validators) {
validator.setValidatorContext(validatorContext);
LOG.debug("Running validator: {} for object {} and method {}", validator, object, method);
FieldValidator fValidator = null;
String fullFieldName = null;
if (validator instanceof FieldValidator) {
fValidator = (FieldValidator) validator;
fullFieldName = validatorContext.getFullFieldName(fValidator.getFieldName());
if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
LOG.debug("Short-circuited, skipping");
continue;
}
}
} else {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
}
ValueStack stack = ActionContext.getContext().getValueStack();
// get the set of validator configs
List<ValidatorConfig> cfgs = validatorCache.get(validatorKey);
if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
// get number of existing errors
List<String> errs = null;
// create clean instances of the validators for the caller's use
ArrayList<Validator> validators = new ArrayList<>(cfgs.size());
for (ValidatorConfig cfg : cfgs) {
if (method == null || method.equals(cfg.getParams().get("methodName"))) {
Validator validator = validatorFactory.getValidator(cfg);
validator.setValidatorType(cfg.getType());
validator.setValueStack(stack);
validators.add(validator);
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
if (fieldErrors != null) {
errs = new ArrayList<>(fieldErrors);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> actionErrors = validatorContext.getActionErrors();
if (actionErrors != null) {
errs = new ArrayList<>(actionErrors);
}
}
validator.validate(object);
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting on field validation");
if (shortcircuitedFields == null) {
shortcircuitedFields = new TreeSet<>();
}
shortcircuitedFields.add(fullFieldName);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection<String> errCol = validatorContext.getActionErrors();
if ((errCol != null) && !errCol.equals(errs)) {
LOG.debug("Short-circuiting");
break;
}
}
continue;
}
validator.validate(object);
}
return validators;
}
/**
* Builds a key for validators - used when caching validators.
* <p>This method 'collects' all the validator configurations for a given
* action invocation.</p>
*
* @param clazz the action.
* @param context the action's context.
* @return a validator key which is the class name plus context.
* <p>It will traverse up the class hierarchy looking for validators for every super class
* and directly implemented interface of the current action, as well as adding validators for
* any alias of this invocation. Nifty!</p>
*
* <p>Given the following class structure:</p>
* <pre>
* interface Thing;
* interface Animal extends Thing;
* interface Quadraped extends Animal;
* class AnimalImpl implements Animal;
* class QuadrapedImpl extends AnimalImpl implements Quadraped;
* class Dog extends QuadrapedImpl;
* </pre>
*
* <p>This method will look for the following config files for Dog:</p>
* <pre>
* Animal
* Animal-context
* AnimalImpl
* AnimalImpl-context
* Quadraped
* Quadraped-context
* QuadrapedImpl
* QuadrapedImpl-context
* Dog
* Dog-context
* </pre>
*
* <p>Note that the validation rules for Thing is never looked for because no class in the
* hierarchy directly implements Thing.</p>
*
* @param clazz the Class to look up validators for.
* @param context the context to use when looking up validators.
* @param checkFile true if the validation config file should be checked to see if it has been
* updated.
* @param checked the set of previously checked class-contexts, null if none have been checked
* @return a list of validator configs for the given class and context.
*/
protected static String buildValidatorKey(Class clazz, String context) {
return clazz.getName() + "/" + context;
protected List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
List<ValidatorConfig> validatorConfigs = new ArrayList<>();
if (checked == null) {
checked = new TreeSet<>();
} else if (checked.contains(clazz.getName())) {
return validatorConfigs;
}
if (clazz.isInterface()) {
for (Class anInterface : clazz.getInterfaces()) {
validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
}
} else {
if (!clazz.equals(Object.class)) {
validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked));
}
}
// look for validators for implemented interfaces
for (Class anInterface1 : clazz.getInterfaces()) {
if (checked.contains(anInterface1.getName())) {
continue;
}
validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
}
checked.add(anInterface1.getName());
}
validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
if (context != null) {
validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile));
}
checked.add(clazz.getName());
return validatorConfigs;
}
@Override
protected List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
@Override
protected List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
protected List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
List<ValidatorConfig> retList = Collections.emptyList();
URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
if ((checkFile && fileManager.fileNeedsReloading(fileUrl)) || !validatorFileCache.containsKey(fileName)) {
try (InputStream is = fileManager.loadFile(fileUrl)) {
if (is != null) {
retList = new ArrayList<>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
}
} catch (IOException e) {
LOG.error("Caught exception while closing file {}", fileName, e);
}
validatorFileCache.put(fileName, retList);
} else {
retList = validatorFileCache.get(fileName);
}
return retList;
}
}
@@ -44,7 +44,7 @@ import java.util.List;
* DefaultActionValidatorManagerTest
*
* @author Jason Carreira
* @author tm_jee
* @author tm_jee
* @version $Date$ $Id$
*/
public class DefaultActionValidatorManagerTest extends XWorkTestCase {
@@ -87,7 +87,7 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
public void testBuildValidatorKey() {
String validatorKey = DefaultActionValidatorManager.buildValidatorKey(SimpleAction.class, alias);
String validatorKey = actionValidatorManager.buildValidatorKey(SimpleAction.class, alias);
assertEquals(SimpleAction.class.getName() + "/" + alias, validatorKey);
}
@@ -239,7 +239,7 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
user.setName("Mark");
// * mark both email to starts with mark to get pass the action-level validator,
// so we could concentrate on testing the field-level validators (User-validation.xml)
// * make both email the same to pass the action-level validator at
// * make both email the same to pass the action-level validator at
// UserMarker-validation.xml
user.setEmail("mark_bad_email_for_field_val@foo.com");
user.setEmail2("mark_bad_email_for_field_val@foo.com");
@@ -255,51 +255,51 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
assertEquals(1, l.size()); // because email-field-val is short-circuit
assertEquals("Email not from the right company.", l.get(0));
// check action errors
l = (List) context.getActionErrors();
assertFalse(context.hasActionErrors());
assertEquals(0, l.size());
} catch (ValidationException ex) {
ex.printStackTrace();
fail("Validation error: " + ex.getMessage());
}
}
public void testActionLevelShortCircuit() throws Exception {
List validatorList = actionValidatorManager.getValidators(User.class, null);
assertEquals(10, validatorList.size());
User user = new User();
// all fields will trigger error, but sc of action-level, cause it to not appear
user.setName(null);
user.setName(null);
user.setEmail("tmjee(at)yahoo.co.uk");
user.setEmail("tm_jee(at)yahoo.co.uk");
ValidatorContext context = new GenericValidatorContext(user);
actionValidatorManager.validate(user, null, context);
// check field level errors
// shouldn't have any because action error prevents validation of anything else
List l = (List) context.getFieldErrors().get("email2");
assertNull(l);
// check action errors
assertTrue(context.hasActionErrors());
l = (List) context.getActionErrors();
assertNotNull(l);
// we only get one, because UserMarker-validation.xml action-level validator
// already sc it :-)
assertEquals(1, l.size());
assertEquals(1, l.size());
assertEquals("Email not the same as email2", l.get(0));
}
public void testShortCircuitNoErrors() {
// get validators
List validatorList = actionValidatorManager.getValidators(User.class, null);
@@ -319,65 +319,65 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase {
fail("Validation error: " + ex.getMessage());
}
}
public void testFieldErrorsOrder() throws Exception {
ValidationOrderAction action = new ValidationOrderAction();
actionValidatorManager.validate(action, "actionContext");
Map fieldErrors = action.getFieldErrors();
Iterator i = fieldErrors.entrySet().iterator();
assertNotNull(fieldErrors);
assertEquals(fieldErrors.size(), 12);
Map.Entry e = (Map.Entry) i.next();
assertEquals(e.getKey(), "username");
assertEquals(((List)e.getValue()).get(0), "username required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "password");
assertEquals(((List)e.getValue()).get(0), "password required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "confirmPassword");
assertEquals(((List)e.getValue()).get(0), "confirm password required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "firstName");
assertEquals(((List)e.getValue()).get(0), "first name required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "lastName");
assertEquals(((List)e.getValue()).get(0), "last name required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "city");
assertEquals(((List)e.getValue()).get(0), "city is required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "province");
assertEquals(((List)e.getValue()).get(0), "province is required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "country");
assertEquals(((List)e.getValue()).get(0), "country is required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "postalCode");
assertEquals(((List)e.getValue()).get(0), "postal code is required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "email");
assertEquals(((List)e.getValue()).get(0), "email is required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "website");
assertEquals(((List)e.getValue()).get(0), "website is required");
e = (Map.Entry) i.next();
assertEquals(e.getKey(), "passwordHint");
assertEquals(((List)e.getValue()).get(0), "password hint is required");
}
*/
}