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
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
109 final ContextResolverFactory crf = new ContextResolverFactory();
110 crf.init(providerServices, injectableFactory);
111
112
113 final MessageBodyFactory messageBodyFactory = new MessageBodyFactory(providerServices, false);
114
115
116 injectableFactory.add(new ContextInjectableProvider<MessageBodyWorkers>(
117 MessageBodyWorkers.class, messageBodyFactory));
118
119
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
144 messageBodyFactory.init();
145
146
147 Errors.setReportMissingDependentFieldOrMethod(true);
148 componentProviderFactory.injectOnAllComponents();
149 componentProviderFactory.injectOnProviderInstances(config.getSingletons());
150
151 return messageBodyFactory;
152 }
153 });
154 }
155
156
157
158
159
160
161
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 }