1 //$Id: ValidateEventListener.java 15133 2008-08-20 10:05:57Z hardy.ferentschik $
2 package org.hibernate.validator.event;
3
4 import java.io.Serializable;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Map;
11
12 import org.hibernate.AssertionFailure;
13 import org.hibernate.EntityMode;
14 import org.hibernate.HibernateException;
15 import org.hibernate.cfg.Configuration;
16 import org.hibernate.event.Initializable;
17 import org.hibernate.event.PreInsertEvent;
18 import org.hibernate.event.PreInsertEventListener;
19 import org.hibernate.event.PreUpdateEvent;
20 import org.hibernate.event.PreUpdateEventListener;
21 import org.hibernate.mapping.Component;
22 import org.hibernate.mapping.PersistentClass;
23 import org.hibernate.mapping.Property;
24 import org.hibernate.property.Getter;
25 import org.hibernate.property.PropertyAccessor;
26 import org.hibernate.property.PropertyAccessorFactory;
27 import org.hibernate.annotations.common.reflection.ReflectionManager;
28 import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
29 import org.hibernate.util.ReflectHelper;
30 import org.hibernate.util.StringHelper;
31 import org.hibernate.validator.ClassValidator;
32 import org.hibernate.validator.Environment;
33 import org.hibernate.validator.InvalidStateException;
34 import org.hibernate.validator.InvalidValue;
35 import org.hibernate.validator.MessageInterpolator;
36
37 /**
38 * Before insert and update, executes the validator framework
39 *
40 * @author Emmanuel Bernard
41 */
42 public class ValidateEventListener implements PreInsertEventListener, PreUpdateEventListener, Initializable {
43 private boolean isInitialized;
44 private Map<Class, ValidatableElement> validators = new HashMap<Class, ValidatableElement>();
45
46 /**
47 * initialize the validators, any non significant validators are not kept
48 */
49 @SuppressWarnings( "unchecked" )
50 public synchronized void initialize(final Configuration cfg) {
51 if ( !isInitialized ) {
52 String interpolatorString = cfg.getProperty( Environment.MESSAGE_INTERPOLATOR_CLASS );
53 MessageInterpolator interpolator = null;
54 if ( StringHelper.isNotEmpty( interpolatorString ) ) {
55 try {
56 Class interpolatorClass = ReflectHelper.classForName( interpolatorString );
57 interpolator = (MessageInterpolator) interpolatorClass.newInstance();
58 }
59 catch (ClassNotFoundException e) {
60 throw new HibernateException( "Unable to find message interpolator: " + interpolatorString, e );
61 }
62 catch (IllegalAccessException e) {
63 throw new HibernateException( "Unable to instanciate message interpolator: " + interpolatorString,
64 e );
65 }
66 catch (InstantiationException e) {
67 throw new HibernateException( "Unable to instanciate message interpolator: " + interpolatorString,
68 e );
69 }
70 catch (ClassCastException e) {
71 throw new HibernateException( "Class does not implement "
72 + MessageInterpolator.class.getName() + ": " + interpolatorString, e );
73 }
74 }
75 Iterator<PersistentClass> classes = (Iterator<PersistentClass>) cfg.getClassMappings();
76 ReflectionManager reflectionManager;
77 try {
78 //TODO introduce q ReflectionManagerHolder interface to avoid reflection
79 //I want to avoid hard link between HAN and Validator for usch a simple need
80 //reuse the existing reflectionManager one when possible
81 reflectionManager =
82 (ReflectionManager) cfg.getClass().getMethod( "getReflectionManager" ).invoke( cfg );
83
84 }
85 catch (Exception e) {
86 reflectionManager = new JavaReflectionManager();
87 }
88 while ( classes.hasNext() ) {
89 PersistentClass clazz = classes.next();
90 final Class mappedClass = clazz.getMappedClass();
91 ClassValidator validator =
92 new ClassValidator( mappedClass, null, interpolator, null, reflectionManager );
93 ValidatableElement element = new ValidatableElement( mappedClass, validator );
94 addSubElement( clazz.getIdentifierProperty(), element );
95 Iterator properties = clazz.getPropertyIterator();
96 while ( properties.hasNext() ) {
97 addSubElement( (Property) properties.next(), element );
98 }
99 if ( element.subElements.size() != 0 || element.validator.hasValidationRules() ) {
100 validators.put( mappedClass, element );
101 }
102 }
103 isInitialized = true;
104 }
105 }
106
107 @SuppressWarnings( "unchecked" )
108 private void addSubElement(Property property, ValidatableElement element) {
109 if ( property != null && property.isComposite() && !property.isBackRef() ) {
110 Component component = (Component) property.getValue();
111 if ( component.isEmbedded() ) return;
112 PropertyAccessor accessor = PropertyAccessorFactory.getPropertyAccessor( property, EntityMode.POJO );
113 Getter getter = accessor.getGetter( element.clazz, property.getName() );
114 ClassValidator validator = new ClassValidator( getter.getReturnType() );
115 ValidatableElement subElement = new ValidatableElement( getter.getReturnType(), validator, getter );
116 Iterator properties = component.getPropertyIterator();
117 while ( properties.hasNext() ) {
118 addSubElement( (Property) properties.next(), subElement );
119 }
120 if ( subElement.getSubElements().size() != 0 || subElement.validator.hasValidationRules() ) {
121 element.addSubElement( subElement );
122 }
123 }
124 }
125
126 @SuppressWarnings( "unchecked" )
127 protected void validate(Object entity, EntityMode mode) {
128 if ( entity == null || !EntityMode.POJO.equals( mode ) ) return;
129 ValidatableElement element;
130 if ( isInitialized ) {
131 element = validators.get( entity.getClass() );
132 }
133 else {
134 throw new AssertionFailure( "Validator event not initialized" );
135 }
136 if ( element == null ) return; //no validation to do
137 List<InvalidValue> consolidatedInvalidValues = new ArrayList<InvalidValue>();
138 validateSubElements( element, entity, consolidatedInvalidValues );
139 InvalidValue[] invalidValues = element.validator == null ?
140 null :
141 element.validator.getInvalidValues( entity );
142 if ( invalidValues != null ) {
143 for ( InvalidValue invalidValue : invalidValues ) {
144 consolidatedInvalidValues.add( invalidValue );
145 }
146 }
147 if ( consolidatedInvalidValues.size() > 0 ) {
148 throw new InvalidStateException(
149 consolidatedInvalidValues.toArray( new InvalidValue[consolidatedInvalidValues.size()] ),
150 entity.getClass().getName()
151 );
152 }
153 }
154
155 @SuppressWarnings( "unchecked" )
156 private void validateSubElements(
157 ValidatableElement element, Object entity, List<InvalidValue> consolidatedInvalidValues
158 ) {
159 if ( element != null ) {
160 for ( ValidatableElement subElement : element.subElements ) {
161 Object component = subElement.getter.get( entity );
162 InvalidValue[] invalidValues = subElement.validator.getInvalidValues( component );
163 for ( InvalidValue invalidValue : invalidValues ) {
164 consolidatedInvalidValues.add( invalidValue );
165 }
166 validateSubElements( subElement, component, consolidatedInvalidValues );
167 }
168 }
169 }
170
171 public boolean onPreInsert(PreInsertEvent event) {
172 validate( event.getEntity(), event.getSource().getEntityMode() );
173 return false;
174 }
175
176 public boolean onPreUpdate(PreUpdateEvent event) {
177 validate( event.getEntity(), event.getSource().getEntityMode() );
178 return false;
179 }
180
181 private static class ValidatableElement implements Serializable {
182 private Class clazz;
183 private ClassValidator validator;
184 private Getter getter;
185 private Collection<ValidatableElement> subElements = new ArrayList<ValidatableElement>();
186
187 public ValidatableElement(Class clazz, ClassValidator validator) {
188 this.clazz = clazz;
189 this.validator = validator;
190 }
191
192 public ValidatableElement(Class clazz, ClassValidator validator, Getter getter) {
193 this( clazz, validator );
194 this.getter = getter;
195 }
196
197 public void addSubElement(ValidatableElement subValidatableElement) {
198 subElements.add( subValidatableElement );
199 }
200
201 public Collection<ValidatableElement> getSubElements() {
202 return this.subElements;
203 }
204 }
205 }