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 static final AtomicReference<Boolean> JERSEY_291_SHIM = new AtomicReference<Boolean>();
38
39 private final InterceptorChainBuilder interceptorChainBuilder;
40
41 public DispatchProviderHelper(InterceptorChainBuilder interceptorChainBuilder) {
42 this.interceptorChainBuilder = interceptorChainBuilder;
43 }
44
45 public RequestDispatcher create(AbstractResourceMethod abstractResourceMethod, InjectableValuesProvider pp) {
46 if (pp == null) {
47 return null;
48 }
49
50 final List<ResourceInterceptor> interceptors = interceptorChainBuilder.getResourceInterceptorsForMethod(abstractResourceMethod.getMethod());
51
52
53
54
55
56 boolean requireReturnOfRepresentation =
57 "GET".equals(abstractResourceMethod.getHttpMethod());
58
59 Class<?> returnType = abstractResourceMethod.getMethod().getReturnType();
60 if (Response.class.isAssignableFrom(returnType)) {
61 return new ResponseOutInvoker(abstractResourceMethod, pp, interceptors);
62 } else if (returnType != void.class) {
63 if (returnType == Object.class || GenericEntity.class.isAssignableFrom(returnType)) {
64 return new ObjectOutInvoker(abstractResourceMethod, pp, interceptors);
65 } else {
66 return new TypeOutInvoker(abstractResourceMethod, pp, interceptors);
67 }
68 } else if (requireReturnOfRepresentation) {
69 return null;
70 } else {
71 return new VoidOutInvoker(abstractResourceMethod, pp, interceptors);
72 }
73 }
74
75 static void invokeMethodWithInterceptors(List<ResourceInterceptor> originalInterceptors,
76 AbstractResourceMethod method,
77 Object resource,
78 HttpContext httpContext,
79 Object[] params,
80 final MethodInvoker methodInvocation) throws IllegalAccessException, InvocationTargetException {
81 ResourceInterceptor lastInterceptor = new ResourceInterceptor() {
82 public void intercept(MethodInvocation invocation) throws IllegalAccessException, InvocationTargetException {
83 methodInvocation.invoke();
84 }
85 };
86
87 List<ResourceInterceptor> interceptors = new ArrayList<ResourceInterceptor>(originalInterceptors);
88 interceptors.add(lastInterceptor);
89
90
91
92 Boolean shim = JERSEY_291_SHIM.get();
93 if (shim == null) {
94 shim = Boolean.getBoolean("com.atlassian.plugins.rest.shim.JERSEY-291");
95 JERSEY_291_SHIM.set(shim);
96 }
97 if (shim) {
98 final List<Parameter> parameterList = method.getParameters();
99 for (int i = 0; i < params.length; ++i) {
100 if (parameterList.get(i).isAnnotationPresent(QueryParam.class)) {
101 final Object param = params[i];
102 if (param instanceof Collection<?> && ((Collection<?>) param).isEmpty()) {
103 params[i] = null;
104 }
105 }
106 }
107 }
108
109 MethodInvocation inv = new DefaultMethodInvocation(resource, method, httpContext, interceptors, params);
110 inv.invoke();
111 }
112
113 private static interface MethodInvoker {
114 void invoke() throws IllegalAccessException, InvocationTargetException;
115 }
116
117
118 private static abstract class EntityParamInInvoker extends ResourceJavaMethodDispatcher {
119 private final InjectableValuesProvider pp;
120 final AbstractResourceMethod abstractResourceMethod;
121 final List<ResourceInterceptor> interceptors;
122
123 EntityParamInInvoker(AbstractResourceMethod abstractResourceMethod,
124 InjectableValuesProvider pp,
125 List<ResourceInterceptor> interceptors) {
126 super(abstractResourceMethod, JavaMethodInvokerFactory.getDefault());
127 this.pp = pp;
128 this.abstractResourceMethod = abstractResourceMethod;
129 this.interceptors = interceptors;
130 }
131
132 final Object[] getParams(HttpContext context) {
133 return pp.getInjectableValues(context);
134 }
135
136 }
137
138 private static final class VoidOutInvoker extends EntityParamInInvoker {
139 VoidOutInvoker(AbstractResourceMethod abstractResourceMethod,
140 InjectableValuesProvider pp,
141 List<ResourceInterceptor> interceptors) {
142 super(abstractResourceMethod, pp, interceptors);
143 }
144
145 public void _dispatch(final Object resource, HttpContext context)
146 throws IllegalAccessException, InvocationTargetException {
147 final Object[] params = getParams(context);
148 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker() {
149 public void invoke() throws IllegalAccessException, InvocationTargetException {
150 method.invoke(resource, params);
151 }
152 });
153 }
154 }
155
156 private static final class TypeOutInvoker extends EntityParamInInvoker {
157 TypeOutInvoker(AbstractResourceMethod abstractResourceMethod,
158 InjectableValuesProvider pp,
159 List<ResourceInterceptor> interceptors) {
160 super(abstractResourceMethod, pp, interceptors);
161 }
162
163 public void _dispatch(final Object resource, final HttpContext context)
164 throws IllegalAccessException, InvocationTargetException {
165 final Object[] params = getParams(context);
166
167 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker() {
168 public void invoke() throws IllegalAccessException, InvocationTargetException {
169 Object o = method.invoke(resource, params);
170 if (o != null) {
171 Response r = new ResponseBuilderImpl().entity(o).status(200).build();
172 context.getResponse().setResponse(r);
173 }
174 }
175 });
176 }
177 }
178
179 private static final class ResponseOutInvoker extends EntityParamInInvoker {
180 ResponseOutInvoker(AbstractResourceMethod abstractResourceMethod,
181 InjectableValuesProvider pp,
182 List<ResourceInterceptor> interceptors) {
183 super(abstractResourceMethod, pp, interceptors);
184 }
185
186 public void _dispatch(final Object resource, final HttpContext context)
187 throws IllegalAccessException, InvocationTargetException {
188 final Object[] params = getParams(context);
189
190 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker() {
191 public void invoke() throws IllegalAccessException, InvocationTargetException {
192 Response r = (Response) method.invoke(resource, params);
193 if (r != null) {
194 context.getResponse().setResponse(r);
195 }
196 }
197 });
198
199 }
200 }
201
202 private static final class ObjectOutInvoker extends EntityParamInInvoker {
203 ObjectOutInvoker(AbstractResourceMethod abstractResourceMethod,
204 InjectableValuesProvider pp,
205 List<ResourceInterceptor> interceptors) {
206 super(abstractResourceMethod, pp, interceptors);
207 }
208
209 public void _dispatch(final Object resource, final HttpContext context)
210 throws IllegalAccessException, InvocationTargetException {
211 final Object[] params = getParams(context);
212
213 invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker() {
214 public void invoke() throws IllegalAccessException, InvocationTargetException {
215 Object o = method.invoke(resource, params);
216
217 if (o instanceof Response) {
218 Response r = (Response) o;
219 context.getResponse().setResponse(r);
220 } else if (o != null) {
221 Response r = new ResponseBuilderImpl().status(200).entity(o).build();
222 context.getResponse().setResponse(r);
223 }
224 }
225 });
226 }
227 }
228 }