View Javadoc

1   package com.atlassian.plugins.rest.common.interceptor.impl;
2   
3   import java.lang.reflect.InvocationTargetException;
4   import java.lang.reflect.Type;
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import javax.ws.rs.core.GenericEntity;
9   import javax.ws.rs.core.Response;
10  
11  import com.atlassian.plugins.rest.common.interceptor.MethodInvocation;
12  import com.atlassian.plugins.rest.common.interceptor.ResourceInterceptor;
13  import com.sun.jersey.api.core.HttpContext;
14  import com.sun.jersey.api.model.AbstractResourceMethod;
15  import com.sun.jersey.core.spi.factory.ResponseBuilderImpl;
16  import com.sun.jersey.server.impl.inject.InjectableValuesProvider;
17  import com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher;
18  import com.sun.jersey.spi.container.JavaMethodInvokerFactory;
19  import com.sun.jersey.spi.dispatch.RequestDispatcher;
20  
21  /**
22   * Helps invoke the appropriate method, wrapping the execution in an interceptor chain.
23   *
24   * This is a private class and used by the  {@link EntityParamDispatchProviderWrapper}
25   * and {@link com.atlassian.plugins.rest.common.multipart.jersey.MultipartFormDispatchProvider}
26   * which both will use this helper class to wrap calls to rest methods with interceptors.
27   *
28   * @since 2.0
29   */
30  public class DispatchProviderHelper
31  {
32      private final InterceptorChainBuilder interceptorChainBuilder;
33  
34      public DispatchProviderHelper(InterceptorChainBuilder interceptorChainBuilder)
35      {
36          this.interceptorChainBuilder = interceptorChainBuilder;
37      }
38  
39      public RequestDispatcher create(AbstractResourceMethod abstractResourceMethod, InjectableValuesProvider pp)
40      {
41          if (pp == null)
42          {
43              return null;
44          }
45  
46          final List<ResourceInterceptor> interceptors = interceptorChainBuilder.getResourceInterceptorsForMethod(abstractResourceMethod.getMethod());
47  
48          // TODO
49          // Strictly speaking a GET request can contain an entity in the
50          // message body, but this is likely to be not implemented by many
51          // servers and clients, but should we support it?
52          boolean requireReturnOfRepresentation =
53                  "GET".equals(abstractResourceMethod.getHttpMethod());
54  
55          Class<?> returnType = abstractResourceMethod.getMethod().getReturnType();
56          if (Response.class.isAssignableFrom(returnType))
57          {
58              return new ResponseOutInvoker(abstractResourceMethod, pp, interceptors);
59          }
60          else if (returnType != void.class)
61          {
62              if (returnType == Object.class || GenericEntity.class.isAssignableFrom(returnType))
63              {
64                  return new ObjectOutInvoker(abstractResourceMethod, pp, interceptors);
65              }
66              else
67              {
68                  return new TypeOutInvoker(abstractResourceMethod, pp, interceptors);
69              }
70          }
71          else if (requireReturnOfRepresentation)
72          {
73              return null;
74          }
75          else
76          {
77              return new VoidOutInvoker(abstractResourceMethod, pp, interceptors);
78          }
79      }
80  
81      private static void invokeMethodWithInterceptors(List<ResourceInterceptor> originalInterceptors,
82                                                       AbstractResourceMethod method,
83                                                       Object resource,
84                                                       HttpContext httpContext,
85                                                       Object[] params,
86                                                       final MethodInvoker methodInvocation) throws IllegalAccessException, InvocationTargetException
87      {
88          ResourceInterceptor lastInterceptor = new ResourceInterceptor()
89          {
90              public void intercept(MethodInvocation invocation) throws IllegalAccessException, InvocationTargetException
91              {
92                  methodInvocation.invoke();
93              }
94          };
95  
96          List<ResourceInterceptor> interceptors = new ArrayList<ResourceInterceptor>(originalInterceptors);
97          interceptors.add(lastInterceptor);
98  
99          MethodInvocation inv = new DefaultMethodInvocation(resource, method, httpContext, interceptors, params);
100         inv.invoke();
101     }
102 
103     private static interface MethodInvoker
104     {
105         void invoke() throws IllegalAccessException, InvocationTargetException;
106     }
107 
108 
109     private static abstract class EntityParamInInvoker extends ResourceJavaMethodDispatcher
110     {
111         private final InjectableValuesProvider pp;
112         final AbstractResourceMethod abstractResourceMethod;
113         final List<ResourceInterceptor> interceptors;
114 
115         EntityParamInInvoker(AbstractResourceMethod abstractResourceMethod,
116                              InjectableValuesProvider pp,
117                              List<ResourceInterceptor> interceptors)
118         {
119             super(abstractResourceMethod, JavaMethodInvokerFactory.getDefault());
120             this.pp = pp;
121             this.abstractResourceMethod = abstractResourceMethod;
122             this.interceptors = interceptors;
123         }
124 
125         final Object[] getParams(HttpContext context)
126         {
127             return pp.getInjectableValues(context);
128         }
129 
130     }
131 
132     private static final class VoidOutInvoker extends EntityParamInInvoker
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 }