1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.json.resource;
18
19 import java.lang.annotation.Annotation;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.util.Arrays;
23
24 import org.forgerock.services.context.Context;
25 import org.forgerock.api.annotations.Create;
26 import org.forgerock.api.annotations.Patch;
27 import org.forgerock.api.annotations.Query;
28 import org.forgerock.api.annotations.Update;
29 import org.forgerock.util.promise.Promise;
30
31
32
33
34
35
36
37
38
39
40
41 final class AnnotatedMethod {
42 private final static int ABSENT = -1;
43 private final Object requestHandler;
44 private final Method method;
45 private final int idParameter;
46 private final int contextParameter;
47 private final int requestParameter;
48 private final int queryHandlerParameter;
49 private final int numberOfParameters;
50 private final String operation;
51
52 AnnotatedMethod(String operation, Object requestHandler, Method method, int idParameter, int contextParameter,
53 int requestParameter, int queryHandlerParameter, int numberOfParameters) {
54 this.operation = operation;
55 this.requestHandler = requestHandler;
56 this.method = method;
57 this.idParameter = idParameter;
58 this.contextParameter = contextParameter;
59 this.requestParameter = requestParameter;
60 this.queryHandlerParameter = queryHandlerParameter;
61 this.numberOfParameters = numberOfParameters;
62 }
63
64 boolean isUsingId() {
65 return idParameter != ABSENT;
66 }
67
68 <T> Promise<T, ResourceException> invoke(Context context, Request request) {
69 return invoke(context, request, null, null);
70 }
71
72 <T> Promise<T, ResourceException> invoke(Context context, Request request, String id) {
73 return invoke(context, request, null, id);
74 }
75
76 <T> Promise<T, ResourceException> invoke(Context context, Request request,
77 QueryResourceHandler queryHandler) {
78 return invoke(context, request, queryHandler, null);
79 }
80
81 @SuppressWarnings("unchecked")
82 <T> Promise<T, ResourceException> invoke(Context context, Request request,
83 QueryResourceHandler queryHandler, String id) {
84 if (method == null) {
85 if (operation.equalsIgnoreCase("Create")) {
86 return new CreateNotSupportedException().asPromise();
87 }
88 return new BadRequestException(operation + " not supported").asPromise();
89 }
90 Object[] args = new Object[numberOfParameters];
91 if (idParameter != ABSENT) {
92 args[idParameter] = id;
93 }
94 if (requestParameter != ABSENT) {
95 args[requestParameter] = request;
96 }
97 if (contextParameter != ABSENT) {
98 args[contextParameter] = context;
99 }
100 if (queryHandlerParameter != ABSENT) {
101 args[queryHandlerParameter] = queryHandler;
102 }
103 try {
104 return (Promise<T, ResourceException>) method.invoke(requestHandler, args);
105 } catch (IllegalAccessException e) {
106 throw new IllegalStateException("Cannot access the annotated method: " + method.getName(), e);
107 } catch (InvocationTargetException e) {
108 throw new IllegalStateException("Exception from invocation expected to be handled by promise", e);
109 }
110 }
111
112 static AnnotatedMethod findMethod(Object requestHandler, Class<? extends Annotation> annotation, boolean needsId) {
113 for (Method method : requestHandler.getClass().getMethods()) {
114 if (method.getAnnotation(annotation) != null) {
115 AnnotatedMethod checked = checkMethod(annotation, requestHandler, method, needsId);
116 if (checked != null) {
117 return checked;
118 }
119 }
120 }
121 for (Method method : requestHandler.getClass().getMethods()) {
122 if (method.getName().equals(annotation.getSimpleName().toLowerCase())) {
123 AnnotatedMethod checked = checkMethod(annotation, requestHandler, method, needsId);
124 if (checked != null) {
125 return checked;
126 }
127 }
128 }
129 return new AnnotatedMethod(annotation.getSimpleName(), null, null, ABSENT, ABSENT, ABSENT, ABSENT, ABSENT);
130 }
131
132 static AnnotatedMethod checkMethod(Class<?> annotation, Object requestHandler, Method method, boolean needsId) {
133 if (Promise.class.equals(method.getReturnType())) {
134 int idParam = ABSENT;
135 int contextParam = ABSENT;
136 int requestParam = ABSENT;
137 int queryHandlerParam = ABSENT;
138 for (int i = 0; i < method.getParameterTypes().length; i++) {
139 Class<?> type = method.getParameterTypes()[i];
140 if (String.class.equals(type)) {
141 idParam = i;
142 } else if (Context.class.equals(type)) {
143 contextParam = i;
144 } else if (Request.class.isAssignableFrom(type)) {
145 requestParam = i;
146 } else if (type.isAssignableFrom(QueryResourceHandler.class)) {
147 queryHandlerParam = i;
148 }
149 }
150 if (Arrays.asList(Create.class, Update.class, Patch.class, Query.class).contains(annotation)
151 && requestParam == ABSENT) {
152 return null;
153 }
154 if (queryHandlerParam == ABSENT && Query.class.equals(annotation)
155 || queryHandlerParam != ABSENT && !Query.class.equals(annotation)) {
156 return null;
157 }
158 if (!needsId || idParam != ABSENT) {
159 return new AnnotatedMethod(annotation.getSimpleName(), requestHandler, method, idParam, contextParam,
160 requestParam, queryHandlerParam, method.getParameterTypes().length);
161 }
162 }
163 return null;
164 }
165 }