AnnotatedMethod.java revision 568e7121664e2ad34a90bf97ae9129134a8ce408
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * The contents of this file are subject to the terms of the Common Development and
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * Distribution License (the License). You may not use this file except in compliance with the
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * specific language governing permission and limitations under the License.
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * When distributing Covered Software, include this CDDL Header Notice in each file and include
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * Header, with the fields enclosed by brackets [] replaced by your own identifying
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * information: "Portions copyright [year] [name of copyright owner]".
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * Copyright 2015 ForgeRock AS.
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Boseimport static org.forgerock.util.promise.Promises.newResultPromise;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Boseimport java.lang.reflect.InvocationTargetException;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Boseimport org.forgerock.util.promise.NeverThrowsException;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private final int contextParameter;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private final int requestParameter;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private final int numberOfParameters;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private final Function<Object, Promise<Response, NeverThrowsException>, NeverThrowsException> responseAdapter;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose AnnotatedMethod(String operation, Object requestHandler, Method method, int contextParameter,
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose Function<Object, Promise<Response, NeverThrowsException>, NeverThrowsException> responseAdapter) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose Promise<Response, NeverThrowsException> invoke(Context context, Request request) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose return newResultPromise(createErrorResponse(Status.NOT_IMPLEMENTED, "Not supported"));
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose Object result = method.invoke(requestHandler, args);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose throw new IllegalStateException("Cannot access the annotated method: " + method.getName(), e);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose throw new IllegalStateException("Exception from invocation expected to be handled by promise", e);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private Response createErrorResponse(Status status, String s) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose return new Response().setStatus(status).setEntity(s);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose static AnnotatedMethod findMethod(Object requestHandler, Class<? extends Annotation> annotation) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose for (Method method : requestHandler.getClass().getMethods()) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose AnnotatedMethod checked = checkMethod(annotation, requestHandler, method);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose for (Method method : requestHandler.getClass().getMethods()) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose String crestVerb = annotation.getSimpleName().toLowerCase();
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose AnnotatedMethod checked = checkMethod(annotation, requestHandler, method);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose return new AnnotatedMethod(annotation.getSimpleName(), null, null, -1, -1, -1, null);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose static AnnotatedMethod checkMethod(Class<?> annotation, Object requestHandler, Method method) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose for (int i = 0; i < method.getParameterTypes().length; i++) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose } else if (Request.class.isAssignableFrom(type)) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose Function<Object, Promise<Response, NeverThrowsException>, NeverThrowsException> resourceCreator;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose if (Promise.class.equals(method.getReturnType())) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose } else if (Response.class.equals(method.getReturnType())) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose resourceCreator = new Function<Object, Promise<Response,NeverThrowsException>, NeverThrowsException>() {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose public Promise<Response, NeverThrowsException> apply(Object o) throws NeverThrowsException {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose resourceCreator = ResponseCreator.forType(method.getReturnType());
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose return new AnnotatedMethod(annotation.getSimpleName(), requestHandler, method, contextParam,
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose requestParam, method.getParameterTypes().length, resourceCreator);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * A function to create a {@link Response} from the generic type of a method that produces response
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private static class ResponseCreator implements Function<Object, Promise<Response, NeverThrowsException>, NeverThrowsException> {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private final Function<Object, Object, NeverThrowsException> entityConverter;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private ResponseCreator(Function<Object, Object, NeverThrowsException> entityConverter) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose public Promise<Response, NeverThrowsException> apply(Object o) throws IllegalStateException {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose .setStatus(content == null ? Status.NO_CONTENT : Status.OK));
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * Uses reflection to deduce the need for a {@code ResourceCreator}, and return an appropriately created one
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * if needed.
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * @param type The type for the {@code Response} entity.
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose * @return A new function.
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private static Function<Object, Promise<Response, NeverThrowsException>, NeverThrowsException> forType(Class<?> type) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose if (String.class.equals(type) || Void.class.equals(type) || byte[].class.equals(type)) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose return new ResponseCreator(new Function<Object, Object, NeverThrowsException>() {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose public Object apply(Object o) throws NeverThrowsException {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose throw new IllegalArgumentException("Unsupported response type: " + type);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose private static class PromisedResponseCreator implements Function<Object, Promise<Response, NeverThrowsException>, NeverThrowsException> {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose public Promise<Response, NeverThrowsException> apply(Object o) throws NeverThrowsException {
52f1093ef3d7c44132ec10c57436865b2cbb19d7Sumit Bose throw new UnsupportedOperationException("to be implemented");
52f1093ef3d7c44132ec10c57436865b2cbb19d7Sumit Bose private static final Function<Object, Object, NeverThrowsException> IDENTITY_FUNCTION = new Function<Object, Object, NeverThrowsException>() {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose public Object apply(Object o) throws NeverThrowsException {