Search in sources :

Example 1 with RoutingException

use of ninja.RoutingException in project ninja by ninjaframework.

the class ControllerMethodInvoker method build.

/**
     * Builds an invoker for a functional method.  Understands what parameters
     * to inject and extract based on type and annotations.
     * @param functionalMethod The method to be invoked
     * @param implementationMethod The method to use for determining what
     *      actual parameters and annotations to use for each argument.  Useful
     *      when type/lambda erasure makes the functional interface not reliable
     *      for reflecting.
     * @param injector The guice injector
     * @param ninjaProperties The NinjaProperties of this application
     * @return An invoker
     */
public static ControllerMethodInvoker build(Method functionalMethod, Method implementationMethod, Injector injector, NinjaProperties ninjaProperties) {
    // get both the parameters...
    final Type[] genericParameterTypes = implementationMethod.getGenericParameterTypes();
    final MethodParameter[] methodParameters = MethodParameter.convertIntoMethodParameters(genericParameterTypes);
    // ... and all annotations for the parameters
    final Annotation[][] paramAnnotations = implementationMethod.getParameterAnnotations();
    ArgumentExtractor<?>[] argumentExtractors = new ArgumentExtractor<?>[methodParameters.length];
    // now we skip through the parameters and process the annotations
    for (int i = 0; i < methodParameters.length; i++) {
        try {
            argumentExtractors[i] = getArgumentExtractor(methodParameters[i], paramAnnotations[i], injector);
        } catch (RoutingException e) {
            throw new RoutingException("Error building argument extractor for parameter " + i + " in method " + implementationMethod.getDeclaringClass().getName() + "." + implementationMethod.getName() + "()", e);
        }
    }
    // Replace a null extractor with a bodyAs extractor, but make sure there's only one
    int bodyAsFound = -1;
    for (int i = 0; i < argumentExtractors.length; i++) {
        if (argumentExtractors[i] == null) {
            if (bodyAsFound > -1) {
                throw new RoutingException("Only one parameter may be deserialised as the body " + implementationMethod.getDeclaringClass().getName() + "." + implementationMethod.getName() + "()\n" + "Extracted parameter is type: " + methodParameters[bodyAsFound].parameterClass.getName() + "\n" + "Extra parmeter is type: " + methodParameters[i].parameterClass.getName());
            } else {
                argumentExtractors[i] = new ArgumentExtractors.BodyAsExtractor(methodParameters[i].parameterClass);
                bodyAsFound = i;
            }
        }
    }
    // parameters
    for (int i = 0; i < argumentExtractors.length; i++) {
        argumentExtractors[i] = validateArgumentWithExtractor(methodParameters[i], paramAnnotations[i], injector, argumentExtractors[i]);
    }
    boolean useStrictArgumentExtractors = determineWhetherToUseStrictArgumentExtractorMode(ninjaProperties);
    return new ControllerMethodInvoker(functionalMethod, argumentExtractors, useStrictArgumentExtractors);
}
Also used : RoutingException(ninja.RoutingException) ParameterizedType(java.lang.reflect.ParameterizedType) Type(java.lang.reflect.Type)

Example 2 with RoutingException

use of ninja.RoutingException in project ninja by ninjaframework.

the class ControllerMethodInvoker method instantiateComponent.

private static <T> T instantiateComponent(Class<? extends T> argumentExtractor, final Annotation annotation, final Class<?> paramType, Injector injector) {
    // Noarg constructor
    Constructor noarg = getNoArgConstructor(argumentExtractor);
    if (noarg != null) {
        try {
            return (T) noarg.newInstance();
        } catch (Exception e) {
            throw new RoutingException(e);
        }
    }
    // Simple case, just takes the annotation
    Constructor simple = getSingleArgConstructor(argumentExtractor, annotation.annotationType());
    if (simple != null) {
        try {
            return (T) simple.newInstance(annotation);
        } catch (Exception e) {
            throw new RoutingException(e);
        }
    }
    // Simple case, just takes the parsed class
    Constructor simpleClass = getSingleArgConstructor(argumentExtractor, Class.class);
    if (simpleClass != null) {
        try {
            return (T) simpleClass.newInstance(paramType);
        } catch (Exception e) {
            throw new RoutingException(e);
        }
    }
    // Complex case, use Guice.  Create a child injector with the annotation in it.
    return injector.createChildInjector(new AbstractModule() {

        @Override
        protected void configure() {
            bind((Class<Annotation>) annotation.annotationType()).toInstance(annotation);
            bind(ArgumentClassHolder.class).toInstance(new ArgumentClassHolder(paramType));
        }
    }).getInstance(argumentExtractor);
}
Also used : RoutingException(ninja.RoutingException) Constructor(java.lang.reflect.Constructor) InvocationTargetException(java.lang.reflect.InvocationTargetException) RoutingException(ninja.RoutingException) BadRequestException(ninja.exceptions.BadRequestException) AbstractModule(com.google.inject.AbstractModule)

