1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package javax.faces.validator;
20
21 import java.beans.FeatureDescriptor;
22 import java.security.AccessController;
23 import java.security.PrivilegedActionException;
24 import java.security.PrivilegedExceptionAction;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34
35 import javax.el.ELContext;
36 import javax.el.ELResolver;
37 import javax.el.FunctionMapper;
38 import javax.el.ValueExpression;
39 import javax.el.VariableMapper;
40 import javax.faces.FacesException;
41 import javax.faces.application.FacesMessage;
42 import javax.faces.component.PartialStateHolder;
43 import javax.faces.component.UIComponent;
44 import javax.faces.context.FacesContext;
45 import javax.faces.el.CompositeComponentExpressionHolder;
46 import javax.validation.ConstraintViolation;
47 import javax.validation.MessageInterpolator;
48 import javax.validation.Validation;
49 import javax.validation.ValidatorFactory;
50 import javax.validation.groups.Default;
51 import javax.validation.metadata.BeanDescriptor;
52
53 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
54 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
55 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFValidator;
56 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
57
58
59
60
61
62
63
64
65
66
67
68
69
70 @JSFValidator(
71 name="f:validateBean",
72 bodyContent="empty")
73 @JSFJspProperty(
74 name = "binding",
75 returnType = "javax.faces.validator.BeanValidator",
76 longDesc = "A ValueExpression that evaluates to a BeanValidator.")
77 public class BeanValidator implements Validator, PartialStateHolder
78 {
79
80 private static final Logger log = Logger.getLogger(BeanValidator.class.getName());
81
82
83
84
85 public static final String VALIDATOR_ID = "javax.faces.Bean";
86
87
88
89
90 public static final String MESSAGE_ID = "javax.faces.validator.BeanValidator.MESSAGE";
91
92
93
94
95
96 @JSFWebConfigParam(defaultValue="true", expectedValues="true, false", since="2.0", group="validation")
97 public static final String DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME
98 = "javax.faces.validator.DISABLE_DEFAULT_BEAN_VALIDATOR";
99
100
101
102
103
104
105
106
107 public static final String VALIDATOR_FACTORY_KEY = "javax.faces.validator.beanValidator.ValidatorFactory";
108
109
110
111
112 public static final String VALIDATION_GROUPS_DELIMITER = ",";
113
114
115
116
117
118 public static final String EMPTY_VALIDATION_GROUPS_PATTERN = "^[\\W,]*$";
119
120 private static final Class<?>[] DEFAULT_VALIDATION_GROUPS_ARRAY = new Class<?>[] { Default.class };
121
122 private static final String DEFAULT_VALIDATION_GROUP_NAME = "javax.validation.groups.Default";
123
124 private String validationGroups;
125
126 private Class<?>[] validationGroupsArray;
127
128 private boolean isTransient = false;
129
130 private boolean _initialStateMarked = false;
131
132
133
134
135 public void validate(final FacesContext context, final UIComponent component, final Object value)
136 throws ValidatorException
137 {
138 if (context == null)
139 {
140 throw new NullPointerException("context");
141 }
142 if (component == null)
143 {
144 throw new NullPointerException("component");
145 }
146
147 ValueExpression valueExpression = component.getValueExpression("value");
148 if (valueExpression == null)
149 {
150 log.warning("cannot validate component with empty value: "
151 + component.getClientId(context));
152 return;
153 }
154
155
156 _ValueReferenceWrapper reference = getValueReference(valueExpression, context);
157 if (reference == null)
158 {
159 return;
160 }
161 Object base = reference.getBase();
162 if (base == null)
163 {
164 return;
165 }
166
167 Class<?> valueBaseClass = base.getClass();
168 if (valueBaseClass == null)
169 {
170 return;
171 }
172
173 Object referenceProperty = reference.getProperty();
174 if (!(referenceProperty instanceof String))
175 {
176
177
178
179 return;
180 }
181 String valueProperty = (String) referenceProperty;
182
183
184 ValidatorFactory validatorFactory = createValidatorFactory(context);
185 javax.validation.Validator validator = createValidator(validatorFactory, context);
186 BeanDescriptor beanDescriptor = validator.getConstraintsForClass(valueBaseClass);
187 if (!beanDescriptor.isBeanConstrained())
188 {
189 return;
190 }
191
192
193 Class[] validationGroupsArray = this.validationGroupsArray;
194
195
196 Set constraintViolations = validator.validateValue(valueBaseClass, valueProperty, value, validationGroupsArray);
197 if (!constraintViolations.isEmpty())
198 {
199 Set<FacesMessage> messages = new LinkedHashSet<FacesMessage>(constraintViolations.size());
200 for (Object violation: constraintViolations)
201 {
202 ConstraintViolation constraintViolation = (ConstraintViolation) violation;
203 String message = constraintViolation.getMessage();
204 Object[] args = new Object[]{ message, _MessageUtils.getLabel(context, component) };
205 FacesMessage msg = _MessageUtils.getErrorMessage(context, MESSAGE_ID, args);
206 messages.add(msg);
207 }
208 throw new ValidatorException(messages);
209 }
210 }
211
212 private javax.validation.Validator createValidator(final ValidatorFactory validatorFactory, FacesContext context)
213 {
214
215
216 if (validationGroupsArray == null)
217 {
218 postSetValidationGroups();
219 }
220
221 return validatorFactory
222 .usingContext()
223 .messageInterpolator(new FacesMessageInterpolator(
224 validatorFactory.getMessageInterpolator(), context))
225 .getValidator();
226
227 }
228
229
230 private static volatile boolean firstValueReferenceWarning = true;
231
232
233
234
235
236
237
238
239 private _ValueReferenceWrapper getValueReference(
240 final ValueExpression valueExpression, final FacesContext context)
241 {
242 ELContext elCtx = context.getELContext();
243 if (_ExternalSpecifications.isUnifiedELAvailable())
244 {
245
246
247
248 _ValueReferenceWrapper wrapper = _BeanValidatorUELUtils.getUELValueReferenceWrapper(valueExpression, elCtx);
249 if (wrapper != null)
250 {
251 if (wrapper.getProperty() == null)
252 {
253
254 if (firstValueReferenceWarning && log.isLoggable(Level.WARNING))
255 {
256 firstValueReferenceWarning = false;
257 log.warning("ValueReference.getProperty() is null. " +
258 "Falling back to classic ValueReference resolving. " +
259 "This fallback may hurt performance. " +
260 "This may be caused by a bug your EL implementation. " +
261 "Glassfish EL-impl-2.2.3 is known for this issue. " +
262 "Try switching to a different EL implementation.");
263 }
264 }
265 else
266 {
267 return wrapper;
268 }
269 }
270 }
271
272
273 return _ValueReferenceResolver.resolve(valueExpression, elCtx);
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287 private ValidatorFactory createValidatorFactory(FacesContext context)
288 {
289 Map<String, Object> applicationMap = context.getExternalContext().getApplicationMap();
290 Object attr = applicationMap.get(VALIDATOR_FACTORY_KEY);
291 if (attr instanceof ValidatorFactory)
292 {
293 return (ValidatorFactory) attr;
294 }
295 else
296 {
297 synchronized (this)
298 {
299 if (_ExternalSpecifications.isBeanValidationAvailable())
300 {
301 ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
302 applicationMap.put(VALIDATOR_FACTORY_KEY, factory);
303 return factory;
304 }
305 else
306 {
307 throw new FacesException("Bean Validation is not present");
308 }
309 }
310 }
311 }
312
313
314
315
316
317 private void postSetValidationGroups()
318 {
319 if (this.validationGroups == null || this.validationGroups.matches(EMPTY_VALIDATION_GROUPS_PATTERN))
320 {
321 this.validationGroupsArray = DEFAULT_VALIDATION_GROUPS_ARRAY;
322 }
323 else
324 {
325 String[] classes = this.validationGroups.split(VALIDATION_GROUPS_DELIMITER);
326 List<Class<?>> validationGroupsList = new ArrayList<Class<?>>(classes.length);
327
328 for (String clazz : classes)
329 {
330 clazz = clazz.trim();
331 if (!clazz.equals(""))
332 {
333 Class<?> theClass = null;
334 ClassLoader cl = null;
335 if (System.getSecurityManager() != null)
336 {
337 try
338 {
339 cl = AccessController.doPrivileged(new PrivilegedExceptionAction<ClassLoader>()
340 {
341 public ClassLoader run() throws PrivilegedActionException
342 {
343 return Thread.currentThread().getContextClassLoader();
344 }
345 });
346 }
347 catch (PrivilegedActionException pae)
348 {
349 throw new FacesException(pae);
350 }
351 }
352 else
353 {
354 cl = Thread.currentThread().getContextClassLoader();
355 }
356
357 try
358 {
359
360 theClass = Class.forName(clazz,false,cl);
361 }
362 catch (ClassNotFoundException ignore)
363 {
364 try
365 {
366
367 theClass = Class.forName(clazz,false, BeanValidator.class.getClassLoader());
368 }
369 catch (ClassNotFoundException e)
370 {
371 throw new RuntimeException("Could not load validation group", e);
372 }
373 }
374
375 validationGroupsList.add(theClass);
376 }
377 }
378
379 this.validationGroupsArray = validationGroupsList.toArray(new Class[validationGroupsList.size()]);
380 }
381 }
382
383
384 public Object saveState(final FacesContext context)
385 {
386 if (!initialStateMarked())
387 {
388
389 return this.validationGroups;
390 }
391 else if (DEFAULT_VALIDATION_GROUP_NAME.equals(this.validationGroups))
392 {
393
394 return null;
395 }
396 else
397 {
398
399
400
401 return this.validationGroups;
402 }
403 }
404
405
406 public void restoreState(final FacesContext context, final Object state)
407 {
408 if (state != null)
409 {
410 this.validationGroups = (String) state;
411 }
412 else
413 {
414
415
416 this.validationGroups = null;
417 }
418
419
420 }
421
422
423
424
425
426 @JSFProperty
427 public String getValidationGroups()
428 {
429 return validationGroups;
430 }
431
432
433
434
435
436
437 public void setValidationGroups(final String validationGroups)
438 {
439 this.validationGroups = validationGroups;
440 this.clearInitialState();
441
442 }
443
444 @JSFProperty
445 private Boolean isDisabled()
446 {
447 return null;
448 }
449
450 @JSFProperty
451 private String getFor()
452 {
453 return null;
454 }
455
456
457 public boolean isTransient()
458 {
459 return isTransient;
460 }
461
462
463 public void setTransient(final boolean isTransient)
464 {
465 this.isTransient = isTransient;
466 }
467
468
469 public void clearInitialState()
470 {
471 _initialStateMarked = false;
472 }
473
474
475 public boolean initialStateMarked()
476 {
477 return _initialStateMarked;
478 }
479
480
481 public void markInitialState()
482 {
483 _initialStateMarked = true;
484 }
485
486
487
488
489
490
491
492
493
494
495 private static class FacesMessageInterpolator implements MessageInterpolator
496 {
497 private final FacesContext facesContext;
498 private final MessageInterpolator interpolator;
499
500 public FacesMessageInterpolator(final MessageInterpolator interpolator, final FacesContext facesContext)
501 {
502 this.interpolator = interpolator;
503 this.facesContext = facesContext;
504 }
505
506 public String interpolate(final String s, final Context context)
507 {
508 Locale locale = facesContext.getViewRoot().getLocale();
509 return interpolator.interpolate(s, context, locale);
510 }
511
512 public String interpolate(final String s, final Context context, final Locale locale)
513 {
514 return interpolator.interpolate(s, context, locale);
515 }
516 }
517 }
518
519
520
521
522
523
524 final class _ValueReferenceWrapper
525 {
526 private final Object base;
527 private final Object property;
528
529
530
531
532
533
534
535 public _ValueReferenceWrapper(final Object base, final Object property)
536 {
537 this.base = base;
538 this.property = property;
539 }
540
541
542
543
544
545 public final Object getBase()
546 {
547 return base;
548 }
549
550
551
552
553
554 public final Object getProperty()
555 {
556 return property;
557 }
558 }
559
560
561
562
563
564 final class _ValueReferenceResolver extends ELResolver
565 {
566 private final ELResolver resolver;
567
568
569
570
571
572
573
574
575
576
577 private _ValueReferenceWrapper lastObject;
578
579
580
581
582
583 _ValueReferenceResolver(final ELResolver elResolver)
584 {
585 this.resolver = elResolver;
586 }
587
588
589
590
591
592
593
594
595 public static _ValueReferenceWrapper resolve(ValueExpression valueExpression, final ELContext elCtx)
596 {
597 _ValueReferenceResolver resolver = new _ValueReferenceResolver(elCtx.getELResolver());
598 ELContext elCtxDecorator = new _ELContextDecorator(elCtx, resolver);
599
600 valueExpression.getValue(elCtxDecorator);
601
602 while (resolver.lastObject.getBase() instanceof CompositeComponentExpressionHolder)
603 {
604 valueExpression = ((CompositeComponentExpressionHolder) resolver.lastObject.getBase())
605 .getExpression((String) resolver.lastObject.getProperty());
606 valueExpression.getValue(elCtxDecorator);
607 }
608
609 return resolver.lastObject;
610 }
611
612
613
614
615
616
617
618
619
620
621
622 @Override
623 public Object getValue(final ELContext context, final Object base, final Object property)
624 {
625 lastObject = new _ValueReferenceWrapper(base, property);
626 return resolver.getValue(context, base, property);
627 }
628
629
630 public final Class<?> getType(final ELContext ctx, final Object base, final Object property)
631 {
632 return resolver.getType(ctx, base, property);
633 }
634
635 public final void setValue(final ELContext ctx, final Object base, final Object property, final Object value)
636 {
637 resolver.setValue(ctx, base, property, value);
638 }
639
640 public final boolean isReadOnly(final ELContext ctx, final Object base, final Object property)
641 {
642 return resolver.isReadOnly(ctx, base, property);
643 }
644
645 public final Iterator<FeatureDescriptor> getFeatureDescriptors(final ELContext ctx, final Object base)
646 {
647 return resolver.getFeatureDescriptors(ctx, base);
648 }
649
650 public final Class<?> getCommonPropertyType(final ELContext ctx, final Object base)
651 {
652 return resolver.getCommonPropertyType(ctx, base);
653 }
654 }
655
656
657
658
659
660 final class _ELContextDecorator extends ELContext
661 {
662 private final ELContext ctx;
663 private final ELResolver interceptingResolver;
664
665
666
667
668
669
670
671 _ELContextDecorator(final ELContext elContext, final ELResolver interceptingResolver)
672 {
673 this.ctx = elContext;
674 this.interceptingResolver = interceptingResolver;
675 }
676
677
678
679
680
681 @Override
682 public final ELResolver getELResolver()
683 {
684 return interceptingResolver;
685 }
686
687
688 public final FunctionMapper getFunctionMapper()
689 {
690 return ctx.getFunctionMapper();
691 }
692
693 public final VariableMapper getVariableMapper()
694 {
695 return ctx.getVariableMapper();
696 }
697
698 public final void setPropertyResolved(final boolean resolved)
699 {
700 ctx.setPropertyResolved(resolved);
701 }
702
703 public final boolean isPropertyResolved()
704 {
705 return ctx.isPropertyResolved();
706 }
707
708 public final void putContext(final Class key, Object contextObject)
709 {
710 ctx.putContext(key, contextObject);
711 }
712
713 public final Object getContext(final Class key)
714 {
715 return ctx.getContext(key);
716 }
717
718 public final Locale getLocale()
719 {
720 return ctx.getLocale();
721 }
722
723 public final void setLocale(final Locale locale)
724 {
725 ctx.setLocale(locale);
726 }
727 }