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 final class ValidateMethodInterceptor
45 implements MethodInterceptor
46 {
47
48 private static final Class<?>[] CAUSE_TYPES = new Class[] { Throwable.class };
49
50 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 Validate validate = invocation.getMethod().getAnnotation( Validate.class );
65
66 Validator validator = validatorFactory.getValidator();
67 MethodValidator methodValidator = validator.unwrap( MethodValidator.class );
68
69 Set<ConstraintViolation<?>> constraintViolations = new HashSet<ConstraintViolation<?>>();
70 Class<?> clazz = invocation.getMethod().getDeclaringClass();
71 Method method = invocation.getMethod();
72 Object[] arguments = invocation.getArguments();
73 Class<?>[] groups = validate.groups();
74
75 constraintViolations.addAll( methodValidator.validateParameters( clazz, method, arguments, groups ) );
76
77 if ( !constraintViolations.isEmpty() )
78 {
79 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 Object returnedValue = invocation.proceed();
89
90 if ( validate.validateReturnedValue() )
91 {
92 constraintViolations.addAll( methodValidator.validateReturnedValue( clazz, method, returnedValue, groups ) );
93
94 if ( !constraintViolations.isEmpty() )
95 {
96 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 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 if ( exceptionWrapperClass == ConstraintViolationException.class )
124 {
125 return exception;
126 }
127
128
129 Throwable rethrowEx = null;
130
131 String errorMessage;
132 Object[] initargs;
133 Class<?>[] initargsType;
134
135 if ( exceptionMessage.length() != 0 )
136 {
137 errorMessage = format( exceptionMessage, arguments );
138 initargs = new Object[] { errorMessage, exception };
139 initargsType = MESSAGE_CAUSE_TYPES;
140 }
141 else
142 {
143 initargs = new Object[] { exception };
144 initargsType = CAUSE_TYPES;
145 }
146
147 Constructor<? extends Throwable> exceptionConstructor = getMatchingConstructor( exceptionWrapperClass,
148 initargsType );
149 if ( exceptionConstructor != null )
150 {
151 try
152 {
153 rethrowEx = exceptionConstructor.newInstance( initargs );
154 }
155 catch ( Exception e )
156 {
157 errorMessage = format( "Impossible to re-throw '%s', it needs the constructor with %s argument(s).",
158 exceptionWrapperClass.getName(),
159 Arrays.toString( initargsType ) );
160 rethrowEx = new RuntimeException( errorMessage, e );
161 }
162 }
163 else
164 {
165 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 rethrowEx = new RuntimeException( errorMessage );
169 }
170
171 return rethrowEx;
172 }
173
174 @SuppressWarnings( "unchecked" )
175 private static <E extends Throwable> Constructor<E> getMatchingConstructor( Class<E> type, Class<?>[] argumentsType )
176 {
177 Class<? super E> currentType = type;
178 while ( Object.class != currentType )
179 {
180 for ( Constructor<?> constructor : currentType.getConstructors() )
181 {
182 if ( Arrays.equals( argumentsType, constructor.getParameterTypes() ) )
183 {
184 return (Constructor<E>) constructor;
185 }
186 }
187 currentType = currentType.getSuperclass();
188 }
189 return null;
190 }
191
192 }