Skip to content

Commit 23acfd4

Browse files
committed
HV-740 Removing beans.xml. Interceptor is now registered programmatically via BeforeBeanDiscovery.addAnnotatedType(). Also using @priority to set the interceptor priority
This way not the whole jar gets scanned for beans. Also started to use metadata to determine whether a method needs to be intercepted
1 parent 270e7dc commit 23acfd4

File tree

5 files changed

+65
-204
lines changed

5 files changed

+65
-204
lines changed

engine/src/main/java/org/hibernate/validator/internal/cdi/ValidationExtension.java

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.hibernate.validator.internal.cdi;
1818

1919
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.Constructor;
21+
import java.lang.reflect.Method;
2022
import java.util.Set;
2123
import javax.enterprise.context.ApplicationScoped;
2224
import javax.enterprise.event.Observes;
@@ -25,21 +27,24 @@
2527
import javax.enterprise.inject.spi.AnnotatedCallable;
2628
import javax.enterprise.inject.spi.AnnotatedConstructor;
2729
import javax.enterprise.inject.spi.AnnotatedMethod;
28-
import javax.enterprise.inject.spi.AnnotatedParameter;
2930
import javax.enterprise.inject.spi.AnnotatedType;
3031
import javax.enterprise.inject.spi.Bean;
3132
import javax.enterprise.inject.spi.BeanManager;
33+
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
3234
import javax.enterprise.inject.spi.Extension;
3335
import javax.enterprise.inject.spi.ProcessAnnotatedType;
3436
import javax.enterprise.inject.spi.ProcessBean;
3537
import javax.enterprise.util.AnnotationLiteral;
38+
import javax.validation.Validation;
3639
import javax.validation.Validator;
3740
import javax.validation.ValidatorFactory;
41+
import javax.validation.metadata.BeanDescriptor;
3842

3943
import org.hibernate.validator.cdi.HibernateValidator;
4044
import org.hibernate.validator.internal.cdi.interceptor.ValidationEnabledAnnotatedType;
41-
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
4245
import org.hibernate.validator.internal.util.Contracts;
46+
import org.hibernate.validator.internal.util.logging.Log;
47+
import org.hibernate.validator.internal.util.logging.LoggerFactory;
4348

4449
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
4550

