1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.component.validate;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.ObjectOutputStream;
26 import java.io.Serializable;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Method;
29 import java.security.AccessController;
30 import java.security.PrivilegedActionException;
31 import java.security.PrivilegedExceptionAction;
32 import java.util.ArrayList;
33 import java.util.LinkedHashSet;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40 import javax.el.ELContext;
41 import javax.el.ValueExpression;
42 import javax.el.ValueReference;
43 import javax.faces.FacesException;
44 import javax.faces.application.FacesMessage;
45 import javax.faces.component.UIComponent;
46 import javax.faces.component.visit.VisitCallback;
47 import javax.faces.component.visit.VisitContext;
48 import javax.faces.component.visit.VisitResult;
49 import javax.faces.context.FacesContext;
50 import javax.faces.validator.BeanValidator;
51 import static javax.faces.validator.BeanValidator.EMPTY_VALIDATION_GROUPS_PATTERN;
52 import static javax.faces.validator.BeanValidator.MESSAGE_ID;
53 import static javax.faces.validator.BeanValidator.VALIDATION_GROUPS_DELIMITER;
54 import static javax.faces.validator.BeanValidator.VALIDATOR_FACTORY_KEY;
55 import javax.faces.validator.Validator;
56 import javax.faces.validator.ValidatorException;
57 import javax.validation.ConstraintViolation;
58 import javax.validation.MessageInterpolator;
59 import javax.validation.Validation;
60 import javax.validation.ValidatorFactory;
61 import javax.validation.groups.Default;
62 import javax.validation.metadata.BeanDescriptor;
63 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
64 import org.apache.myfaces.shared.util.MessageUtils;
65 import org.apache.myfaces.shared.util.MyFacesObjectInputStream;
66 import org.apache.myfaces.util.ExternalSpecifications;
67
68
69
70
71 public class WholeBeanValidator implements Validator
72 {
73 private static final Logger log = Logger.getLogger(WholeBeanValidator.class.getName());
74
75 private static final Class<?>[] DEFAULT_VALIDATION_GROUPS_ARRAY = new Class<?>[] { Default.class };
76
77 private static final String DEFAULT_VALIDATION_GROUP_NAME = "javax.validation.groups.Default";
78
79 private static final String CANDIDATE_COMPONENT_VALUES_MAP = "oam.WBV.candidatesMap";
80
81 private static final String BEAN_VALIDATION_FAILED = "oam.WBV.validationFailed";
82
83 private Class<?>[] validationGroupsArray;
84
85 @Override
86 public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
87 {
88 if (context == null)
89 {
90 throw new NullPointerException("context");
91 }
92 if (component == null)
93 {
94 throw new NullPointerException("component");
95 }
96
97 ValueExpression valueExpression = component.getValueExpression("value");
98 if (valueExpression == null)
99 {
100 log.warning("cannot validate component with empty value: "
101 + component.getClientId(context));
102 return;
103 }
104
105 Object base = valueExpression.getValue(context.getELContext());
106
107 Class<?> valueBaseClass = base.getClass();
108 if (valueBaseClass == null)
109 {
110 return;
111 }
112
113
114 ValidatorFactory validatorFactory = createValidatorFactory(context);
115 javax.validation.Validator validator = createValidator(validatorFactory, context,
116 (ValidateWholeBeanComponent)component);
117 BeanDescriptor beanDescriptor = validator.getConstraintsForClass(valueBaseClass);
118 if (!beanDescriptor.isBeanConstrained())
119 {
120 return;
121 }
122
123
124 Class[] validationGroupsArray = this.validationGroupsArray;
125
126
127
128
129
130 Boolean beanValidationFailed = (Boolean) context.getViewRoot().getTransientStateHelper()
131 .getTransient(BEAN_VALIDATION_FAILED);
132
133 if (Boolean.TRUE.equals(beanValidationFailed))
134 {
135
136 return;
137 }
138
139 Map<String, Object> candidatesMap = (Map<String, Object>) context.getViewRoot()
140 .getTransientStateHelper().getTransient(CANDIDATE_COMPONENT_VALUES_MAP);
141 if (candidatesMap != null)
142 {
143 Object copy = createBeanCopy(base);
144
145 UpdateBeanCopyCallback callback = new UpdateBeanCopyCallback(this, base, copy, candidatesMap);
146 context.getViewRoot().visitTree(
147 VisitContext.createVisitContext(context, candidatesMap.keySet(), null),
148 callback);
149
150 Set constraintViolations = validator.validate(copy, validationGroupsArray);
151 if (!constraintViolations.isEmpty())
152 {
153 Set<FacesMessage> messages = new LinkedHashSet<FacesMessage>(constraintViolations.size());
154 for (Object violation: constraintViolations)
155 {
156 ConstraintViolation constraintViolation = (ConstraintViolation) violation;
157 String message = constraintViolation.getMessage();
158 Object[] args = new Object[]{ message, MessageUtils.getLabel(context, component) };
159 FacesMessage msg = MessageUtils.getMessage(FacesMessage.SEVERITY_ERROR, MESSAGE_ID, args, context);
160 messages.add(msg);
161 }
162 throw new ValidatorException(messages);
163 }
164 }
165 }
166
167 private Object createBeanCopy(Object base)
168 {
169 Object copy = null;
170 try
171 {
172 copy = base.getClass().newInstance();
173 }
174 catch (Exception ex)
175 {
176 Logger.getLogger(WholeBeanValidator.class.getName()).log(Level.FINEST, null, ex);
177 }
178
179 if (base instanceof Serializable)
180 {
181 copy = copySerializableObject(base);
182 }
183 else if(base instanceof Cloneable)
184 {
185 Method cloneMethod;
186 try
187 {
188 cloneMethod = base.getClass().getMethod("clone");
189 copy = cloneMethod.invoke(base);
190 }
191 catch (Exception ex)
192 {
193 Logger.getLogger(WholeBeanValidator.class.getName()).log(Level.FINEST, null, ex);
194 }
195 }
196 else
197 {
198 Class<?> clazz = base.getClass();
199 try
200 {
201 Constructor<?> copyConstructor = clazz.getConstructor(clazz);
202 if (copyConstructor != null)
203 {
204 copy = copyConstructor.newInstance(base);
205 }
206 }
207 catch (Exception ex)
208 {
209 Logger.getLogger(WholeBeanValidator.class.getName()).log(Level.FINEST, null, ex);
210 }
211 }
212
213 if (copy == null)
214 {
215 throw new FacesException("Cannot create copy for wholeBeanValidator: "+base.getClass().getName());
216 }
217
218 return copy;
219 }
220
221 private Object copySerializableObject(Object base)
222 {
223 Object copy = null;
224 try
225 {
226 ByteArrayOutputStream baos = new ByteArrayOutputStream();
227 ObjectOutputStream oos = new ObjectOutputStream(baos);
228 oos.writeObject(base);
229 oos.flush();
230 oos.close();
231 baos.close();
232 byte[] byteData = baos.toByteArray();
233 ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
234 try
235 {
236 copy = new MyFacesObjectInputStream(bais).readObject();
237 }
238 catch (ClassNotFoundException e)
239 {
240
241 }
242 }
243 catch (IOException e)
244 {
245
246 }
247 return copy;
248 }
249
250 private javax.validation.Validator createValidator(final ValidatorFactory validatorFactory,
251 FacesContext context, ValidateWholeBeanComponent component)
252 {
253
254
255 if (validationGroupsArray == null)
256 {
257 postSetValidationGroups(component);
258 }
259
260 return validatorFactory
261 .usingContext()
262 .messageInterpolator(new FacesMessageInterpolator(
263 validatorFactory.getMessageInterpolator(), context))
264 .getValidator();
265
266 }
267
268
269
270
271
272
273
274
275
276 private ValueReference getValueReference(
277 final ValueExpression valueExpression, final FacesContext context)
278 {
279 ELContext elCtx = context.getELContext();
280
281 return _ValueReferenceResolver.resolve(valueExpression, elCtx);
282 }
283
284
285
286
287
288
289
290
291
292
293
294
295 private ValidatorFactory createValidatorFactory(FacesContext context)
296 {
297 Map<String, Object> applicationMap = context.getExternalContext().getApplicationMap();
298 Object attr = applicationMap.get(VALIDATOR_FACTORY_KEY);
299 if (attr instanceof ValidatorFactory)
300 {
301 return (ValidatorFactory) attr;
302 }
303 else
304 {
305 synchronized (this)
306 {
307 if (ExternalSpecifications.isBeanValidationAvailable())
308 {
309 ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
310 applicationMap.put(VALIDATOR_FACTORY_KEY, factory);
311 return factory;
312 }
313 else
314 {
315 throw new FacesException("Bean Validation is not present");
316 }
317 }
318 }
319 }
320
321
322
323
324
325 private void postSetValidationGroups(ValidateWholeBeanComponent component)
326 {
327 String validationGroups = getValidationGroups(component);
328 if (validationGroups == null || validationGroups.matches(EMPTY_VALIDATION_GROUPS_PATTERN))
329 {
330 this.validationGroupsArray = DEFAULT_VALIDATION_GROUPS_ARRAY;
331 }
332 else
333 {
334 String[] classes = validationGroups.split(VALIDATION_GROUPS_DELIMITER);
335 List<Class<?>> validationGroupsList = new ArrayList<Class<?>>(classes.length);
336
337 for (String clazz : classes)
338 {
339 clazz = clazz.trim();
340 if (!clazz.isEmpty())
341 {
342 Class<?> theClass = null;
343 ClassLoader cl = null;
344 if (System.getSecurityManager() != null)
345 {
346 try
347 {
348 cl = AccessController.doPrivileged(new PrivilegedExceptionAction<ClassLoader>()
349 {
350 public ClassLoader run() throws PrivilegedActionException
351 {
352 return Thread.currentThread().getContextClassLoader();
353 }
354 });
355 }
356 catch (PrivilegedActionException pae)
357 {
358 throw new FacesException(pae);
359 }
360 }
361 else
362 {
363 cl = Thread.currentThread().getContextClassLoader();
364 }
365
366 try
367 {
368
369 theClass = Class.forName(clazz,false,cl);
370 }
371 catch (ClassNotFoundException ignore)
372 {
373 try
374 {
375
376 theClass = Class.forName(clazz,false, BeanValidator.class.getClassLoader());
377 }
378 catch (ClassNotFoundException e)
379 {
380 throw new RuntimeException("Could not load validation group", e);
381 }
382 }
383
384 validationGroupsList.add(theClass);
385 }
386 }
387
388 this.validationGroupsArray = validationGroupsList.toArray(new Class[validationGroupsList.size()]);
389 }
390 }
391
392
393
394
395
396 @JSFProperty
397 public String getValidationGroups(ValidateWholeBeanComponent component)
398 {
399 return component.getValidationGroups();
400 }
401
402
403
404
405
406
407 public void setValidationGroups(ValidateWholeBeanComponent component, final String validationGroups)
408 {
409 component.setValidationGroups(validationGroups);
410 }
411
412
413
414
415
416
417
418
419 private static class FacesMessageInterpolator implements MessageInterpolator
420 {
421 private final FacesContext facesContext;
422 private final MessageInterpolator interpolator;
423
424 public FacesMessageInterpolator(final MessageInterpolator interpolator, final FacesContext facesContext)
425 {
426 this.interpolator = interpolator;
427 this.facesContext = facesContext;
428 }
429
430 public String interpolate(final String s, final MessageInterpolator.Context context)
431 {
432 Locale locale = facesContext.getViewRoot().getLocale();
433 return interpolator.interpolate(s, context, locale);
434 }
435
436 public String interpolate(final String s, final MessageInterpolator.Context context, final Locale locale)
437 {
438 return interpolator.interpolate(s, context, locale);
439 }
440 }
441
442 private static class UpdateBeanCopyCallback implements VisitCallback
443 {
444 private WholeBeanValidator validator;
445 private Object wholeBeanBase;
446 private Object wholeBeanBaseCopy;
447 private Map<String, Object> candidateValuesMap;
448
449 public UpdateBeanCopyCallback(WholeBeanValidator validator, Object wholeBeanBase, Object wholeBeanBaseCopy,
450 Map<String, Object> candidateValuesMap)
451 {
452 this.validator = validator;
453 this.wholeBeanBase = wholeBeanBase;
454 this.wholeBeanBaseCopy = wholeBeanBaseCopy;
455 this.candidateValuesMap = candidateValuesMap;
456 }
457
458 @Override
459 public VisitResult visit(VisitContext context, UIComponent target)
460 {
461
462
463
464
465
466
467 ValueExpression valueExpression = target.getValueExpression("value");
468 if (valueExpression == null)
469 {
470 log.warning("cannot validate component with empty value: "
471 + target.getClientId(context.getFacesContext()));
472 return VisitResult.ACCEPT;
473 }
474
475
476 ValueReference reference = validator.getValueReference(
477 valueExpression, context.getFacesContext());
478 if (reference == null)
479 {
480 return VisitResult.ACCEPT;
481 }
482
483 Object base = reference.getBase();
484 if (base == null)
485 {
486 return VisitResult.ACCEPT;
487 }
488
489 Object referenceProperty = reference.getProperty();
490 if (!(referenceProperty instanceof String))
491 {
492
493
494
495 return VisitResult.ACCEPT;
496 }
497
498
499 if (base == this.wholeBeanBase || base.equals(this.wholeBeanBase))
500 {
501
502 ELContext elCtxDecorator = new _ELContextDecorator(context.getFacesContext().getELContext(),
503 new CopyBeanInterceptorELResolver(context.getFacesContext().getApplication().getELResolver(),
504 this.wholeBeanBase, this.wholeBeanBaseCopy));
505
506 valueExpression.setValue(elCtxDecorator, candidateValuesMap.get(
507 target.getClientId(context.getFacesContext())));
508 }
509
510 return VisitResult.ACCEPT;
511 }
512 }
513 }