2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.validator.util;
19  
20  import java.lang.reflect.Method;
21  import java.lang.reflect.Modifier;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Locale;
26  import java.util.Map;
27  import org.apache.commons.validator.Arg;
28  import org.apache.commons.validator.Field;
29  import org.apache.commons.validator.Form;
30  import org.apache.commons.validator.ValidatorAction;
31  import org.apache.commons.validator.ValidatorResources;
32  
33  /***
34   * <p>Custom wrapper around a Commons Validator <code>ValidatorAction</code>
35   * that precalculates as many of the introspective lookup operations as
36   * possible.  This ensures that runtime operation of the validator checks
37   * can proceed as quickly as possible.</p>
38   */
39  public final class ShaleValidatorAction {
40      
41  
42      // ------------------------------------------------------------ Constructors
43  
44  
45      /***
46       * <p>Create a new instance of ShaleValidatorAction that wraps the
47       * specified <code>ValidatorAction</code>.</p>
48       *
49       * @param resources <code>ValidatorResources</code> for this application
50       * @param action The <code>ValidatorAction</code> to be wrapped
51       *
52       * @exception IllegalArgumentException if configuration data is missing
53       *  or incorrect
54       */
55      public ShaleValidatorAction(ValidatorResources resources,
56                                  ValidatorAction action) {
57  
58          this.resources = resources;
59          this.action = action;
60          precalculate();
61  
62      }
63      
64  
65      // -------------------------------------------------------- Static Variables
66  
67  
68      /***
69       * <p>The prefix for form names that correspond to validator types.</p>
70       */
71      private static final String FORM_NAME_PREFIX = "org.apache.shale.validator.";
72  
73  
74      /***
75       * <p>Map of the classes for primitive Java types, keyed by the Java
76       * keywords for the corresponding primitive.</p>
77       */
78      private static final Map PRIMITIVE_TYPES = new HashMap();
79      static {
80          PRIMITIVE_TYPES.put("boolean", boolean.class);
81          PRIMITIVE_TYPES.put("byte", byte.class);
82          PRIMITIVE_TYPES.put("char", char.class);
83          PRIMITIVE_TYPES.put("double", double.class);
84          PRIMITIVE_TYPES.put("float", float.class);
85          PRIMITIVE_TYPES.put("int", int.class);
86          PRIMITIVE_TYPES.put("long", long.class);
87          PRIMITIVE_TYPES.put("short", short.class);
88      }
89  
90  
91      // ------------------------------------------------------ Instance Variables
92  
93  
94      /***
95       * <p>The <code>ValidatorAction</code> that we are wrapping.</p>
96       */
97      private ValidatorAction action = null;
98  
99  
100     /***
101      * <p>The class containing the validation method to be called for this action.</p>
102      */
103     private Class clazz = null;
104 
105 
106     /***
107      * <p>The field definition containing the mappings for parameter and
108      * message subtitution for this validator type.</p>
109      */
110     private Field field = null;
111 
112 
113     /***
114      * <p>The form definition corresponding to this validator type that is used
115      * for mapping parameter and message arguments.</p>
116      */
117     private Form form = null;
118 
119 
120     /***
121      * <p>Return an instance of the specified validation class, if the requested
122      * validation method is not static.</p>
123      */
124     private Object instance = null;
125 
126 
127     /***
128      * <p>Array of message arguments defining replacement parameters for error
129      * messages related to this validator.</p>
130      */
131     private Arg[] messageArgs = null;
132 
133 
134     /***
135      * <p>The validation <code>Method</code> to be called for this action.</p>
136      */
137     private Method method = null;
138 
139 
140     /***
141      * <p>Array of parameter arguments defining values to be sent in to
142      * the validator method for this validator.</p>
143      */
144     private Arg[] parameterArgs = null;
145 
146 
147     /***
148      * <p>The <code>ValidatorResources</code> for this application.</p>
149      */
150     private ValidatorResources resources = null;
151 
152 
153     /***
154      * <p>The parameter signature for the validation method to be called
155      * for this action.</p>
156      */
157     private Class[] signature = null;
158 
159 
160     // -------------------------------------------------------------- Properties
161 
162 
163     /***
164      * <p>Return the <code>ValidatorAction</code> instance we are wrapping.</p>
165      */
166     public ValidatorAction getAction() {
167         return this.action;
168     }
169 
170 
171     /***
172      * <p>Return an instance of the requested validator class, if the requested
173      * validation method is not static.  If the method is static, return
174      * <code>null</code> instead.</p>
175      */
176     public Object getInstance() {
177         return this.instance;
178     }
179 
180 
181     /***
182      * <p>Return an array of argument metadata describing subtitution values
183      * for error messages emitted by this validator type.</p>
184      */
185     public Arg[] getMessageArgs() {
186         return this.messageArgs;
187     }
188 
189 
190     /***
191      * <p>Return the lookup key for the error message template to be used
192      * if this validation fails.</p>
193      */
194     public String getMessageKey() {
195         return this.action.getMsg();
196     }
197 
198 
199     /***
200      * <p>Return the validation <code>Method</code> to be called for this
201      * action.</p>
202      */
203     public Method getMethod() {
204         return this.method;
205     }
206 
207 
208     /***
209      * <p>Return an array of argument metadata describing the parameter values
210      * to be sent to the validation method for this validator type.</p>
211      */
212     public Arg[] getParameterArgs() {
213         return this.parameterArgs;
214     }
215 
216 
217     /***
218      * <p>Return the parameter signature for the validation method to be called
219      * for this action.</p>
220      */
221     public Class[] getSignature() {
222         return this.signature;
223     }
224 
225 
226     // --------------------------------------------------------- Private Methods
227 
228 
229     /***
230      * <p>Precalculate the values to be returned by our public properties.</p>
231      *
232      * @exception IllegalArgumentException if configuration data is missing
233      *  or incorrect
234      */
235     private void precalculate() {
236 
237         List list = null;
238         String value = null;
239         String values = null;
240 
241         // Acquire a reference to the class loader we will use
242         ClassLoader loader = Thread.currentThread().getContextClassLoader();
243         if (loader == null) {
244             loader = this.getClass().getClassLoader();
245         }
246 
247         // Look up the class that contains the validation method
248         // we will be calling
249         try {
250             this.clazz = loader.loadClass(action.getClassname());
251         } catch (ClassNotFoundException e) {
252             throw new IllegalArgumentException("ClassNotFoundException:"
253                 + " Cannot load class '" + action.getClassname()
254                 + "' specified by ValidatorAction with name '"
255                 + action.getName() + "'");
256         }
257 
258         // Calculate the method parameter signature of the validation
259         // method we will be calling
260         list = new ArrayList();
261         values = action.getMethodParams().trim();
262         while (values.length() > 0) {
263             // Identify the class name of the next class to be loaded
264             int comma = values.indexOf(',');
265             if (comma >= 0) {
266                 value = values.substring(0, comma).trim();
267                 values = values.substring(comma + 1);
268             } else {
269                 value = values.trim();
270                 values = "";
271             }
272             if (value.length() == 0) {
273                 break;
274             }
275             // Add the corresponding class instance to our list
276             Class clazz = (Class) PRIMITIVE_TYPES.get(value);
277             if (clazz == null) {
278                 try {
279                     clazz = loader.loadClass(value);
280                 } catch (ClassNotFoundException e) {
281                     throw new IllegalArgumentException("ClassNotFoundException:"
282                         + " Cannot load method parameter class '" + value
283                         + "' specified by ValidatorAction with name '"
284                         + action.getName() + "'");
285                 }
286             }
287             list.add(clazz);
288         }
289         this.signature = (Class[]) list.toArray(new Class[list.size()]);
290 
291         // Look up the validation method we will be calling
292         try {
293             this.method = this.clazz.getMethod(action.getMethod(), this.signature);
294         } catch (NoSuchMethodException e) {
295             throw new IllegalArgumentException("NoSuchMethodException:"
296                 + " Method named '" + action.getMethod() + "' with parameters '"
297                 + action.getMethodParams() + "' for class '" + action.getClassname()
298                 + "' not found. Specified by ValidatorAction with name '"
299                 + action.getName() + "'");
300         }
301 
302         // Create an instance of the validator class if we need one
303         // (which is true only if the validation method is not static
304         if (!Modifier.isStatic(this.method.getModifiers())) {
305             try {
306                 this.instance = clazz.newInstance();
307             } catch (IllegalAccessException e) {
308                 throw new IllegalArgumentException("IllegalAccessException:"
309                     + " Not allowed to instantiate class '" + action.getClassname()
310                     + "' specified by ValidatorAction with name '" + action.getName()
311                     + "' (validation method with name '" + action.getMethod()
312                     + "' is not static)");
313             } catch (InstantiationException e) {
314                 throw new IllegalArgumentException("InstantiationException:"
315                     + " Could not instantiate class '" + action.getClassname()
316                     + "' specified by ValidatorAction with name '" + action.getName()
317                     + "' (validation method with name '" + action.getMethod()
318                     + "' is not static)");
319             }
320         }
321 
322         // Cache the form definition associated with this validator action type
323         this.form = resources.getForm(Locale.getDefault(),
324                                       FORM_NAME_PREFIX + action.getName());
325         if (this.form == null) {
326             throw new IllegalArgumentException(FORM_NAME_PREFIX + action.getName());
327         }
328 
329         // Look up the predefined "fields" from our form definition
330         // and cache interesting information
331         this.field = this.form.getField(action.getName());
332         if (this.field == null) {
333             throw new IllegalArgumentException("Field " + action.getName());
334         }
335         this.messageArgs = field.getArgs("message");
336         if (this.messageArgs == null) {
337             this.messageArgs = new Arg[0];
338         }
339         this.parameterArgs = field.getArgs("parameter");
340         if (this.parameterArgs == null) {
341             this.parameterArgs = new Arg[0];
342         }
343 
344         // FIXME - For some reason, Commons Validator is returning a trailing
345         // null Arg in the parameterArgs array sometimes, so strip it off
346         // if present
347         if ((this.parameterArgs.length > 0)
348          && (this.parameterArgs[this.parameterArgs.length - 1] == null)) {
349             Arg[] results = new Arg[this.parameterArgs.length - 1];
350             System.arraycopy(this.parameterArgs, 0, results, 0, results.length);
351             this.parameterArgs = results;
352         }
353 
354         // For robustness, validate the length of the parameter arguments
355         // and parameter signature arrays, which should be identical
356         if (this.parameterArgs.length != this.signature.length) {
357             throw new IllegalArgumentException(this.action.getName()
358               + ": signature defines " + this.signature.length
359               + " elements but " + this.parameterArgs.length
360               + " parameter arguments are specified");
361         }
362 
363     }
364 
365 
366 }