1   package com.atlassian.plugins.rest.common.multipart.jersey;
2   
3   import com.atlassian.plugins.rest.common.multipart.FilePart;
4   import com.atlassian.plugins.rest.common.multipart.MultipartForm;
5   import com.atlassian.plugins.rest.common.multipart.MultipartFormParam;
6   import com.sun.jersey.api.core.HttpContext;
7   import com.sun.jersey.api.model.AbstractResourceMethod;
8   import com.sun.jersey.api.model.Parameter;
9   import com.sun.jersey.core.spi.component.ComponentScope;
10  import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
11  import com.sun.jersey.server.impl.inject.InjectableValuesProvider;
12  import com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider;
13  import com.sun.jersey.spi.inject.Injectable;
14  
15  import java.lang.annotation.Annotation;
16  import java.util.ArrayList;
17  import java.util.Collection;
18  import java.util.HashSet;
19  import java.util.List;
20  import java.util.Set;
21  import javax.ws.rs.ext.Provider;
22  
23  /**
24   * Dispatchs requests to methods with MultipartFormParam parameters
25   *
26   * @since 2.4
27   */
28  @Provider
29  public class MultipartFormDispatchProvider extends AbstractResourceMethodDispatchProvider
30  {
31      private static final String MULTIPART_FORM_PROPERTY = "com.atlassian.rest.multipart.form";
32  
33      @Override
34      protected InjectableValuesProvider getInjectableValuesProvider(final AbstractResourceMethod method)
35      {
36          // We only return one if we find a MultipartFormParam annotated parameter
37          for (Parameter param : method.getParameters())
38          {
39              for (Annotation annotation : param.getAnnotations())
40              {
41                  if (annotation instanceof MultipartFormParam)
42                  {
43                      // Here we return an object that will parse the form (done by MultipartFormMessageBodyReader when getEntity() is called)
44                      return new InjectableValuesProvider(getInjectables(method))
45                      {
46                          @Override
47                          public Object[] getInjectableValues(final HttpContext context)
48                          {
49                              if (!context.getProperties().containsKey(MULTIPART_FORM_PROPERTY))
50                              {
51                                  context.getProperties().put(MULTIPART_FORM_PROPERTY, context.getRequest().getEntity(
52                                          MultipartForm.class, MultipartForm.class, method.getAnnotations()));
53                              }
54                              return super.getInjectableValues(context);
55                          }
56                      };
57                  }
58              }
59          }
60          return null;
61      }
62  
63      private List<Injectable> getInjectables(AbstractResourceMethod method)
64      {
65          List<Injectable> is = new ArrayList<Injectable>(method.getParameters().size());
66          for (int i = 0; i < method.getParameters().size(); i++)
67          {
68              Parameter parameter = method.getParameters().get(i);
69              Injectable<?> injectable = null;
70              // We don't support entities
71              if (Parameter.Source.ENTITY == parameter.getSource())
72              {
73                  return null;
74              }
75              for (Annotation annotation : parameter.getAnnotations())
76              {
77                  if (annotation instanceof MultipartFormParam)
78                  {
79                      // It's a multipart parameter, we get the injectable for it
80                      injectable = getMultipartFormInjectable(parameter, (MultipartFormParam) annotation);
81                  }
82              }
83              if (injectable == null)
84              {
85                  // This defaults back so that everything else, eg @HeaderParam, @Context etc can get injected
86                  injectable = getInjectableProviderContext().getInjectable(parameter, ComponentScope.PerRequest);
87              }
88              if (injectable == null)
89              {
90                  return null;
91              }
92              is.add(injectable);
93          }
94          return is;
95      }
96  
97      private Injectable<?> getMultipartFormInjectable(final Parameter parameter, final MultipartFormParam annotation)
98      {
99          // FilePart
100         if (parameter.getParameterClass().equals(FilePart.class))
101         {
102             return new AbstractHttpContextInjectable<FilePart>()
103             {
104                 public FilePart getValue(final HttpContext context)
105                 {
106                     return ((MultipartForm) context.getProperties().get(MULTIPART_FORM_PROPERTY)).getFilePart(annotation.value());
107                 }
108             };
109         }
110         // Collection of file parts
111         if (Collection.class.isAssignableFrom(parameter.getParameterClass()))
112         {
113             return new AbstractHttpContextInjectable<Collection<FilePart>>()
114             {
115                 public Collection<FilePart> getValue(final HttpContext context)
116                 {
117                     Collection<FilePart> parts = ((MultipartForm) context.getProperties().get(MULTIPART_FORM_PROPERTY)).getFileParts(annotation.value());
118                     if (parameter.getParameterClass().isAssignableFrom(Collection.class))
119                     {
120                         return parts;
121                     }
122                     else if (parameter.getParameterClass().isAssignableFrom(List.class))
123                     {
124                         return new ArrayList<FilePart>(parts);
125                     }
126                     else if (parameter.getParameterClass().isAssignableFrom(Set.class))
127                     {
128                         return new HashSet<FilePart>(parts);
129                     }
130                     return null;
131                 }
132             };
133         }
134         return null;
135     }
136 }