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