View Javadoc

1   package com.atlassian.plugins.rest.module.jersey;
2   
3   import java.io.ByteArrayOutputStream;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.io.UnsupportedEncodingException;
7   import java.lang.annotation.Annotation;
8   import java.lang.reflect.ParameterizedType;
9   import java.lang.reflect.Type;
10  import java.nio.charset.Charset;
11  import java.util.Collections;
12  import java.util.List;
13  import java.util.Map;
14  
15  import javax.ws.rs.core.Context;
16  import javax.ws.rs.core.GenericEntity;
17  import javax.ws.rs.core.MediaType;
18  import javax.ws.rs.core.MultivaluedMap;
19  import javax.ws.rs.ext.ContextResolver;
20  import javax.ws.rs.ext.ExceptionMapper;
21  import javax.ws.rs.ext.MessageBodyReader;
22  import javax.ws.rs.ext.MessageBodyWriter;
23  import javax.ws.rs.ext.Providers;
24  
25  import com.atlassian.plugin.AutowireCapablePlugin;
26  import com.atlassian.plugins.rest.module.OsgiComponentProviderFactory;
27  import com.atlassian.plugins.rest.module.ResourceConfigManager;
28  
29  import com.sun.jersey.api.core.ResourceConfig;
30  import com.sun.jersey.core.header.InBoundHeaders;
31  import com.sun.jersey.core.header.OutBoundHeaders;
32  import com.sun.jersey.core.spi.component.ComponentContext;
33  import com.sun.jersey.core.spi.component.ComponentScope;
34  import com.sun.jersey.core.spi.component.ProviderFactory;
35  import com.sun.jersey.core.spi.component.ProviderServices;
36  import com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory;
37  import com.sun.jersey.core.spi.component.ioc.IoCProviderFactory;
38  import com.sun.jersey.core.spi.factory.ContextResolverFactory;
39  import com.sun.jersey.core.spi.factory.InjectableProviderFactory;
40  import com.sun.jersey.core.spi.factory.MessageBodyFactory;
41  import com.sun.jersey.core.util.FeaturesAndProperties;
42  import com.sun.jersey.spi.MessageBodyWorkers;
43  import com.sun.jersey.spi.inject.Errors;
44  import com.sun.jersey.spi.inject.Injectable;
45  import com.sun.jersey.spi.inject.InjectableProvider;
46  import com.sun.jersey.spi.inject.Errors.Closure;
47  import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
48  
49  import org.osgi.framework.Bundle;
50  
51  public class JerseyEntityHandler
52  {
53      private final MessageBodyFactory messageBodyFactory;
54      private final ResourceConfigManager resourceConfigManager;
55  
56      public JerseyEntityHandler(final AutowireCapablePlugin plugin, final Bundle bundle)
57      {
58          resourceConfigManager = new ResourceConfigManager(plugin, bundle);
59          messageBodyFactory = Errors.processWithErrors(new Closure<MessageBodyFactory>()
60          {
61              public MessageBodyFactory f()
62              {
63                  ResourceConfig config = resourceConfigManager.createResourceConfig(Collections.<String, Object>emptyMap(),
64                          new String[0], Collections.<String>emptySet());
65  
66                  IoCComponentProviderFactory provider = new OsgiComponentProviderFactory(config, plugin);
67                  final InjectableProviderFactory injectableFactory = new InjectableProviderFactory();
68  
69                  ProviderFactory componentProviderFactory = new IoCProviderFactory(injectableFactory, provider);
70  
71                  ProviderServices providerServices = new ProviderServices(
72                          componentProviderFactory,
73                          config.getClasses(),
74                          config.getSingletons());
75  
76                  // Allow injection of features and properties
77                  injectableFactory.add(new ContextInjectableProvider<FeaturesAndProperties>(FeaturesAndProperties.class, config));
78  
79                  injectableFactory.add(new InjectableProvider<Context, Type>() {
80                      public ComponentScope getScope() {
81                          return ComponentScope.Singleton;
82                      }
83  
84                      public Injectable<Injectable> getInjectable(ComponentContext ic, Context a, Type c) {
85                          if (c instanceof ParameterizedType) {
86                              ParameterizedType pt = (ParameterizedType)c;
87                              if (pt.getRawType() == Injectable.class) {
88                                  if (pt.getActualTypeArguments().length == 1) {
89                                      final Injectable<?> i = injectableFactory.getInjectable(
90                                              a.annotationType(),
91                                              ic,
92                                              a,
93                                              pt.getActualTypeArguments()[0],
94                                              ComponentScope.PERREQUEST_UNDEFINED_SINGLETON);
95                                      if (i == null)
96                                          return null;
97                                      return new Injectable<Injectable>() {
98                                          public Injectable getValue() {
99                                              return i;
100                                         }
101                                     };
102                                 }
103                             }
104                         }
105 
106                         return null;
107                     }
108                 });
109 
110                 injectableFactory.configure(providerServices);
111 
112                 // Obtain all context resolvers
113                 final ContextResolverFactory crf = new ContextResolverFactory();
114                 crf.init(providerServices, injectableFactory);
115 
116                 // Obtain all message body readers/writers
117                 final MessageBodyFactory messageBodyFactory = new MessageBodyFactory(providerServices, false);
118                 
119                 // Allow injection of message body context
120                 injectableFactory.add(new ContextInjectableProvider<MessageBodyWorkers>(
121                         MessageBodyWorkers.class, messageBodyFactory));
122 
123                 // Injection of Providers
124                 Providers providers = new Providers()
125                 {
126                     public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> c, Type t,
127                                                                          Annotation[] as, MediaType m)
128                     {
129                         return messageBodyFactory.getMessageBodyReader(c, t, as, m);
130                     }
131 
132                     public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> c, Type t,
133                                                                          Annotation[] as, MediaType m)
134                     {
135                         return messageBodyFactory.getMessageBodyWriter(c, t, as, m);
136                     }
137 
138                     public <T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> c)
139                     {
140                         throw new IllegalArgumentException("This method is not supported on the client side");
141                     }
142 
143                     public <T> ContextResolver<T> getContextResolver(Class<T> ct, MediaType m)
144                     {
145                         return crf.resolve(ct, m);
146                     }
147                 };
148                 injectableFactory.add(
149                         new ContextInjectableProvider<Providers>(
150                                 Providers.class, providers));
151 
152                 // Initiate message body readers/writers
153                 messageBodyFactory.init();
154 
155                 // Inject on all components
156                 Errors.setReportMissingDependentFieldOrMethod(true);
157                 componentProviderFactory.injectOnAllComponents();
158                 componentProviderFactory.injectOnProviderInstances(config.getSingletons());
159                 
160                 return messageBodyFactory;
161             }
162         });
163     }
164     
165     /**
166      * package protected constructor exposed for unit tests,
167      * @see JerseyEntityHandler(AutowireCapablePlugin plugin, Bundle bundle)
168      * @param msgBodyFactory
169      * @param resourceConfigMgr
170      */
171     JerseyEntityHandler(MessageBodyFactory msgBodyFactory, ResourceConfigManager resourceConfigMgr)
172     {
173     	messageBodyFactory = msgBodyFactory;
174     	resourceConfigManager = resourceConfigMgr;
175     }
176 
177     public String marshall(Object entity, MediaType mediaType, Charset charset) throws IOException
178     {
179         Type entityType;
180         if (entity instanceof GenericEntity)
181         {
182             final GenericEntity ge = (GenericEntity) entity;
183             entityType = ge.getType();
184             entity = ge.getEntity();
185         }
186         else
187         {
188             entityType = entity.getClass();
189         }
190         final Class entityClass = entity.getClass();
191 
192         MessageBodyWriter writer = messageBodyFactory.getMessageBodyWriter(entityClass, entityType, new Annotation[0], mediaType);
193         if (writer == null)
194         {
195             throw new RuntimeException("Unable to find a message body writer for " + entityClass);
196         }
197 
198         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
199         
200         writer.writeTo(entity, entityClass, entityType, new Annotation[0], mediaType, new OutBoundHeaders(), outputStream);
201 
202         try
203         {
204             return outputStream.toString(charset.name());
205         }
206         catch (UnsupportedEncodingException e)
207         {
208             throw new RuntimeException("Should never happen as we have already seen the " +
209                     "Charset is supported as we have it as a parameter");
210         }
211     }
212 
213     public <T> T unmarshall(Class<T> entityClass, MediaType mediaType, InputStream entityStream,
214                             Map<String, List<String>> responseHeaders) throws IOException
215     {
216         MessageBodyReader<T> reader = messageBodyFactory.getMessageBodyReader(entityClass, entityClass, new Annotation[0],
217                 mediaType);
218         MultivaluedMap<String, String> headers = new InBoundHeaders();
219         headers.putAll(responseHeaders);
220 
221         return reader.readFrom(entityClass, entityClass, new Annotation[0], mediaType, headers, entityStream);
222     }
223 
224     public void destroy()
225     {
226         resourceConfigManager.destroy();
227     }
228 
229     private static class ContextInjectableProvider<T> extends
230             SingletonTypeInjectableProvider<Context, T>
231     {
232 
233         ContextInjectableProvider(Type type, T instance)
234         {
235             super(type, instance);
236         }
237     }
238 }