Example 3 with RoutingException

use of ninja.RoutingException in project ninja by ninjaframework.

the class ControllerMethodInvoker method validateArgumentWithExtractor.

private static ArgumentExtractor<?> validateArgumentWithExtractor(MethodParameter methodParameter, Annotation[] annotations, Injector injector, ArgumentExtractor<?> extractor) {
    // We have validators that get applied before parsing, and validators
    // that get applied after parsing.
    List<Validator<?>> preParseValidators = new ArrayList<>();
    List<Validator<?>> postParseValidators = new ArrayList<>();
    Class<?> boxedParamType = methodParameter.parameterClass;
    if (methodParameter.parameterClass.isPrimitive()) {
        boxedParamType = box(methodParameter.parameterClass);
    }
    // Now we have an extractor, lets apply validators that are able to validate
    for (Annotation annotation : annotations) {
        WithValidator withValidator = annotation.annotationType().getAnnotation(WithValidator.class);
        if (withValidator != null) {
            Validator<?> validator = instantiateComponent(withValidator.value(), annotation, methodParameter.parameterClass, injector);
            // If the validator can validate the extractors type, then it's a pre parse validator
            if (validator.getValidatedType().isAssignableFrom(extractor.getExtractedType())) {
                preParseValidators.add(validator);
            // If it can validate the parameter type, it's a post parse validator
            } else if (validator.getValidatedType().isAssignableFrom(boxedParamType)) {
                postParseValidators.add(validator);
            // Otherwise, we can't validate with this validator
            } else {
                throw new RoutingException("Validator for field " + extractor.getFieldName() + " validates type " + validator.getValidatedType() + ", which doesn't match extracted type " + extractor.getExtractedType() + " or parameter type " + methodParameter.parameterClass);
            }
        }
    }
    // If we have pre parse validators, wrap our extractor in them
    if (!preParseValidators.isEmpty()) {
        extractor = new ValidatingArgumentExtractor(extractor, preParseValidators);
    }
    // String, and we can lookup a parser to parse it into the param type
    if (!boxedParamType.isAssignableFrom(extractor.getExtractedType())) {
        if (extractor.getFieldName() != null) {
            if (String.class.isAssignableFrom(extractor.getExtractedType())) {
                // Look up a parser for a single-valued parameter
                ParamParser<?> parser = injector.getInstance(ParamParsers.class).getParamParser(methodParameter.parameterClass);
                if (parser == null) {
                    throw new RoutingException("Can't find parameter parser for type " + extractor.getExtractedType() + " on field " + extractor.getFieldName());
                } else {
                    extractor = new ParsingArgumentExtractor(extractor, parser);
                }
            } else if (String[].class.isAssignableFrom(extractor.getExtractedType())) {
                // Look up a parser for a multi-valued parameter
                ArrayParamParser<?> parser = injector.getInstance(ParamParsers.class).getArrayParser(methodParameter.parameterClass);
                if (parser == null) {
                    throw new RoutingException("Can't find parameter array parser for type " + extractor.getExtractedType() + " on field " + extractor.getFieldName());
                } else {
                    extractor = new ParsingArrayExtractor(extractor, parser);
                }
            } else {
                throw new RoutingException("Extracted type " + extractor.getExtractedType() + " for field " + extractor.getFieldName() + " doesn't match parameter type " + methodParameter.parameterClass);
            }
        }
    }
    // If we have any post parse validators, wrap our extractor in them
    if (!postParseValidators.isEmpty()) {
        extractor = new ValidatingArgumentExtractor(extractor, postParseValidators);
    }
    if (methodParameter.isOptional) {
        extractor = new OptionalArgumentExtractor(extractor);
    }
    return extractor;
}
Also used : RoutingException(ninja.RoutingException) ArrayList(java.util.ArrayList) WithValidator(ninja.validation.WithValidator) Annotation(java.lang.annotation.Annotation) WithValidator(ninja.validation.WithValidator) Validator(ninja.validation.Validator) ArrayParamParser(ninja.params.ParamParsers.ArrayParamParser)

Aggregations

RoutingException (ninja.RoutingException)3 AbstractModule (com.google.inject.AbstractModule)1 Annotation (java.lang.annotation.Annotation)1 Constructor (java.lang.reflect.Constructor)1 InvocationTargetException (java.lang.reflect.InvocationTargetException)1 ParameterizedType (java.lang.reflect.ParameterizedType)1 Type (java.lang.reflect.Type)1 ArrayList (java.util.ArrayList)1 BadRequestException (ninja.exceptions.BadRequestException)1 ArrayParamParser (ninja.params.ParamParsers.ArrayParamParser)1 Validator (ninja.validation.Validator)1 WithValidator (ninja.validation.WithValidator)1