1   package com.atlassian.plugins.rest.common.interceptor.impl;
2   
3   import com.atlassian.plugins.rest.common.interceptor.MethodInvocation;
4   import com.atlassian.plugins.rest.common.interceptor.ResourceInterceptor;
5   import com.atlassian.plugins.rest.common.interceptor.impl.DefaultMethodInvocation;
6   import com.atlassian.plugins.rest.common.interceptor.impl.InterceptorChainBuilder;
7   import com.sun.jersey.api.core.HttpContext;
8   import com.sun.jersey.api.model.AbstractResourceMethod;
9   import com.sun.jersey.server.impl.ResponseBuilderImpl;
10  import com.sun.jersey.server.impl.inject.InjectableValuesProvider;
11  import com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider;
12  import com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher;
13  import com.sun.jersey.server.impl.model.method.dispatch.ResourceMethodDispatchProvider;
14  import com.sun.jersey.spi.dispatch.RequestDispatcher;
15  
16  import javax.ws.rs.core.GenericEntity;
17  import javax.ws.rs.core.Response;
18  import java.lang.reflect.InvocationTargetException;
19  import java.lang.reflect.Type;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  /**
24   * Helps invoke the appropriate method, wrapping the execution in an interceptor chain
25   *
26   * @since 2.0
27   */
28  class DispatchProviderHelper
29  {
30      private final InterceptorChainBuilder interceptorChainBuilder;
31  
32      public DispatchProviderHelper(InterceptorChainBuilder interceptorChainBuilder)
33      {
34          this.interceptorChainBuilder = interceptorChainBuilder;
35      }
36  
37      public RequestDispatcher create(AbstractResourceMethod abstractResourceMethod, InjectableValuesProvider pp)
38      {
39          if (pp == null)
40          {
41              return null;
42          }
43  
44          final List<ResourceInterceptor> interceptors = interceptorChainBuilder.getResourceInterceptorsForMethod(abstractResourceMethod.getMethod());
45  
46          // TODO
47          // Strictly speaking a GET request can contain an entity in the
48          // message body, but this is likely to be not implemented by many
49          // servers and clients, but should we support it?
50          boolean requireReturnOfRepresentation =
51                  "GET".equals(abstractResourceMethod.getHttpMethod());
52  
53          Class<?> returnType = abstractResourceMethod.getMethod().getReturnType();
54          if (Response.class.isAssignableFrom(returnType))
55          {
56              return new ResponseOutInvoker(abstractResourceMethod, pp, interceptors);
57          }
58          else if (returnType != void.class)
59          {
60              if (returnType == Object.class || GenericEntity.class.isAssignableFrom(returnType))
61              {
62                  return new ObjectOutInvoker(abstractResourceMethod, pp, interceptors);
63              }
64              else
65              {
66                  return new TypeOutInvoker(abstractResourceMethod, pp, interceptors);
67              }
68          }
69          else if (requireReturnOfRepresentation)
70          {
71              return null;
72          }
73          else
74          {
75              return new VoidOutInvoker(abstractResourceMethod, pp, interceptors);
76          }
77      }
78  
79      private static void invokeMethodWithInterceptors(List<ResourceInterceptor> originalInterceptors,
80                                                       AbstractResourceMethod method,
81                                                       Object resource,
82                                                       HttpContext httpContext,
83                                                       Object[] params,
84                                                       final MethodInvoker methodInvocation) throws IllegalAccessException, InvocationTargetException
85      {
86          ResourceInterceptor lastInterceptor = new ResourceInterceptor()
87          {
88              public void intercept(MethodInvocation invocation) throws IllegalAccessException, InvocationTargetException
89              {
90                  methodInvocation.invoke();
91              }
92          };
93  
94          List<ResourceInterceptor> interceptors = new ArrayList<ResourceInterceptor>(originalInterceptors);
95          interceptors.add(lastInterceptor);
96  
97          MethodInvocation inv = new DefaultMethodInvocation(resource, method, httpContext, interceptors, params);
98          inv.invoke();
99      }
100 
101     private static interface MethodInvoker
102     {
103         void invoke() throws IllegalAccessException, InvocationTargetException;
104     }
105 
106 
107     private static abstract class EntityParamInInvoker extends ResourceJavaMethodDispatcher
108     {
109         private final InjectableValuesProvider pp;
110         final AbstractResourceMethod abstractResourceMethod;
111         final List<ResourceInterceptor> interceptors;
112 
113         EntityParamInInvoker(AbstractResourceMethod abstractResourceMethod,
114                              InjectableValuesProvider pp,
115                              List<ResourceInterceptor> interceptors)
116         {
117             super(abstractResourceMethod);
118             this.pp = pp;
119             this.abstractResourceMethod = abstractResourceMethod;
120             this.interceptors = interceptors;
121         }
122 
123         final Object[] getParams(HttpContext context)
124         {
125             return pp.getInjectableValues(context);
126         }
127 
128     }
129 
130     private static final class VoidOutInvoker extends EntityParamInInvoker
131     {
132 
133 
134         VoidOutInvoker(AbstractResourceMethod abstractResourceMethod,
135                        InjectableValuesProvider pp,
136                        List<ResourceInterceptor> interceptors)
137         {
138             super(abstractResourceMethod, pp, interceptors);
139         }
140 
141         public void _dispatch(final Object resource, HttpContext context)
142                 throws IllegalAccessException, InvocationTargetException
143         {
144             final Object[] params = getParams(context);
145             invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
146             {
147                 public void invoke() throws IllegalAccessException, InvocationTargetException
148                 {
149                     method.invoke(resource, params);
150                 }
151             });
152         }
153     }
154 
155     private static final class TypeOutInvoker extends EntityParamInInvoker
156     {
157         private final Type t;
158 
159         TypeOutInvoker(AbstractResourceMethod abstractResourceMethod,
160                        InjectableValuesProvider pp,
161                        List<ResourceInterceptor> interceptors)
162         {
163             super(abstractResourceMethod, pp, interceptors);
164             this.t = abstractResourceMethod.getMethod().getGenericReturnType();
165         }
166 
167         public void _dispatch(final Object resource, final HttpContext context)
168                 throws IllegalAccessException, InvocationTargetException
169         {
170             final Object[] params = getParams(context);
171 
172             invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
173             {
174                 public void invoke() throws IllegalAccessException, InvocationTargetException
175                 {
176                     Object o = method.invoke(resource, params);
177                     if (o != null)
178                     {
179                         Response r = new ResponseBuilderImpl().
180                                 entityWithType(o, t).status(200).build();
181                         context.getResponse().setResponse(r);
182                     }
183                 }
184             });
185         }
186     }
187 
188     private static final class ResponseOutInvoker extends EntityParamInInvoker
189     {
190         ResponseOutInvoker(AbstractResourceMethod abstractResourceMethod,
191                            InjectableValuesProvider pp,
192                            List<ResourceInterceptor> interceptors)
193         {
194             super(abstractResourceMethod, pp, interceptors);
195         }
196 
197         public void _dispatch(final Object resource, final HttpContext context)
198                 throws IllegalAccessException, InvocationTargetException
199         {
200             final Object[] params = getParams(context);
201 
202             invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
203             {
204                 public void invoke() throws IllegalAccessException, InvocationTargetException
205                 {
206                     Response r = (Response) method.invoke(resource, params);
207                     if (r != null)
208                     {
209                         context.getResponse().setResponse(r);
210                     }
211                 }
212             });
213 
214         }
215     }
216 
217     private static final class ObjectOutInvoker extends EntityParamInInvoker
218     {
219         ObjectOutInvoker(AbstractResourceMethod abstractResourceMethod,
220                          InjectableValuesProvider pp,
221                          List<ResourceInterceptor> interceptors)
222         {
223             super(abstractResourceMethod, pp, interceptors);
224         }
225 
226         public void _dispatch(final Object resource, final HttpContext context)
227                 throws IllegalAccessException, InvocationTargetException
228         {
229             final Object[] params = getParams(context);
230 
231             invokeMethodWithInterceptors(interceptors, abstractResourceMethod, resource, context, params, new MethodInvoker()
232             {
233                 public void invoke() throws IllegalAccessException, InvocationTargetException
234                 {
235                     Object o = method.invoke(resource, params);
236 
237                     if (o instanceof Response)
238                     {
239                         Response r = (Response) o;
240                         context.getResponse().setResponse(r);
241                     }
242                     else if (o != null)
243                     {
244                         Response r = new ResponseBuilderImpl().status(200).entity(o).build();
245                         context.getResponse().setResponse(r);
246                     }
247                 }
248             });
249 
250 
251         }
252     }
253 }