001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.xwork.validator;
006:
007: import com.opensymphony.util.ClassLoaderUtil;
008: import com.opensymphony.xwork.ObjectFactory;
009: import com.opensymphony.xwork.XworkException;
010: import org.apache.commons.logging.Log;
011: import org.apache.commons.logging.LogFactory;
012:
013: import java.io.InputStream;
014: import java.util.HashMap;
015: import java.util.Map;
016:
017: /**
018: * ValidatorFactory
019: *
020: * <p>
021: * <!-- START SNIPPET: javadoc -->
022: * Validation rules are handled by validators, which must be registered with
023: * the ValidatorFactory (using the registerValidator method). The simplest way to do so is to add a file name
024: * validators.xml in the root of the classpath (/WEB-INF/classes) that declares
025: * all the validators you intend to use.
026: * <!-- END SNIPPET: javadoc -->
027: * </p>
028: *
029: *
030: * <p>
031: * <b>INFORMATION</b>
032: * <!-- START SNIPPET: information -->
033: * validators.xml if being defined should be available in the classpath. However
034: * this is not necessary, if no custom validator is needed. Webwork will automatically
035: * picked up a predefined sets of validators defined in
036: * com/opensymphony/xwork/validator/validators/default.xml packaged together
037: * in xwork jar file that comes with webwork distribution. See ValidatorFactory
038: * static block for details.
039: * <!-- END SNIPPET: information -->
040: * </p>
041: *
042: * <p>
043: * <b>WARNING</b>
044: * <!-- START SNIPPET: warning -->
045: * If custom validator is being defined and a validators.xml is created and
046: * place in the classpath, do remember to copy all the other pre-defined validators
047: * that is needed into the validators.xml as if not they will not be registered.
048: * Once a validators.xml is detected in the classpath, the default one
049: * (com/opensymphony/xwork/validator/validators/default.xml) will not be loaded.
050: * It is only loaded when a custom validators.xml cannot be found in the classpath.
051: * Be careful.
052: * <!-- END SNIPPET: warning -->
053: * </p>
054: *
055: * <p><b>Note:</b>
056: * <!-- START SNIPPET: turningOnValidators -->
057: * The default validationWorkflowStack already includes this.<br/>
058: * All that is required to enable validation for an Action is to put the
059: * ValidationInterceptor in the interceptor refs of the action (see xwork.xml) like so:
060: * <!-- END SNIPPET: turningOnValidators -->
061: * </p>
062: *
063: * <pre>
064: * <!-- START SNIPPET: exTurnOnValidators -->
065: * <interceptor name="validator" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>
066: * <!-- END SNIPPET: exTurnOnValidators -->
067: * </pre>
068: *
069: * <p><b>Field Validators</b>
070: * <!-- START SNIPPET: fieldValidators -->
071: * Field validators, as the name indicate, act on single fields accessible through an action.
072: * A validator, in contrast, is more generic and can do validations in the full action context,
073: * involving more than one field (or even no field at all) in validation rule.
074: * Most validations can be defined on per field basis. This should be preferred over
075: * non-field validation whereever possible, as field validator messages are bound to the
076: * related field and will be presented next to the corresponding input element in the
077: * respecting view.
078: * <!-- END SNIPPET: fieldValidators -->
079: * </p>
080: *
081: * <p><b>Non Field Validators</b>
082: * <!-- START SNIPPET: nonFieldValidators -->
083: * Non-field validators only add action level messages. Non-field validators
084: * are mostly domain specific and therefore offer custom implementations.
085: * The most important standard non-field validator provided by XWork/WebWork
086: * is ExpressionValidator.
087: * <!-- END SNIPPET: nonFieldValidators -->
088: * </p>
089: *
090: * <p><b>NOTE:</b>
091: * <!-- START SNIPPET: validatorsNote -->
092: * Non-field validators takes precedence over field validators
093: * regardless of the order they are defined in *-validation.xml. If a non-field
094: * validator is short-circuited, it will causes its non-field validator to not
095: * being executed. See validation framework documentation for more info.
096: * <!-- END SNIPPET: validatorsNote -->
097: * </p>
098: *
099: * <p><b>VALIDATION RULES:</b>
100: * <!-- START SNIPPET: validationRules1 -->
101: * Validation rules can be specified:
102: * <ol>
103: * <li> Per Action class: in a file named ActionName-validation.xml</li>
104: * <li> Per Action alias: in a file named ActionName-alias-validation.xml</li>
105: * <li> Inheritance hierarchy and interfaces implemented by Action class:
106: * WebWork searches up the inheritance tree of the action to find default
107: * validations for parent classes of the Action and interfaces implemented</li>
108: * </ol>
109: * Here is an example for SimpleAction-validation.xml:
110: * <!-- END SNIPPET: validationRules1 -->
111: * <p>
112: *
113: * <pre>
114: * <!-- START SNIPPET: exValidationRules1 -->
115: * <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
116: * "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
117: * <validators>
118: * <field name="bar">
119: * <field-validator type="required">
120: * <message>You must enter a value for bar.</message>
121: * </field-validator>
122: * <field-validator type="int">
123: * <param name="min">6</param>
124: * <param name="max">10</param>
125: * <message>bar must be between ${min} and ${max}, current value is ${bar}.</message>
126: * </field-validator>
127: * </field>
128: * <field name="bar2">
129: * <field-validator type="regex">
130: * <param name="regex">[0-9],[0-9]</param>
131: * <message>The value of bar2 must be in the format "x, y", where x and y are between 0 and 9</message>
132: * </field-validator>
133: * </field>
134: * <field name="date">
135: * <field-validator type="date">
136: * <param name="min">12/22/2002</param>
137: * <param name="max">12/25/2002</param>
138: * <message>The date must be between 12-22-2002 and 12-25-2002.</message>
139: * </field-validator>
140: * </field>
141: * <field name="foo">
142: * <field-validator type="int">
143: * <param name="min">0</param>
144: * <param name="max">100</param>
145: * <message key="foo.range">Could not find foo.range!</message>
146: * </field-validator>
147: * </field>
148: * <validator type="expression">
149: * <param name="expression">foo lt bar </param>
150: * <message>Foo must be greater than Bar. Foo = ${foo}, Bar = ${bar}.</message>
151: * </validator>
152: * </validators>
153: * <!-- END SNIPPET: exValidationRules1 -->
154: * </pre>
155: *
156: *
157: * <p>
158: * <!-- START SNIPPET: validationRules2 -->
159: * Here we can see the configuration of validators for the SimpleAction class.
160: * Validators (and field-validators) must have a type attribute, which refers
161: * to a name of an Validator registered with the ValidatorFactory as above.
162: * Validator elements may also have <param> elements with name and value attributes
163: * to set arbitrary parameters into the Validator instance. See below for discussion
164: * of the message element.
165: * <!-- END SNIPPET: validationRules2 -->
166: * </p>
167: *
168: *
169: *
170: * <!-- START SNIPPET: validationRules3 -->
171: * <p>Each Validator or Field-Validator element must define one message element inside
172: * the validator element body. The message element has 1 attributes, key which is not
173: * required. The body of the message tag is taken as the default message which should
174: * be added to the Action if the validator fails.Key gives a message key to look up
175: * in the Action's ResourceBundles using getText() from LocaleAware if the Action
176: * implements that interface (as ActionSupport does). This provides for Localized
177: * messages based on the Locale of the user making the request (or whatever Locale
178: * you've set into the LocaleAware Action). After either retrieving the message from
179: * the ResourceBundle using the Key value, or using the Default message, the current
180: * Validator is pushed onto the ValueStack, then the message is parsed for \$\{...\}
181: * sections which are replaced with the evaluated value of the string between the
182: * \$\{ and \}. This allows you to parameterize your messages with values from the
183: * Validator, the Action, or both.</p>
184: *
185: *
186: * <p>If the validator fails, the validator is pushed onto the ValueStack and the
187: * message - either the default or the locale-specific one if the key attribute is
188: * defined (and such a message exists) - is parsed for ${...} sections which are
189: * replaced with the evaluated value of the string between the ${ and }. This
190: * allows you to parameterize your messages with values from the validator, the
191: * Action, or both. </p>
192: *
193: * <p><b>NOTE:</b>Since validation rules are in an XML file, you must make sure
194: * you escape special characters. For example, notice that in the expression
195: * validator rule above we use ">" instead of ">". Consult a resource on XML
196: * for the full list of characters that must be escaped. The most commonly used
197: * characters that must be escaped are: & (use &), > (user >), and < (use <).</p>
198: *
199: * <p>Here is an example of a parameterized message:</p>
200: * <p>This will pull the min and max parameters from the IntRangeFieldValidator and
201: * the value of bar from the Action.</p>
202: * <!-- END SNIPPET: validationRules3 -->
203: *
204: * <pre>
205: * <!-- START SNIPPET: exValidationRules3 -->
206: * bar must be between ${min} and ${max}, current value is ${bar}.
207: * <!-- END SNIPPET: exValidationRules3 -->
208: * </pre>
209: *
210: * @version $Date: 2007-02-16 10:22:20 +0100 (Fr, 16 Feb 2007) $ $Id: ValidatorFactory.java 1354 2007-02-16 09:22:20Z rainerh $
211: * @author Jason Carreira
212: * @author James House
213: */
214: public class ValidatorFactory {
215:
216: private static Map validators = new HashMap();
217: private static Log LOG = LogFactory.getLog(ValidatorFactory.class);
218:
219: static {
220: parseValidators();
221: }
222:
223: private ValidatorFactory() {
224: }
225:
226: /**
227: * Get a Validator that matches the given configuration.
228: *
229: * @param cfg the configurator.
230: * @return the validator.
231: */
232: public static Validator getValidator(ValidatorConfig cfg) {
233:
234: String className = lookupRegisteredValidatorType(cfg.getType());
235:
236: Validator validator;
237:
238: try {
239: // instantiate the validator, and set configured parameters
240: //todo - can this use the ThreadLocal?
241: validator = ObjectFactory.getObjectFactory()
242: .buildValidator(className, cfg.getParams(), null); // ActionContext.getContext().getContextMap());
243: } catch (Exception e) {
244: final String msg = "There was a problem creating a Validator of type "
245: + className + " : caused by " + e.getMessage();
246: throw new XworkException(msg, e, cfg);
247: }
248:
249: // set other configured properties
250: validator.setMessageKey(cfg.getMessageKey());
251: validator.setDefaultMessage(cfg.getDefaultMessage());
252: if (validator instanceof ShortCircuitableValidator) {
253: ((ShortCircuitableValidator) validator).setShortCircuit(cfg
254: .isShortCircuit());
255: }
256:
257: return validator;
258: }
259:
260: /**
261: * Registers the given validator to the existing map of validators.
262: * This will <b>add</b> to the existing list.
263: *
264: * @param name name of validator to add.
265: * @param className the FQ classname of the validator.
266: */
267: public static void registerValidator(String name, String className) {
268: if (LOG.isDebugEnabled()) {
269: LOG.debug("Registering validator of class " + className
270: + " with name " + name);
271: }
272:
273: validators.put(name, className);
274: }
275:
276: /**
277: * Lookup to get the FQ classname of the given validator name.
278: *
279: * @param name name of validator to lookup.
280: * @return the found FQ classname
281: * @throws IllegalArgumentException is thrown if the name is not found.
282: */
283: public static String lookupRegisteredValidatorType(String name) {
284: // lookup the validator class mapped to the type name
285: String className = (String) validators.get(name);
286:
287: if (className == null) {
288: throw new IllegalArgumentException(
289: "There is no validator class mapped to the name "
290: + name);
291: }
292:
293: return className;
294: }
295:
296: private static void parseValidators() {
297: if (LOG.isDebugEnabled()) {
298: LOG.debug("Loading validator definitions.");
299: }
300:
301: String resourceName = "validators.xml";
302: InputStream is = ClassLoaderUtil.getResourceAsStream(
303: resourceName, ValidatorFactory.class);
304: if (is == null) {
305: resourceName = "com/opensymphony/xwork/validator/validators/default.xml";
306: is = ClassLoaderUtil.getResourceAsStream(resourceName,
307: ValidatorFactory.class);
308: }
309:
310: if (is != null) {
311: ValidatorFileParser.parseValidatorDefinitions(is,
312: resourceName);
313: }
314: }
315: }
|