1 | |
package org.apache.onami.validation; |
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
import static java.lang.String.format; |
23 | |
import static java.util.Arrays.deepToString; |
24 | |
|
25 | |
import java.lang.reflect.Constructor; |
26 | |
import java.lang.reflect.Method; |
27 | |
import java.util.Arrays; |
28 | |
import java.util.HashSet; |
29 | |
import java.util.Set; |
30 | |
|
31 | |
import javax.inject.Inject; |
32 | |
import javax.validation.ConstraintViolation; |
33 | |
import javax.validation.ConstraintViolationException; |
34 | |
import javax.validation.Validator; |
35 | |
import javax.validation.ValidatorFactory; |
36 | |
|
37 | |
import org.aopalliance.intercept.MethodInterceptor; |
38 | |
import org.aopalliance.intercept.MethodInvocation; |
39 | |
import org.apache.bval.jsr303.extensions.MethodValidator; |
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | 1 | final class ValidateMethodInterceptor |
45 | |
implements MethodInterceptor |
46 | |
{ |
47 | |
|
48 | 1 | private static final Class<?>[] CAUSE_TYPES = new Class[] { Throwable.class }; |
49 | |
|
50 | 1 | private static final Class<?>[] MESSAGE_CAUSE_TYPES = new Class[] { String.class, Throwable.class }; |
51 | |
|
52 | |
|
53 | |
|
54 | |
|
55 | |
@Inject |
56 | |
private ValidatorFactory validatorFactory; |
57 | |
|
58 | |
|
59 | |
|
60 | |
|
61 | |
public Object invoke( MethodInvocation invocation ) |
62 | |
throws Throwable |
63 | |
{ |
64 | 3 | Validate validate = invocation.getMethod().getAnnotation( Validate.class ); |
65 | |
|
66 | 3 | Validator validator = validatorFactory.getValidator(); |
67 | 3 | MethodValidator methodValidator = validator.unwrap( MethodValidator.class ); |
68 | |
|
69 | 3 | Set<ConstraintViolation<?>> constraintViolations = new HashSet<ConstraintViolation<?>>(); |
70 | 3 | Class<?> clazz = invocation.getMethod().getDeclaringClass(); |
71 | 3 | Method method = invocation.getMethod(); |
72 | 3 | Object[] arguments = invocation.getArguments(); |
73 | 3 | Class<?>[] groups = validate.groups(); |
74 | |
|
75 | 3 | constraintViolations.addAll( methodValidator.validateParameters( clazz, method, arguments, groups ) ); |
76 | |
|
77 | 3 | if ( !constraintViolations.isEmpty() ) |
78 | |
{ |
79 | 2 | throw getException( new ConstraintViolationException( format( "Validation error when calling method '%s' with arguments %s", |
80 | |
method, |
81 | |
deepToString( arguments ) ), |
82 | |
constraintViolations ), |
83 | |
validate.rethrowExceptionsAs(), |
84 | |
validate.exceptionMessage(), |
85 | |
arguments ); |
86 | |
} |
87 | |
|
88 | 1 | Object returnedValue = invocation.proceed(); |
89 | |
|
90 | 1 | if ( validate.validateReturnedValue() ) |
91 | |
{ |
92 | 1 | constraintViolations.addAll( methodValidator.validateReturnedValue( clazz, method, returnedValue, groups ) ); |
93 | |
|
94 | 1 | if ( !constraintViolations.isEmpty() ) |
95 | |
{ |
96 | 0 | throw getException( new ConstraintViolationException( format( "Method '%s' returned a not valid value %s", |
97 | |
method, |
98 | |
returnedValue ), |
99 | |
constraintViolations ), |
100 | |
validate.rethrowExceptionsAs(), |
101 | |
validate.exceptionMessage(), |
102 | |
arguments ); |
103 | |
} |
104 | |
} |
105 | |
|
106 | 1 | return returnedValue; |
107 | |
} |
108 | |
|
109 | |
|
110 | |
|
111 | |
|
112 | |
|
113 | |
|
114 | |
|
115 | |
|
116 | |
|
117 | |
private static Throwable getException( ConstraintViolationException exception, |
118 | |
Class<? extends Throwable> exceptionWrapperClass, |
119 | |
String exceptionMessage, |
120 | |
Object[] arguments ) |
121 | |
{ |
122 | |
|
123 | 2 | if ( exceptionWrapperClass == ConstraintViolationException.class ) |
124 | |
{ |
125 | 1 | return exception; |
126 | |
} |
127 | |
|
128 | |
|
129 | 1 | Throwable rethrowEx = null; |
130 | |
|
131 | |
String errorMessage; |
132 | |
Object[] initargs; |
133 | |
Class<?>[] initargsType; |
134 | |
|
135 | 1 | if ( exceptionMessage.length() != 0 ) |
136 | |
{ |
137 | 1 | errorMessage = format( exceptionMessage, arguments ); |
138 | 1 | initargs = new Object[] { errorMessage, exception }; |
139 | 1 | initargsType = MESSAGE_CAUSE_TYPES; |
140 | |
} |
141 | |
else |
142 | |
{ |
143 | 0 | initargs = new Object[] { exception }; |
144 | 0 | initargsType = CAUSE_TYPES; |
145 | |
} |
146 | |
|
147 | 1 | Constructor<? extends Throwable> exceptionConstructor = getMatchingConstructor( exceptionWrapperClass, |
148 | |
initargsType ); |
149 | 1 | if ( exceptionConstructor != null ) |
150 | |
{ |
151 | |
try |
152 | |
{ |
153 | 1 | rethrowEx = exceptionConstructor.newInstance( initargs ); |
154 | |
} |
155 | 0 | catch ( Exception e ) |
156 | |
{ |
157 | 0 | errorMessage = format( "Impossible to re-throw '%s', it needs the constructor with %s argument(s).", |
158 | |
exceptionWrapperClass.getName(), |
159 | |
Arrays.toString( initargsType ) ); |
160 | 0 | rethrowEx = new RuntimeException( errorMessage, e ); |
161 | 1 | } |
162 | |
} |
163 | |
else |
164 | |
{ |
165 | 0 | errorMessage = format( "Impossible to re-throw '%s', it needs the constructor with %s or %s argument(s).", |
166 | |
exceptionWrapperClass.getName(), Arrays.toString( CAUSE_TYPES ), |
167 | |
Arrays.toString( MESSAGE_CAUSE_TYPES ) ); |
168 | 0 | rethrowEx = new RuntimeException( errorMessage ); |
169 | |
} |
170 | |
|
171 | 1 | return rethrowEx; |
172 | |
} |
173 | |
|
174 | |
@SuppressWarnings( "unchecked" ) |
175 | |
private static <E extends Throwable> Constructor<E> getMatchingConstructor( Class<E> type, Class<?>[] argumentsType ) |
176 | |
{ |
177 | 1 | Class<? super E> currentType = type; |
178 | 1 | while ( Object.class != currentType ) |
179 | |
{ |
180 | 2 | for ( Constructor<?> constructor : currentType.getConstructors() ) |
181 | |
{ |
182 | 2 | if ( Arrays.equals( argumentsType, constructor.getParameterTypes() ) ) |
183 | |
{ |
184 | 1 | return (Constructor<E>) constructor; |
185 | |
} |
186 | |
} |
187 | 0 | currentType = currentType.getSuperclass(); |
188 | |
} |
189 | 0 | return null; |
190 | |
} |
191 | |
|
192 | |
} |