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
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
113 final ContextResolverFactory crf = new ContextResolverFactory();
114 crf.init(providerServices, injectableFactory);
115
116
117 final MessageBodyFactory messageBodyFactory = new MessageBodyFactory(providerServices, false);
118
119
120 injectableFactory.add(new ContextInjectableProvider<MessageBodyWorkers>(
121 MessageBodyWorkers.class, messageBodyFactory));
122
123
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
153 messageBodyFactory.init();
154
155
156 Errors.setReportMissingDependentFieldOrMethod(true);
157 componentProviderFactory.injectOnAllComponents();
158 componentProviderFactory.injectOnProviderInstances(config.getSingletons());
159
160 return messageBodyFactory;
161 }
162 });
163 }
164
165
166
167
168
169
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 }