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.module.ContainerManagedPlugin;
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      private final MessageBodyFactory messageBodyFactory;
53      private final ResourceConfigManager resourceConfigManager;
54  
55      public JerseyEntityHandler(final ContainerManagedPlugin plugin, final Bundle bundle) {
56          resourceConfigManager = new ResourceConfigManager(plugin, bundle);
57          messageBodyFactory = Errors.processWithErrors(new Closure<MessageBodyFactory>() {
58              public MessageBodyFactory f() {
59                  ResourceConfig config = resourceConfigManager.createResourceConfig(Collections.<String, Object>emptyMap(),
60                          new String[0], Collections.<String>emptySet());
61  
62                  IoCComponentProviderFactory provider = new OsgiComponentProviderFactory(config, (ContainerManagedPlugin) plugin);
63                  final InjectableProviderFactory injectableFactory = new InjectableProviderFactory();
64  
65                  ProviderFactory componentProviderFactory = new IoCProviderFactory(injectableFactory, provider);
66  
67                  ProviderServices providerServices = new ProviderServices(
68                          componentProviderFactory,
69                          config.getClasses(),
70                          config.getSingletons());
71  
72                  // Allow injection of features and properties
73                  injectableFactory.add(new ContextInjectableProvider<FeaturesAndProperties>(FeaturesAndProperties.class, config));
74  
75                  injectableFactory.add(new InjectableProvider<Context, Type>() {
76                      public ComponentScope getScope() {
77                          return ComponentScope.Singleton;
78                      }
79  
80                      public Injectable<Injectable> getInjectable(ComponentContext ic, Context a, Type c) {
81                          if (c instanceof ParameterizedType) {
82                              ParameterizedType pt = (ParameterizedType) c;
83                              if (pt.getRawType() == Injectable.class) {
84                                  if (pt.getActualTypeArguments().length == 1) {
85                                      final Injectable<?> i = injectableFactory.getInjectable(
86                                              a.annotationType(),
87                                              ic,
88                                              a,
89                                              pt.getActualTypeArguments()[0],
90                                              ComponentScope.PERREQUEST_UNDEFINED_SINGLETON);
91                                      if (i == null)
92                                          return null;
93                                      return new Injectable<Injectable>() {
94                                          public Injectable getValue() {
95                                              return i;
96                                          }
97                                      };
98                                  }
99                              }
100                         }
101 
102                         return null;
103                     }
104                 });
105 
106                 injectableFactory.configure(providerServices);
107 
108                 // Obtain all context resolvers
109                 final ContextResolverFactory crf = new ContextResolverFactory();
110                 crf.init(providerServices, injectableFactory);
111 
112                 // Obtain all message body readers/writers
113                 final MessageBodyFactory messageBodyFactory = new MessageBodyFactory(providerServices, false);
114 
115                 // Allow injection of message body context
116                 injectableFactory.add(new ContextInjectableProvider<MessageBodyWorkers>(
117                         MessageBodyWorkers.class, messageBodyFactory));
118 
119                 // Injection of Providers
120                 Providers providers = new Providers() {
121                     public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> c, Type t,
122                                                                          Annotation[] as, MediaType m) {
123                         return messageBodyFactory.getMessageBodyReader(c, t, as, m);
124                     }
125 
126                     public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> c, Type t,
127                                                                          Annotation[] as, MediaType m) {
128                         return messageBodyFactory.getMessageBodyWriter(c, t, as, m);
129                     }
130 
131                     public <T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> c) {
132                         throw new IllegalArgumentException("This method is not supported on the client side");
133                     }
134 
135                     public <T> ContextResolver<T> getContextResolver(Class<T> ct, MediaType m) {
136                         return crf.resolve(ct, m);
137                     }
138                 };
139                 injectableFactory.add(
140                         new ContextInjectableProvider<Providers>(
141                                 Providers.class, providers));
142 
143                 // Initiate message body readers/writers
144                 messageBodyFactory.init();
145 
146                 // Inject on all components
147                 Errors.setReportMissingDependentFieldOrMethod(true);
148                 componentProviderFactory.injectOnAllComponents();
149                 componentProviderFactory.injectOnProviderInstances(config.getSingletons());
150 
151                 return messageBodyFactory;
152             }
153         });
154     }
155 
156     /**
157      * package protected constructor exposed for unit tests,
158      *
159      * @param msgBodyFactory
160      * @param resourceConfigMgr
161      * @see JerseyEntityHandler#JerseyEntityHandler(ContainerManagedPlugin, Bundle)
162      */
163     JerseyEntityHandler(MessageBodyFactory msgBodyFactory, ResourceConfigManager resourceConfigMgr) {
164         messageBodyFactory = msgBodyFactory;
165         resourceConfigManager = resourceConfigMgr;
166     }
167 
168     public String marshall(Object entity, MediaType mediaType, Charset charset) throws IOException {
169         Type entityType;
170         if (entity instanceof GenericEntity) {
171             final GenericEntity ge = (GenericEntity) entity;
172             entityType = ge.getType();
173             entity = ge.getEntity();
174         } else {
175             entityType = entity.getClass();
176         }
177         final Class entityClass = entity.getClass();
178 
179         MessageBodyWriter writer = messageBodyFactory.getMessageBodyWriter(entityClass, entityType, new Annotation[0], mediaType);
180         if (writer == null) {
181             throw new RuntimeException("Unable to find a message body writer for " + entityClass);
182         }
183 
184         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
185 
186         writer.writeTo(entity, entityClass, entityType, new Annotation[0], mediaType, new OutBoundHeaders(), outputStream);
187 
188         try {
189             return outputStream.toString(charset.name());
190         } catch (UnsupportedEncodingException e) {
191             throw new RuntimeException("Should never happen as we have already seen the " +
192                     "Charset is supported as we have it as a parameter");
193         }
194     }
195 
196     public <T> T unmarshall(Class<T> entityClass, MediaType mediaType, InputStream entityStream,
197                             Map<String, List<String>> responseHeaders) throws IOException {
198         MessageBodyReader<T> reader = messageBodyFactory.getMessageBodyReader(entityClass, entityClass, new Annotation[0],
199                 mediaType);
200         MultivaluedMap<String, String> headers = new InBoundHeaders();
201         headers.putAll(responseHeaders);
202 
203         return reader.readFrom(entityClass, entityClass, new Annotation[0], mediaType, headers, entityStream);
204     }
205 
206     public void destroy() {
207         resourceConfigManager.destroy();
208     }
209 
210     private static class ContextInjectableProvider<T> extends
211             SingletonTypeInjectableProvider<Context, T> {
212 
213         ContextInjectableProvider(Type type, T instance) {
214             super(type, instance);
215         }
216     }
217 }