Coverage Report - org.apache.onami.validation.ValidateMethodInterceptor
 
Classes in this File Line Coverage Branch Coverage Complexity
ValidateMethodInterceptor
78%
36/46
66%
12/18
5.667
 
 1  
 package org.apache.onami.validation;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 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  
  * Method interceptor for {@link Validate} annotation.
 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  
      * The {@link ValidatorFactory} reference.
 54  
      */
 55  
     @Inject
 56  
     private ValidatorFactory validatorFactory;
 57  
 
 58  
     /**
 59  
      * {@inheritDoc}
 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  
      * Define the {@link Throwable} has to be thrown when a validation error occurs and the user defined the custom
 111  
      * error wrapper.
 112  
      *
 113  
      * @param exception the occurred validation error.
 114  
      * @param exceptionWrapperClass the user defined custom error wrapper.
 115  
      * @return the {@link Throwable} has o be thrown.
 116  
      */
 117  
     private static Throwable getException( ConstraintViolationException exception,
 118  
                                            Class<? extends Throwable> exceptionWrapperClass,
 119  
                                            String exceptionMessage,
 120  
                                            Object[] arguments )
 121  
     {
 122  
         // check the thrown exception is of same re-throw type
 123  2
         if ( exceptionWrapperClass == ConstraintViolationException.class )
 124  
         {
 125  1
             return exception;
 126  
         }
 127  
 
 128  
         // re-throw the exception as new exception
 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  
 }