@@ -52,9 +57,32 @@
5257
* @author Hardy Ferentschik
5358
*/
5459
public class ValidationExtension implements Extension {
55-
private final ConstraintHelper constraintHelper = new ConstraintHelper();
56-
private boolean validatorRegisteredUnderDefaultQualifier = false;
57-
private boolean validatorRegisteredUnderHibernateQualifier = false;
60+
private static final Log log = LoggerFactory.make();
61+
62+
private final Validator validator;
63+
private boolean validatorRegisteredUnderDefaultQualifier;
64+
private boolean validatorRegisteredUnderHibernateQualifier;
65+
66+
public ValidationExtension() {
67+
validator = Validation.buildDefaultValidatorFactory().getValidator();
68+
validatorRegisteredUnderDefaultQualifier = false;
69+
validatorRegisteredUnderHibernateQualifier = false;
70+
}
71+
72+
/**
73+
* Used to register the method validation interceptor binding annotation.
74+
*
75+
* @param beforeBeanDiscoveryEvent event fired before the bean discovery process starts
76+
* @param beanManager the bean manager.
77+
*/
78+
public void beforeBeanDiscovery(@Observes BeforeBeanDiscovery beforeBeanDiscoveryEvent, final BeanManager beanManager) {
79+
Contracts.assertNotNull( beforeBeanDiscoveryEvent, "The BeforeBeanDiscovery event cannot be null" );
80+
Contracts.assertNotNull( beanManager, "The BeanManager cannot be null" );
81+
82+
// Register the interceptor explicitly. This way, no beans.xml is needed
83+
AnnotatedType<ValidationExtension> annotatedType = beanManager.createAnnotatedType( ValidationExtension.class );
84+
beforeBeanDiscoveryEvent.addAnnotatedType( annotatedType );
85+
}
5886

5987
/**
6088
* Registers the Hibernate specific {@code ValidatorFactory} and {@code Validator}. The qualifiers used for registration
@@ -110,7 +138,18 @@ public void processBean(@Observes ProcessBean<?> processBeanEvent) {
110138
public <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> processAnnotatedTypeEvent) {
111139
Contracts.assertNotNull( processAnnotatedTypeEvent, "The ProcessAnnotatedType event cannot be null" );
112140
final AnnotatedType<T> type = processAnnotatedTypeEvent.getAnnotatedType();
113-
Set<AnnotatedCallable<? super T>> constrainedCallables = determineConstrainedCallables( type );
141+
142+
// TODO we need to take @@ValidateExecutable into account as well
143+
BeanDescriptor beanDescriptor = validator.getConstraintsForClass( type.getJavaClass() );
144+
if ( !beanDescriptor.hasConstrainedExecutables() ) {
145+
return;
146+
}
147+
148+
if ( log.isDebugEnabled() ) {
149+
log.debug( type.getJavaClass() + " contains executable constraints" );
150+
}
151+
152+
Set<AnnotatedCallable<? super T>> constrainedCallables = determineConstrainedCallables( type, beanDescriptor );
114153
if ( !constrainedCallables.isEmpty() ) {
115154
ValidationEnabledAnnotatedType<T> wrappedType = new ValidationEnabledAnnotatedType<T>(
116155
type,
@@ -120,47 +159,26 @@ public <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> processAn
120159
}
121160
}
122161

123-
private <T> Set<AnnotatedCallable<? super T>> determineConstrainedCallables(AnnotatedType<T> type) {
162+
private <T> Set<AnnotatedCallable<? super T>> determineConstrainedCallables(AnnotatedType<T> type, BeanDescriptor beanDescriptor) {
124163
Set<AnnotatedCallable<? super T>> callables = newHashSet();
125164

126-
for ( AnnotatedConstructor<T> constructor : type.getConstructors() ) {
127-
if ( isCallableConstrained( constructor ) ) {
128-
callables.add( constructor );
165+
for ( AnnotatedConstructor<T> annotatedConstructor : type.getConstructors() ) {
166+
Constructor constructor = annotatedConstructor.getJavaMember();
167+
if ( beanDescriptor.getConstraintsForConstructor( constructor.getParameterTypes() ) != null ) {
168+
callables.add( annotatedConstructor );
129169
}
130170
}
131171

132-
for ( AnnotatedMethod<? super T> method : type.getMethods() ) {
133-
if ( isCallableConstrained( method ) ) {
134-
callables.add( method );
172+
for ( AnnotatedMethod<? super T> annotatedMethod : type.getMethods() ) {
173+
Method method = annotatedMethod.getJavaMember();
174+
if ( beanDescriptor.getConstraintsForMethod( method.getName(), method.getParameterTypes() ) != null ) {
175+
callables.add( annotatedMethod );
135176
}
136177
}
137178

138179
return callables;
139180
}
140181

141-
private <T> boolean isCallableConstrained(AnnotatedCallable<? super T> callable) {
142-
if ( containsConstraintAnnotation( callable.getAnnotations() ) ) {
143-
return true;
144-
}
145-
146-
for ( AnnotatedParameter<? super T> parameter : callable.getParameters() ) {
147-
if ( containsConstraintAnnotation( parameter.getAnnotations() ) ) {
148-
return true;
149-
}
150-
}
151-
152-
return false;
153-
}
154-
155-
private boolean containsConstraintAnnotation(Set<Annotation> annotations) {
156-
for ( Annotation annotation : annotations ) {
157-
if ( constraintHelper.isConstraintAnnotation( annotation.annotationType() ) ) {
158-
return true;
159-
}
160-
}
161-
return false;
162-
}
163-
164182
/**
165183
* The set of qualifier this extension should bind its {@code ValidatorFactory} and {@code Validator}
166184
* under. This set is based on the observed registered beans via the {@code ProcessBean} event.

engine/src/main/java/org/hibernate/validator/internal/cdi/interceptor/ValidationInterceptor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Arrays;
2222
import java.util.Iterator;
2323
import java.util.Set;
24+
import javax.annotation.Priority;
2425
import javax.inject.Inject;
2526
import javax.interceptor.AroundInvoke;
2627
import javax.interceptor.Interceptor;
@@ -41,6 +42,7 @@
4142
*/
4243
@MethodValidated
4344
@Interceptor
45+
@Priority( 3090 )
4446
public class ValidationInterceptor implements Serializable {
4547

4648
private static final long serialVersionUID = 604440259030722151L;
@@ -99,6 +101,12 @@ public Object validateMethodInvocation(InvocationContext ctx) throws Exception {
99101
return result;
100102
}
101103

104+
// @AroundConstruct
105+
// public Object validateConstructorInvocation(InvocationContext ctx) throws Exception {
106+
// // TODO implement
107+
// return null;
108+
// }
109+
102110
private String getMessage(Method method, Object[] args, Set<? extends ConstraintViolation<?>> violations) {
103111

104112
StringBuilder message = new StringBuilder();

engine/src/main/resources/META-INF/beans.xml

Lines changed: 0 additions & 28 deletions
This file was deleted.

engine/src/test/java/org/hibernate/validator/test/internal/cdi/ValidationExtensionTest.java

Lines changed: 1 addition & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -18,61 +18,39 @@
1818

1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.Type;
21-
import java.util.ArrayList;
22-
import java.util.Collections;
2321
import java.util.HashSet;
2422
import java.util.Set;
2523
import javax.enterprise.inject.Default;
2624
import javax.enterprise.inject.spi.AfterBeanDiscovery;
27-
import javax.enterprise.inject.spi.AnnotatedCallable;
28-
import javax.enterprise.inject.spi.AnnotatedConstructor;
29-
import javax.enterprise.inject.spi.AnnotatedMethod;
30-
import javax.enterprise.inject.spi.AnnotatedParameter;
31-
import javax.enterprise.inject.spi.AnnotatedType;
3225
import javax.enterprise.inject.spi.Bean;
3326
import javax.enterprise.inject.spi.BeanManager;
34-
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
35-
import javax.enterprise.inject.spi.ProcessAnnotatedType;
3627
import javax.enterprise.inject.spi.ProcessBean;
3728
import javax.enterprise.util.AnnotationLiteral;
3829
import javax.validation.Validator;
3930
import javax.validation.ValidatorFactory;
40-
import javax.validation.constraints.NotNull;
4131

42-
import org.easymock.Capture;
4332
import org.testng.annotations.BeforeMethod;
4433
import org.testng.annotations.Test;
4534

4635
import org.hibernate.validator.cdi.HibernateValidator;
4736
import org.hibernate.validator.internal.cdi.ValidationExtension;
4837
import org.hibernate.validator.internal.cdi.ValidatorBean;
4938
import org.hibernate.validator.internal.cdi.ValidatorFactoryBean;
50-
import org.hibernate.validator.internal.cdi.interceptor.MethodValidated;
51-
import org.hibernate.validator.internal.util.annotationfactory.AnnotationDescriptor;
52-
import org.hibernate.validator.internal.util.annotationfactory.AnnotationFactory;
5339

54-
import static org.easymock.EasyMock.capture;
5540
import static org.easymock.EasyMock.createMock;
5641
import static org.easymock.EasyMock.expect;
5742
import static org.easymock.EasyMock.isA;
5843
import static org.easymock.EasyMock.replay;
5944
import static org.easymock.EasyMock.verify;
60-
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;
61-
import static org.testng.Assert.assertTrue;
6245

6346
/**
6447
* @author Hardy Ferentschik
6548
*/
6649
@Test(singleThreaded = true) // needs to run single threaded, because the mocks are shared across test methods
67-
public class ValidationExtensionTest<T> {
50+
public class ValidationExtensionTest {
6851
private ValidationExtension extension;
6952
private AfterBeanDiscovery afterBeanDiscoveryMock;
7053
private ProcessBean processBeanMock;
71-
private BeforeBeanDiscovery beforeBeanDiscoveryMock;
72-
private ProcessAnnotatedType<T> processAnnotatedTypeMock;
73-
private AnnotatedType<T> annotatedTypeMock;
74-
private AnnotatedMethod<T> annotatedMethodMock;
75-
private AnnotatedConstructor<T> annotatedConstructorMock;
7654
private Bean<ValidatorFactory> validatorFactoryBeanMock;
7755
private Bean<Validator> validatorBeanMock;
7856
private BeanManager beanManagerMock;
@@ -83,11 +61,6 @@ public void setUp() {
8361
extension = new ValidationExtension();
8462
afterBeanDiscoveryMock = createMock( AfterBeanDiscovery.class );
8563
processBeanMock = createMock( ProcessBean.class );
86-
beforeBeanDiscoveryMock = createMock( BeforeBeanDiscovery.class );
87-
processAnnotatedTypeMock = createMock( ProcessAnnotatedType.class );
88-
annotatedTypeMock = createMock( AnnotatedType.class );
89-
annotatedMethodMock = createMock( AnnotatedMethod.class );
90-
annotatedConstructorMock = createMock( AnnotatedConstructor.class );
9164
beanManagerMock = createMock( BeanManager.class );
9265
validatorFactoryBeanMock = createMock( Bean.class );
9366
validatorBeanMock = createMock( Bean.class );
@@ -194,113 +167,6 @@ public void testNoRegistrationRequired() {
194167
public void testProcessAnnotatedTypeNullParameter() {
195168
extension.processAnnotatedType( null );
196169
}
197-
198-
@Test
199-
public void testConstrainedMethodGetsInterceptorBidingAdded() {
200-
AnnotationDescriptor<NotNull> descriptor = new AnnotationDescriptor<NotNull>( NotNull.class );
201-
Annotation notNull = AnnotationFactory.create( descriptor );
202-
setupMocks( annotatedMethodMock, notNull );
203-
204-
Capture<AnnotatedType<T>> capturedType = new Capture<AnnotatedType<T>>();
205-
processAnnotatedTypeMock.setAnnotatedType( capture( capturedType ) );
206-
207-
// get the mocks ready
208-
replay( processAnnotatedTypeMock, annotatedTypeMock, annotatedMethodMock, annotatedConstructorMock );
209-
210-
// run the code
211-
extension.processAnnotatedType( processAnnotatedTypeMock );
212-
213-
// verify the mocks
214-
verify( processAnnotatedTypeMock, annotatedTypeMock, annotatedMethodMock, annotatedConstructorMock );
215-
216-
// check the captured type has @MethodValidated added
217-
Set<AnnotatedMethod<? super T>> methods = capturedType.getValue().getMethods();
218-
assertTrue( methods.size() == 1, "We still should only have a single method" );
219-
AnnotatedMethod<?> method = methods.iterator().next();
220-
assertTrue(
221-
method.isAnnotationPresent( MethodValidated.class ),
222-
"The @MethodValidated annotation method should have been added"
223-
);
224-
}
225-
226-
@Test
227-
public void testConstrainedConstructorGetsInterceptorBidingAdded() {
228-
AnnotationDescriptor<NotNull> descriptor = new AnnotationDescriptor<NotNull>( NotNull.class );
229-
Annotation notNull = AnnotationFactory.create( descriptor );
230-
setupMocks( annotatedConstructorMock, notNull );
231-
232-
Capture<AnnotatedType<T>> capturedType = new Capture<AnnotatedType<T>>();
233-
processAnnotatedTypeMock.setAnnotatedType( capture( capturedType ) );
234-
235-
// get the mocks ready
236-
replay( processAnnotatedTypeMock, annotatedTypeMock, annotatedMethodMock, annotatedConstructorMock );
237-
238-
// run the code
239-
extension.processAnnotatedType( processAnnotatedTypeMock );
240-
241-
// verify the mocks
242-
verify( processAnnotatedTypeMock, annotatedTypeMock, annotatedMethodMock, annotatedConstructorMock );
243-
244-
// check the captured type has @MethodValidated added
245-
Set<AnnotatedConstructor<T>> constructors = capturedType.getValue().getConstructors();
246-
assertTrue( constructors.size() == 1, "We still should only have a single constructor" );
247-
AnnotatedConstructor<?> constructor = constructors.iterator().next();
248-
assertTrue(
249-
constructor.isAnnotationPresent( MethodValidated.class ),
250-
"The @MethodValidated annotation method should have been added"
251-
);
252-
}
253-
254-
@Test
255-
public void testUnConstrainedMethodDoesNotGetInterceptorBidingAdded() {
256-
setupMocks( annotatedMethodMock );
257-
258-
// get the mocks ready
259-
replay( processAnnotatedTypeMock, annotatedTypeMock, annotatedMethodMock );
260-
261-
// run the code
262-
extension.processAnnotatedType( processAnnotatedTypeMock );
263-
264-
// verify the mocks
265-
verify( processAnnotatedTypeMock, annotatedTypeMock, annotatedMethodMock );
266-
}
267-
268-
private void setupMocks(AnnotatedCallable<T> callable, Annotation... constraintAnnotations) {
269-
expect( processAnnotatedTypeMock.getAnnotatedType() ).andReturn( annotatedTypeMock );
270-
271-
Set<AnnotatedConstructor<T>> constructors = newHashSet();
272-
Set<Annotation> constructorAnnotations = newHashSet();
273-
if ( callable instanceof AnnotatedConstructor ) {
274-
constructors.add( (AnnotatedConstructor<T>) callable );
275-
Collections.addAll( constructorAnnotations, constraintAnnotations );
276-
}
277-
expect( annotatedTypeMock.getConstructors() ).andReturn( constructors );
278-
279-
Set<AnnotatedMethod<? super T>> methods = newHashSet();
280-
Set<Annotation> methodAnnotations = newHashSet();
281-
if ( callable instanceof AnnotatedMethod ) {
282-
methods.add( (AnnotatedMethod<T>) callable );
283-
Collections.addAll( methodAnnotations, constraintAnnotations );
284-
}
285-
expect( annotatedTypeMock.getMethods() ).andReturn( methods );
286-
287-
if ( callable instanceof AnnotatedConstructor ) {
288-
expect( annotatedConstructorMock.getAnnotations() ).andReturn( constructorAnnotations );
289-
}
290-
else {
291-
expect( annotatedMethodMock.getAnnotations() ).andReturn( methodAnnotations );
292-
}
293-
294-
if ( constraintAnnotations.length == 0 ) {
295-
// if there is no constraint annotation on the method the parameters get checked
296-
expect( annotatedMethodMock.getParameters() ).andReturn( new ArrayList<AnnotatedParameter<T>>() );
297-
}
298-
else {
299-
// if we have found a constraint annotation we expect another call to getConstructors and getMethods when the wrapped type gets build
300-
expect( annotatedTypeMock.getConstructors() ).andReturn( constructors );
301-
expect( annotatedTypeMock.getMethods() ).andReturn( methods );
302-
}
303-
}
304170
}
305171

306172

0 commit comments

Comments
 (0)