1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.validator;
18
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.InputStreamReader;
23 import java.io.Serializable;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Modifier;
27 import java.nio.charset.StandardCharsets;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.StringTokenizer;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.commons.validator.util.ValidatorUtils;
37
38
39
40
41
42
43
44 public class ValidatorAction implements Serializable {
45
46 private static final long serialVersionUID = 1339713700053204597L;
47
48
49
50
51 private transient Log log = LogFactory.getLog(ValidatorAction.class);
52
53
54
55
56 private String name;
57
58
59
60
61 private String className;
62
63
64
65
66 private Class<?> validationClass;
67
68
69
70
71 private String method;
72
73
74
75
76 private transient Method validationMethod;
77
78
79
80
81
82
83
84
85
86
87
88 private String methodParams = Validator.BEAN_PARAM + "," + Validator.VALIDATOR_ACTION_PARAM + "," + Validator.FIELD_PARAM;
89
90
91
92
93 private Class<?>[] parameterClasses;
94
95
96
97
98
99 private String depends;
100
101
102
103
104 private String msg;
105
106
107
108
109 private String jsFunctionName;
110
111
112
113
114 private String jsFunction;
115
116
117
118
119 private String javascript;
120
121
122
123
124 private Object instance;
125
126
127
128
129
130 private final List<String> dependencyList = Collections.synchronizedList(new ArrayList<>());
131
132
133
134
135 private final List<String> methodParameterList = new ArrayList<>();
136
137
138
139
140 public ValidatorAction() {
141
142 }
143
144
145
146
147
148
149
150
151
152
153 boolean executeValidationMethod(final Field field,
154
155
156 final Map<String, Object> params, final ValidatorResults results, final int pos) throws ValidatorException {
157
158 params.put(Validator.VALIDATOR_ACTION_PARAM, this);
159
160 try {
161 if (validationMethod == null) {
162 synchronized (this) {
163 final ClassLoader loader = getClassLoader(params);
164 loadValidationClass(loader);
165 loadParameterClasses(loader);
166 loadValidationMethod();
167 }
168 }
169
170 final Object[] paramValues = getParameterValues(params);
171
172 if (field.isIndexed()) {
173 handleIndexedField(field, pos, paramValues);
174 }
175
176 Object result = null;
177 try {
178 result = validationMethod.invoke(getValidationClassInstance(), paramValues);
179
180 } catch (IllegalArgumentException | IllegalAccessException e) {
181 throw new ValidatorException(e.getMessage());
182 } catch (final InvocationTargetException e) {
183
184 if (e.getTargetException() instanceof Exception) {
185 throw (Exception) e.getTargetException();
186
187 }
188 if (e.getTargetException() instanceof Error) {
189 throw (Error) e.getTargetException();
190 }
191 }
192
193 final boolean valid = isValid(result);
194 if (!valid || valid && !onlyReturnErrors(params)) {
195 results.add(field, name, valid, result);
196 }
197
198 if (!valid) {
199 return false;
200 }
201
202
203
204 } catch (final Exception e) {
205 if (e instanceof ValidatorException) {
206 throw (ValidatorException) e;
207 }
208
209 getLog().error("Unhandled exception thrown during validation: " + e.getMessage(), e);
210
211 results.add(field, name, false);
212 return false;
213 }
214
215 return true;
216 }
217
218
219
220
221 private String formatJavaScriptFileName() {
222 String fname = jsFunction.substring(1);
223
224 if (!jsFunction.startsWith("/")) {
225 fname = jsFunction.replace('.', '/') + ".js";
226 }
227
228 return fname;
229 }
230
231
232
233
234 private String generateJsFunction() {
235 final StringBuilder jsName = new StringBuilder("org.apache.commons.validator.javascript");
236
237 jsName.append(".validate");
238 jsName.append(name.substring(0, 1).toUpperCase());
239 jsName.append(name.substring(1));
240
241 return jsName.toString();
242 }
243
244
245
246
247 private ClassLoader getClassLoader(final Map<String, Object> params) {
248 final Validator v = getValidator(params);
249 return v.getClassLoader();
250 }
251
252
253
254
255
256
257 public String getClassname() {
258 return className;
259 }
260
261
262
263
264
265
266 public List<String> getDependencyList() {
267 return Collections.unmodifiableList(dependencyList);
268 }
269
270
271
272
273
274
275 public String getDepends() {
276 return depends;
277 }
278
279
280
281
282
283
284 public synchronized String getJavascript() {
285 return javascript;
286 }
287
288
289
290
291
292
293 public String getJsFunctionName() {
294 return jsFunctionName;
295 }
296
297
298
299
300
301
302
303
304 private Log getLog() {
305 if (log == null) {
306 log = LogFactory.getLog(ValidatorAction.class);
307 }
308 return log;
309 }
310
311
312
313
314
315
316 public String getMethod() {
317 return method;
318 }
319
320
321
322
323
324
325 public String getMethodParams() {
326 return methodParams;
327 }
328
329
330
331
332
333
334 public String getMsg() {
335 return msg;
336 }
337
338
339
340
341
342
343 public String getName() {
344 return name;
345 }
346
347
348
349
350
351
352
353
354 private Object[] getParameterValues(final Map<String, ? super Object> params) {
355
356 final Object[] paramValue = new Object[methodParameterList.size()];
357
358 for (int i = 0; i < methodParameterList.size(); i++) {
359 final String paramClassName = methodParameterList.get(i);
360 paramValue[i] = params.get(paramClassName);
361 }
362
363 return paramValue;
364 }
365
366
367
368
369 private Object getValidationClassInstance() throws ValidatorException {
370 if (Modifier.isStatic(validationMethod.getModifiers())) {
371 instance = null;
372
373 } else if (instance == null) {
374 try {
375 instance = validationClass.getConstructor().newInstance();
376 } catch (final ReflectiveOperationException e) {
377 final String msg1 = "Couldn't create instance of " + className + ". " + e.getMessage();
378
379 throw new ValidatorException(msg1);
380 }
381 }
382
383 return instance;
384 }
385
386 private Validator getValidator(final Map<String, Object> params) {
387 return (Validator) params.get(Validator.VALIDATOR_PARAM);
388 }
389
390
391
392
393
394
395
396
397 private void handleIndexedField(final Field field, final int pos, final Object[] paramValues) throws ValidatorException {
398
399 final int beanIndex = methodParameterList.indexOf(Validator.BEAN_PARAM);
400 final int fieldIndex = methodParameterList.indexOf(Validator.FIELD_PARAM);
401
402 final Object[] indexedList = field.getIndexedProperty(paramValues[beanIndex]);
403
404
405 paramValues[beanIndex] = indexedList[pos];
406
407
408
409 final Field indexedField = (Field) field.clone();
410 indexedField.setKey(ValidatorUtils.replace(indexedField.getKey(), Field.TOKEN_INDEXED, "[" + pos + "]"));
411
412 paramValues[fieldIndex] = indexedField;
413 }
414
415
416
417
418 protected void init() {
419 loadJavascriptFunction();
420 }
421
422
423
424
425
426
427
428 public boolean isDependency(final String validatorName) {
429 return dependencyList.contains(validatorName);
430 }
431
432
433
434
435
436 private boolean isValid(final Object result) {
437 if (result instanceof Boolean) {
438 final Boolean valid = (Boolean) result;
439 return valid.booleanValue();
440 }
441 return result != null;
442 }
443
444
445
446
447 private boolean javaScriptAlreadyLoaded() {
448 return javascript != null;
449 }
450
451
452
453
454
455
456
457
458 protected synchronized void loadJavascriptFunction() {
459
460 if (javaScriptAlreadyLoaded()) {
461 return;
462 }
463
464 if (getLog().isTraceEnabled()) {
465 getLog().trace(" Loading function begun");
466 }
467
468 if (jsFunction == null) {
469 jsFunction = generateJsFunction();
470 }
471
472 final String javaScriptFileName = formatJavaScriptFileName();
473
474 if (getLog().isTraceEnabled()) {
475 getLog().trace(" Loading js function '" + javaScriptFileName + "'");
476 }
477
478 javascript = readJavaScriptFile(javaScriptFileName);
479
480 if (getLog().isTraceEnabled()) {
481 getLog().trace(" Loading JavaScript function completed");
482 }
483
484 }
485
486
487
488
489
490
491
492 private void loadParameterClasses(final ClassLoader loader) throws ValidatorException {
493
494 if (parameterClasses != null) {
495 return;
496 }
497
498 final Class<?>[] parameterClasses = new Class[methodParameterList.size()];
499
500 for (int i = 0; i < methodParameterList.size(); i++) {
501 final String paramClassName = methodParameterList.get(i);
502
503 try {
504 parameterClasses[i] = loader.loadClass(paramClassName);
505
506 } catch (final ClassNotFoundException e) {
507 throw new ValidatorException(e.getMessage());
508 }
509 }
510
511 this.parameterClasses = parameterClasses;
512 }
513
514
515
516
517
518
519
520 private void loadValidationClass(final ClassLoader loader) throws ValidatorException {
521
522 if (validationClass != null) {
523 return;
524 }
525
526 try {
527 validationClass = loader.loadClass(className);
528 } catch (final ClassNotFoundException e) {
529 throw new ValidatorException(e.toString());
530 }
531 }
532
533
534
535
536
537
538 private void loadValidationMethod() throws ValidatorException {
539 if (validationMethod != null) {
540 return;
541 }
542
543 try {
544 validationMethod = validationClass.getMethod(method, parameterClasses);
545
546 } catch (final NoSuchMethodException e) {
547 throw new ValidatorException("No such validation method: " + e.getMessage());
548 }
549 }
550
551
552
553
554 private boolean onlyReturnErrors(final Map<String, Object> params) {
555 final Validator v = getValidator(params);
556 return v.getOnlyReturnErrors();
557 }
558
559
560
561
562
563
564
565
566
567
568 private InputStream openInputStream(final String javaScriptFileName, final ClassLoader classLoader) {
569 InputStream is = null;
570 if (classLoader != null) {
571 is = classLoader.getResourceAsStream(javaScriptFileName);
572 }
573 if (is == null) {
574 return getClass().getResourceAsStream(javaScriptFileName);
575 }
576 return is;
577 }
578
579
580
581
582
583
584
585 private String readJavaScriptFile(final String javaScriptFileName) {
586 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
587 if (classLoader == null) {
588 classLoader = getClass().getClassLoader();
589 }
590
591 final InputStream is = openInputStream(javaScriptFileName, classLoader);
592 if (is == null) {
593 getLog().debug(" Unable to read javascript name " + javaScriptFileName);
594 return null;
595 }
596 final StringBuilder buffer = new StringBuilder();
597
598 try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
599 String line = null;
600 while ((line = reader.readLine()) != null) {
601 buffer.append(line).append("\n");
602 }
603 } catch (final IOException e) {
604 getLog().error("Error reading JavaScript file.", e);
605
606 }
607 final String function = buffer.toString();
608 return function.isEmpty() ? null : function;
609 }
610
611
612
613
614
615
616
617 @Deprecated
618 public void setClassname(final String className) {
619 this.className = className;
620 }
621
622
623
624
625
626
627 public void setClassName(final String className) {
628 this.className = className;
629 }
630
631
632
633
634
635
636 public void setDepends(final String depends) {
637 this.depends = depends;
638
639 dependencyList.clear();
640
641 final StringTokenizer st = new StringTokenizer(depends, ",");
642 while (st.hasMoreTokens()) {
643 final String depend = st.nextToken().trim();
644
645 if (depend != null && !depend.isEmpty()) {
646 dependencyList.add(depend);
647 }
648 }
649 }
650
651
652
653
654
655
656 public synchronized void setJavascript(final String javaScript) {
657 if (jsFunction != null) {
658 throw new IllegalStateException("Cannot call setJavascript() after calling setJsFunction()");
659 }
660
661 this.javascript = javaScript;
662 }
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691 public synchronized void setJsFunction(final String jsFunction) {
692 if (javascript != null) {
693 throw new IllegalStateException("Cannot call setJsFunction() after calling setJavascript()");
694 }
695
696 this.jsFunction = jsFunction;
697 }
698
699
700
701
702
703
704 public void setJsFunctionName(final String jsFunctionName) {
705 this.jsFunctionName = jsFunctionName;
706 }
707
708
709
710
711
712
713 public void setMethod(final String method) {
714 this.method = method;
715 }
716
717
718
719
720
721
722 public void setMethodParams(final String methodParams) {
723 this.methodParams = methodParams;
724
725 methodParameterList.clear();
726
727 final StringTokenizer st = new StringTokenizer(methodParams, ",");
728 while (st.hasMoreTokens()) {
729 final String value = st.nextToken().trim();
730
731 if (value != null && !value.isEmpty()) {
732 methodParameterList.add(value);
733 }
734 }
735 }
736
737
738
739
740
741
742 public void setMsg(final String msg) {
743 this.msg = msg;
744 }
745
746
747
748
749
750
751 public void setName(final String name) {
752 this.name = name;
753 }
754
755
756
757
758
759
760 @Override
761 public String toString() {
762 final StringBuilder results = new StringBuilder("ValidatorAction: ");
763 results.append(name);
764 results.append("\n");
765
766 return results.toString();
767 }
768 }