1 package com.atlassian.plugins.rest.common.interceptor.impl;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.List;
7 import java.util.concurrent.atomic.AtomicReference;
8
9 import javax.ws.rs.QueryParam;
10 import javax.ws.rs.core.GenericEntity;
11 import javax.ws.rs.core.Response;
12
13 import com.atlassian.plugins.rest.common.interceptor.MethodInvocation;
14 import com.atlassian.plugins.rest.common.interceptor.ResourceInterceptor;
15 import com.sun.jersey.api.core.HttpContext;
16 import com.sun.jersey.api.model.AbstractResourceMethod;
17 import com.sun.jersey.api.model.Parameter;
18 import com.sun.jersey.core.spi.factory.ResponseBuilderImpl;
19 import com.sun.jersey.server.impl.inject.InjectableValuesProvider;
20 import com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher;
21 import com.sun.jersey.spi.container.JavaMethodInvokerFactory;
22 import com.sun.jersey.spi.dispatch.RequestDispatcher;
23
24
25
26
27
28
29
30
31
32
33
34 public class DispatchProviderHelper
35 {
36
37
38 static final AtomicReference<Boolean> JERSEY_291_SHIM = new AtomicReference<Boolean>();
39
40 private final InterceptorChainBuilder interceptorChainBuilder;
41
42 public DispatchProviderHelper(InterceptorChainBuilder interceptorChainBuilder)
43 {
44 this.interceptorChainBuilder = interceptorChainBuilder;
45 }
46
47 public RequestDispatcher create(AbstractResourceMethod abstractResourceMethod, InjectableValuesProvider pp)
48 {
49 if (pp == null)
50 {
51 return null;
52 }
53
54 final List<ResourceInterceptor> interceptors = interceptorChainBuilder.getResourceInterceptorsForMethod(abstractResourceMethod.getMethod());
55
56
57
58
59
60 boolean requireReturnOfRepresentation =
61 "GET".equals(abstractResourceMethod.getHttpMethod());
62
63 Class<?> returnType = abstractResourceMethod.getMethod().getReturnType();
64 if (Response.class.isAssignableFrom(returnType))
65 {
66 return new ResponseOutInvoker(abstractResourceMethod, pp, interceptors);
67 }
68 else if (returnType != void.class)
69 {
70 if (returnType == Object.class || GenericEntity.class.isAssignableFrom(returnType))
71 {
72 return new ObjectOutInvoker(abstractResourceMethod, pp, interceptors);
73 }
74 else
75 {
76 return new TypeOutInvoker(abstractResourceMethod, pp, interceptors);
77 }
78 }
79 else if (requireReturnOfRepresentation)
80 {
81 return null;
82 }
83 else
84 {
85 return new VoidOutInvoker(abstractResourceMethod, pp, interceptors);
86 }
87 }
88
89 static void invokeMethodWithInterceptors(List<ResourceInterceptor> originalInterceptors,
90 AbstractResourceMethod method,
91 Object resource,
92 HttpContext httpContext,
93 Object[] params,
94 final MethodInvoker methodInvocation) throws IllegalAccessException, InvocationTargetException
95 {
96 ResourceInterceptor lastInterceptor = new ResourceInterceptor()
97 {
98 public void intercept(MethodInvocation invocation) throws IllegalAccessException, InvocationTargetException
99 {
100 methodInvocation.invoke();
101 }
102 };
103
104 List<ResourceInterceptor> interceptors = new ArrayList<ResourceInterceptor>(originalInterceptors);
105 interceptors.add(lastInterceptor);
106
107
108
109 Boolean shim = JERSEY_291_SHIM.get();
110 if (shim == null)
111 {
112 shim = Boolean.getBoolean("com.atlassian.plugins.rest.shim.JERSEY-291");
113 JERSEY_291_SHIM.set(shim);
114 }
115 if (shim)
116 {
117 final List<Parameter> parameterList = method.getParameters();
118 for (int i=0; i<params.length; ++i)
119 {
120 if (parameterList.get(i).isAnnotationPresent(QueryParam.class))
121 {
122 final Object param = params[i];
123 if (param instanceof Collection<?> && ((Collection<?>)param).isEmpty())
124 {
125 params[i] = null;
126 }
127 }
128 }
129 }
130
131 MethodInvocation inv = new DefaultMethodInvocation(resource, method, httpContext, interceptors, params);
132 inv.invoke();
133 }
134
135 private static interface MethodInvoker
136 {
137 void invoke() throws IllegalAccessException, InvocationTargetException;
138 }
139
140
141 private static abstract class EntityParamInInvoker extends ResourceJavaMethodDispatcher
142 {
143 private final InjectableValuesProvider pp;
144 final AbstractResourceMethod abstractResourceMethod;
145 final List<ResourceInterceptor> interceptors;
146
147 EntityParamInInvoker(AbstractResourceMethod abstractResourceMethod,
148 InjectableValuesProvider pp,
149 List<ResourceInterceptor> interceptors)
150 {
151 super(abstractResourceMethod, JavaMethodInvokerFactory.getDefault());
152 this.pp = pp;
153 this.abstractResourceMethod = abstractResourceMethod;
154 this.interceptors = interceptors;
155 }
156
157 final Object[] getParams(HttpContext context)
158 {
159 return pp.getInjectableValues(context);
160 }
161
162 }
163
164 private static final class VoidOutInvoker extends EntityParamInInvoker
165 {
166 VoidOutInvoker(AbstractResourceMethod abstractResourceMethod,
167 InjectableValuesProvider pp,
168 List<ResourceInterceptor> interceptors)
169 {
170 super(abstractResourceMethod, pp, interceptors);
171 }
172
173 public void _dispatch(final Object resource, HttpContext context)
174 throws IllegalAccessException, InvocationTargetException
175 {
176 final Object[] params = getParams(context);
177 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
178 {
179 public void invoke() throws IllegalAccessException, InvocationTargetException
180 {
181 method.invoke(resource, params);
182 }
183 });
184 }
185 }
186
187 private static final class TypeOutInvoker extends EntityParamInInvoker
188 {
189 TypeOutInvoker(AbstractResourceMethod abstractResourceMethod,
190 InjectableValuesProvider pp,
191 List<ResourceInterceptor> interceptors)
192 {
193 super(abstractResourceMethod, pp, interceptors);
194 }
195
196 public void _dispatch(final Object resource, final HttpContext context)
197 throws IllegalAccessException, InvocationTargetException
198 {
199 final Object[] params = getParams(context);
200
201 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
202 {
203 public void invoke() throws IllegalAccessException, InvocationTargetException
204 {
205 Object o = method.invoke(resource, params);
206 if (o != null)
207 {
208 Response r = new ResponseBuilderImpl().entity(o).status(200).build();
209 context.getResponse().setResponse(r);
210 }
211 }
212 });
213 }
214 }
215
216 private static final class ResponseOutInvoker extends EntityParamInInvoker
217 {
218 ResponseOutInvoker(AbstractResourceMethod abstractResourceMethod,
219 InjectableValuesProvider pp,
220 List<ResourceInterceptor> interceptors)
221 {
222 super(abstractResourceMethod, pp, interceptors);
223 }
224
225 public void _dispatch(final Object resource, final HttpContext context)
226 throws IllegalAccessException, InvocationTargetException
227 {
228 final Object[] params = getParams(context);
229
230 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
231 {
232 public void invoke() throws IllegalAccessException, InvocationTargetException
233 {
234 Response r = (Response) method.invoke(resource, params);
235 if (r != null)
236 {
237 context.getResponse().setResponse(r);
238 }
239 }
240 });
241
242 }
243 }
244
245 private static final class ObjectOutInvoker extends EntityParamInInvoker
246 {
247 ObjectOutInvoker(AbstractResourceMethod abstractResourceMethod,
248 InjectableValuesProvider pp,
249 List<ResourceInterceptor> interceptors)
250 {
251 super(abstractResourceMethod, pp, interceptors);
252 }
253
254 public void _dispatch(final Object resource, final HttpContext context)
255 throws IllegalAccessException, InvocationTargetException
256 {
257 final Object[] params = getParams(context);
258
259 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
260 {
261 public void invoke() throws IllegalAccessException, InvocationTargetException
262 {
263 Object o = method.invoke(resource, params);
264
265 if (o instanceof Response)
266 {
267 Response r = (Response) o;
268 context.getResponse().setResponse(r);
269 }
270 else if (o != null)
271 {
272 Response r = new ResponseBuilderImpl().status(200).entity(o).build();
273 context.getResponse().setResponse(r);
274 }
275 }
276 });
277 }
278 }
279 }