1 package com.atlassian.plugins.rest.module.jersey;
2
3 import com.atlassian.plugin.AutowireCapablePlugin;
4 import com.atlassian.plugins.rest.module.OsgiComponentProviderFactory;
5 import com.atlassian.plugins.rest.module.ResourceConfigManager;
6 import com.sun.jersey.api.core.ResourceConfig;
7 import com.sun.jersey.core.header.InBoundHeaders;
8 import com.sun.jersey.core.header.OutBoundHeaders;
9 import com.sun.jersey.core.spi.component.ProviderFactory;
10 import com.sun.jersey.core.spi.component.ProviderServices;
11 import com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory;
12 import com.sun.jersey.core.spi.component.ioc.IoCProviderFactory;
13 import com.sun.jersey.core.spi.factory.ContextResolverFactory;
14 import com.sun.jersey.core.spi.factory.InjectableProviderFactory;
15 import com.sun.jersey.core.spi.factory.MessageBodyFactory;
16 import com.sun.jersey.spi.MessageBodyWorkers;
17 import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
18 import org.osgi.framework.Bundle;
19
20 import javax.ws.rs.core.Context;
21 import javax.ws.rs.core.GenericEntity;
22 import javax.ws.rs.core.MediaType;
23 import javax.ws.rs.core.MultivaluedMap;
24 import javax.ws.rs.ext.ContextResolver;
25 import javax.ws.rs.ext.ExceptionMapper;
26 import javax.ws.rs.ext.MessageBodyReader;
27 import javax.ws.rs.ext.MessageBodyWriter;
28 import javax.ws.rs.ext.Providers;
29 import java.io.ByteArrayOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.UnsupportedEncodingException;
33 import java.lang.annotation.Annotation;
34 import java.lang.reflect.Type;
35 import java.nio.charset.Charset;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Map;
39
40 public class JerseyEntityHandler
41 {
42 private final MessageBodyFactory messageBodyFactory;
43 private final ResourceConfigManager resourceConfigManager;
44
45 public JerseyEntityHandler(AutowireCapablePlugin plugin, Bundle bundle)
46 {
47 resourceConfigManager = new ResourceConfigManager(plugin, bundle);
48 ResourceConfig config = resourceConfigManager.createResourceConfig(Collections.<String, Object>emptyMap(),
49 new String[0], Collections.<String>emptySet());
50
51 IoCComponentProviderFactory provider = new OsgiComponentProviderFactory(config, plugin);
52 InjectableProviderFactory injectableFactory = new InjectableProviderFactory();
53
54 ProviderFactory componentProviderFactory = new IoCProviderFactory(injectableFactory, provider);
55
56 ProviderServices providerServices = new ProviderServices(
57 injectableFactory,
58 componentProviderFactory,
59 config.getClasses(),
60 config.getSingletons());
61
62 injectableFactory.configure(providerServices);
63
64
65 final ContextResolverFactory crf = new ContextResolverFactory(providerServices,
66 injectableFactory);
67
68
69 messageBodyFactory = new MessageBodyFactory(providerServices);
70
71 injectableFactory.add(new ContextInjectableProvider<MessageBodyWorkers>(
72 MessageBodyWorkers.class, messageBodyFactory));
73
74
75 Providers providers = new Providers()
76 {
77 public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> c, Type t,
78 Annotation[] as, MediaType m)
79 {
80 return messageBodyFactory.getMessageBodyReader(c, t, as, m);
81 }
82
83 public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> c, Type t,
84 Annotation[] as, MediaType m)
85 {
86 return messageBodyFactory.getMessageBodyWriter(c, t, as, m);
87 }
88
89 public <T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> c)
90 {
91 throw new IllegalArgumentException("This method is not supported on the client side");
92 }
93
94 public <T> ContextResolver<T> getContextResolver(Class<T> ct, MediaType m)
95 {
96 return crf.resolve(ct, m);
97 }
98 };
99 injectableFactory.add(
100 new ContextInjectableProvider<Providers>(
101 Providers.class, providers));
102
103
104 messageBodyFactory.init();
105
106
107 componentProviderFactory.injectOnAllComponents();
108 componentProviderFactory.injectOnProviderInstances(config.getSingletons());
109 }
110
111 public String marshall(Object entity, MediaType mediaType, Charset charset) throws IOException
112 {
113 Type entityType;
114 if (entity instanceof GenericEntity)
115 {
116 final GenericEntity ge = (GenericEntity) entity;
117 entityType = ge.getType();
118 entity = ge.getEntity();
119 }
120 else
121 {
122 entityType = entity.getClass();
123 }
124 final Class entityClass = entity.getClass();
125
126 MessageBodyWriter writer = messageBodyFactory.getMessageBodyWriter(entityClass, entityType, new Annotation[0], mediaType);
127
128 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
129 writer.writeTo(entity, entityClass, entityType, new Annotation[0], mediaType, new OutBoundHeaders(), outputStream);
130
131 return getRequestBody(outputStream, charset);
132 }
133
134 public <T> T unmarshall(Class<T> entityClass, MediaType mediaType, InputStream entityStream,
135 Map<String, List<String>> responseHeaders) throws IOException
136 {
137 MessageBodyReader<T> reader = messageBodyFactory.getMessageBodyReader(entityClass, entityClass, new Annotation[0],
138 mediaType);
139 MultivaluedMap<String, String> headers = new InBoundHeaders();
140 headers.putAll(responseHeaders);
141
142 return reader.readFrom(entityClass, entityClass, new Annotation[0], mediaType, headers, entityStream);
143 }
144
145
146
147
148
149
150
151
152
153
154 private String getRequestBody(final ByteArrayOutputStream outputStream, Charset charset)
155 {
156 byte[] bytes = outputStream.toByteArray();
157 byte last = 0;
158 int bodyStartIndex = -1;
159 for (int i = 0; i < bytes.length; i++)
160 {
161
162 if (last == 10 && bytes[i] == 10)
163 {
164 bodyStartIndex = i + 1;
165 break;
166 }
167 last = bytes[i];
168 }
169 if (bodyStartIndex == -1)
170 {
171
172 bodyStartIndex = 0;
173 }
174
175 try
176 {
177 return new String(bytes, bodyStartIndex, bytes.length - bodyStartIndex, charset.name());
178 }
179 catch (UnsupportedEncodingException e)
180 {
181 throw new RuntimeException("Should never happen. We only use the String(byte[], int, int, String) method " +
182 "to preserve Java 5 compatibility.");
183 }
184 }
185
186 public void destroy()
187 {
188 resourceConfigManager.destroy();
189 }
190
191 private static class ContextInjectableProvider<T> extends
192 SingletonTypeInjectableProvider<Context, T>
193 {
194
195 ContextInjectableProvider(Type type, T instance)
196 {
197 super(type, instance);
198 }
199 }
200
201
202 }