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);
}
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);
}
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;
}
Aggregations