
com.opensymphony.xwork2.validator.AnnotationActionValidatorManager Maven / Gradle / Ivy
/*
* Copyright (c) 2002-2006 by OpenSymphony
* All rights reserved.
*/
package com.opensymphony.xwork2.validator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
import java.io.InputStream;
import java.io.IOException;
import com.opensymphony.xwork2.util.FileManager;
/**
* AnnotationActionValidatorManager
*
* This is the entry point into XWork's annotations-based validator framework.
* Validation rules are specified as annotations within the source files.
*
* @author Rainer Hermanns
* @author jepjep
* @version $Id: AnnotationActionValidatorManager.java 1187 2006-11-13 09:05:32 +0100 (Mon, 13 Nov 2006) mrdon $
*/
public class AnnotationActionValidatorManager implements ActionValidatorManager {
/**
* The file suffix for any validation file.
*/
protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
private static final Map> validatorCache = Collections.synchronizedMap(new HashMap>());
private static final Map> validatorFileCache = Collections.synchronizedMap(new HashMap>());
private static final Log LOG = LogFactory.getLog(AnnotationActionValidatorManager.class);
/**
* Returns a list of validators for the given class and context. This is the primary
* lookup method for validators.
*
* @param clazz the class to lookup.
* @param context the context of the action class - can be null.
* @return a list of all validators for the given class and context.
*/
public synchronized List getValidators(Class clazz, String context) {
final String validatorKey = buildValidatorKey(clazz, context);
if (validatorCache.containsKey(validatorKey)) {
if (FileManager.isReloadingConfigs()) {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
}
} else {
validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
}
// get the set of validator configs
List cfgs = validatorCache.get(validatorKey);
// create clean instances of the validators for the caller's use
ArrayList validators = new ArrayList(cfgs.size());
for (ValidatorConfig cfg : cfgs) {
Validator validator = ValidatorFactory.getValidator(cfg);
validator.setValidatorType(cfg.getType());
validators.add(validator);
}
return validators;
}
/**
* Validates the given object using action and its context.
*
* @param object the action to validate.
* @param context the action's context.
* @throws ValidationException if an error happens when validating the action.
*/
public void validate(Object object, String context) throws ValidationException {
ValidatorContext validatorContext = new DelegatingValidatorContext(object);
validate(object, context, validatorContext);
}
/**
* Validates an action give its context and a validation context.
*
* @param object the action to validate.
* @param context the action's context.
* @param validatorContext
* @throws ValidationException if an error happens when validating the action.
*/
public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
List validators = getValidators(object.getClass(), context);
Set shortcircuitedFields = null;
for (final Validator validator: validators) {
try {
validator.setValidatorContext(validatorContext);
if (LOG.isDebugEnabled()) {
LOG.debug("Running validator: " + validator + " for object " + object);
}
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)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Short-circuited, skipping");
}
continue;
}
}
if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit())
{
// get number of existing errors
List errs = null;
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection fieldErrors = (Collection) validatorContext.getFieldErrors().get(fullFieldName);
if (fieldErrors != null) {
errs = new ArrayList(fieldErrors);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection actionErrors = validatorContext.getActionErrors();
if (actionErrors != null) {
errs = new ArrayList(actionErrors);
}
}
validator.validate(object);
if (fValidator != null) {
if (validatorContext.hasFieldErrors()) {
Collection errCol = (Collection) validatorContext.getFieldErrors().get(fullFieldName);
if ((errCol != null) && !errCol.equals(errs)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Short-circuiting on field validation");
}
if (shortcircuitedFields == null) {
shortcircuitedFields = new TreeSet();
}
shortcircuitedFields.add(fullFieldName);
}
}
} else if (validatorContext.hasActionErrors()) {
Collection errCol = validatorContext.getActionErrors();
if ((errCol != null) && !errCol.equals(errs)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Short-circuiting");
}
break;
}
}
continue;
}
validator.validate(object);
} finally {
validator.setValidatorContext( null );
}
}
}
/**
* Builds a key for validators - used when caching validators.
*
* @param clazz the action.
* @param context the action's context.
* @return a validator key which is the class name plus context.
*/
protected static String buildValidatorKey(Class clazz, String context) {
StringBuffer sb = new StringBuffer(clazz.getName());
sb.append("/");
sb.append(context);
return sb.toString();
}
private List buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
return loadFile(fileName, aClass, checkFile);
}
protected List buildClassValidatorConfigs(Class aClass, boolean checkFile) {
String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
List result = new ArrayList(loadFile(fileName, aClass, checkFile));
List annotationResult = new ArrayList(AnnotationValidationConfigurationBuilder.buildAnnotationClassValidatorConfigs(aClass));
result.addAll(annotationResult);
return result;
}
/**
* This method 'collects' all the validator configurations for a given
* action invocation.
*
* 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!
*
* Given the following class structure:
*
* interface Thing;
* interface Animal extends Thing;
* interface Quadraped extends Animal;
* class AnimalImpl implements Animal;
* class QuadrapedImpl extends AnimalImpl implements Quadraped;
* class Dog extends QuadrapedImpl;
*
*
* This method will look for the following config files for Dog:
*
* Animal
* Animal-context
* AnimalImpl
* AnimalImpl-context
* Quadraped
* Quadraped-context
* QuadrapedImpl
* QuadrapedImpl-context
* Dog
* Dog-context
*
*
* Note that the validation rules for Thing is never looked for because no class in the
* hierarchy directly implements Thing.
*
* @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.
*/
private List buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set checked) {
List validatorConfigs = new ArrayList();
if (checked == null) {
checked = new TreeSet();
} else if (checked.contains(clazz.getName())) {
return validatorConfigs;
}
if (clazz.isInterface()) {
Class[] interfaces = clazz.getInterfaces();
for (Class anInterface : interfaces) {
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
Class[] interfaces = clazz.getInterfaces();
for (Class anInterface1 : interfaces) {
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;
}
private List loadFile(String fileName, Class clazz, boolean checkFile) {
List retList = Collections.emptyList();
if ((checkFile && FileManager.fileNeedsReloading(fileName)) || !validatorFileCache.containsKey(fileName)) {
InputStream is = null;
try {
is = FileManager.loadFile(fileName, clazz);
if (is != null) {
retList = new ArrayList(ValidatorFileParser.parseActionValidatorConfigs(is, fileName));
}
} catch (Exception e) {
LOG.error("Caught exception while loading file " + fileName, e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOG.error("Unable to close input stream for " + fileName, e);
}
}
}
validatorFileCache.put(fileName, retList);
} else {
retList = validatorFileCache.get(fileName);
}
return retList;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy