1 package com.atlassian.plugins.rest.module;
2
3 import com.atlassian.plugin.osgi.factory.OsgiPlugin;
4 import com.atlassian.plugins.rest.common.expand.jersey.ExpandResponseFilter;
5 import com.atlassian.plugins.rest.common.expand.resolver.ChainingEntityExpanderResolver;
6 import com.atlassian.plugins.rest.common.expand.resolver.CollectionEntityExpanderResolver;
7 import com.atlassian.plugins.rest.common.expand.resolver.EntityExpanderResolver;
8 import com.atlassian.plugins.rest.common.expand.resolver.ExpandConstraintEntityExpanderResolver;
9 import com.atlassian.plugins.rest.common.expand.resolver.ListWrapperEntityExpanderResolver;
10 import com.atlassian.plugins.rest.common.expand.resolver.IdentityEntityExpanderResolver;
11 import com.atlassian.plugins.rest.module.expand.resolver.OsgiPluginEntityExpanderResolver;
12 import com.atlassian.plugins.rest.module.filter.ExtensionJerseyFilter;
13 import com.atlassian.plugins.rest.module.filter.AcceptHeaderJerseyMvcFilter;
14 import com.atlassian.plugins.rest.module.json.JsonWithPaddingResponseFilter;
15 import com.google.common.base.Preconditions;
16 import com.google.common.collect.Lists;
17 import com.sun.jersey.api.core.ResourceConfig;
18 import com.sun.jersey.spi.container.ResourceFilterFactory;
19 import com.sun.jersey.spi.container.WebApplication;
20 import com.sun.jersey.spi.container.servlet.ServletContainer;
21 import com.sun.jersey.spi.container.servlet.WebConfig;
22 import com.sun.jersey.spi.inject.InjectableProvider;
23 import com.sun.jersey.spi.template.TemplateProcessor;
24 import org.apache.commons.lang.StringUtils;
25 import org.osgi.framework.BundleContext;
26 import org.osgi.framework.ServiceReference;
27
28 import javax.servlet.Filter;
29 import javax.servlet.FilterChain;
30 import javax.servlet.FilterConfig;
31 import javax.servlet.ServletException;
32 import javax.servlet.ServletRequest;
33 import javax.servlet.ServletResponse;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36 import javax.ws.rs.core.UriBuilder;
37 import java.io.IOException;
38 import java.net.URI;
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.Map;
43
44
45
46
47
48 class RestDelegatingServletFilter implements Filter
49 {
50
51
52
53 private final ServletContainer servletContainer;
54
55 private OsgiServiceAccessor<ResourceFilterFactory> resourceFilterFactories;
56 private OsgiServiceAccessor<InjectableProvider> injectableProviders;
57 private OsgiServiceAccessor<TemplateProcessor> templateProcessors;
58
59 RestDelegatingServletFilter(OsgiPlugin plugin, RestApiContext restContextPath)
60 {
61 final BundleContext bundleContext = Preconditions.checkNotNull(plugin).getBundle().getBundleContext();
62
63
64 this.resourceFilterFactories = new OsgiServiceAccessor<ResourceFilterFactory>(ResourceFilterFactory.class, bundleContext, new OsgiFactory<ResourceFilterFactory>()
65 {
66 public ResourceFilterFactory getInstance(BundleContext bundleContext, ServiceReference serviceReference)
67 {
68 return new OsgiServiceReferenceResourceFilterFactory(bundleContext, serviceReference);
69 }
70 });
71
72
73 this.injectableProviders = new OsgiServiceAccessor<InjectableProvider>(InjectableProvider.class, bundleContext, new OsgiFactory<InjectableProvider>()
74 {
75 public InjectableProvider getInstance(BundleContext bundleContext, ServiceReference serviceReference)
76 {
77 return (InjectableProvider) bundleContext.getService(serviceReference);
78 }
79 });
80
81 this.templateProcessors = new OsgiServiceAccessor<TemplateProcessor>(TemplateProcessor.class, bundleContext, new OsgiFactory<TemplateProcessor>()
82 {
83 public TemplateProcessor getInstance(BundleContext bundleContext, ServiceReference serviceReference)
84 {
85 return (TemplateProcessor) bundleContext.getService(serviceReference);
86 }
87 });
88
89 this.servletContainer = new JerseyOsgiServletContainer(plugin, restContextPath, resourceFilterFactories, injectableProviders, templateProcessors);
90 }
91
92 public void init(FilterConfig config) throws ServletException
93 {
94 final ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
95 try
96 {
97 Thread.currentThread().setContextClassLoader(new ChainingClassLoader(RestModuleDescriptor.class.getClassLoader(), currentThreadClassLoader));
98 servletContainer.init(config);
99 }
100 finally
101 {
102 Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
103 }
104 }
105
106 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
107 {
108 final ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
109 try
110 {
111 Thread.currentThread().setContextClassLoader(new ChainingClassLoader(RestModuleDescriptor.class.getClassLoader(), currentThreadClassLoader));
112 servletContainer.doFilter(request, response, chain);
113 }
114 finally
115 {
116 Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
117 }
118 }
119
120 public void destroy()
121 {
122 servletContainer.destroy();
123 resourceFilterFactories.release();
124 injectableProviders.release();
125 templateProcessors.release();
126 }
127
128
129
130
131 private static class JerseyOsgiServletContainer extends ServletContainer
132 {
133 private final OsgiPlugin plugin;
134 private final RestApiContext restApiContext;
135 private final OsgiServiceAccessor<ResourceFilterFactory> resourceFilterFactories;
136 private final OsgiServiceAccessor<InjectableProvider> injectableProviders;
137 private final OsgiServiceAccessor<TemplateProcessor> templateProcessors;
138
139 public JerseyOsgiServletContainer(OsgiPlugin plugin, RestApiContext restApiContext,
140 OsgiServiceAccessor<ResourceFilterFactory> resourceFilterFactories,
141 OsgiServiceAccessor<InjectableProvider> injectableProviders,
142 OsgiServiceAccessor<TemplateProcessor> templateProcessors)
143 {
144 this.plugin = Preconditions.checkNotNull(plugin);
145 this.restApiContext = Preconditions.checkNotNull(restApiContext);
146 this.resourceFilterFactories = Preconditions.checkNotNull(resourceFilterFactories);
147 this.injectableProviders = Preconditions.checkNotNull(injectableProviders);
148 this.templateProcessors = Preconditions.checkNotNull(templateProcessors);
149 }
150
151 @Override
152 protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props, WebConfig webConfig) throws ServletException
153 {
154
155 final String[] excludes = StringUtils.split(webConfig.getInitParameter(ExtensionJerseyFilter.class.getName() + "#excludes"), " ,;");
156 final Collection<String> excludesCollection = excludes != null ? Arrays.asList(excludes) : Collections.<String>emptyList();
157
158 final EntityExpanderResolver expanderResolver = new ChainingEntityExpanderResolver(Lists.<EntityExpanderResolver>newArrayList(
159 new OsgiPluginEntityExpanderResolver(plugin),
160 new CollectionEntityExpanderResolver(),
161 new ListWrapperEntityExpanderResolver(),
162 new ExpandConstraintEntityExpanderResolver(),
163 new IdentityEntityExpanderResolver()
164 ));
165
166 final Collection<Object> providers = Lists.newLinkedList();
167 providers.addAll(injectableProviders.get());
168 providers.addAll(templateProcessors.get());
169
170 return new OsgiResourceConfig(plugin.getBundle(),
171 Lists.newArrayList(new ExtensionJerseyFilter(excludesCollection), new AcceptHeaderJerseyMvcFilter()),
172 Lists.newArrayList(new JsonWithPaddingResponseFilter(), new ExpandResponseFilter(expanderResolver)),
173 resourceFilterFactories.get(),
174 providers);
175 }
176
177 @Override
178 public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
179 throws IOException, ServletException
180 {
181
182 String baseUriPath;
183 if (request.getServletPath().startsWith(restApiContext.getPathToLatest()))
184 {
185 baseUriPath = request.getContextPath() + restApiContext.getPathToLatest();
186 }
187 else
188 {
189 baseUriPath = request.getContextPath() + restApiContext.getPathToVersion();
190 }
191 final UriBuilder absoluteUriBuilder = UriBuilder.fromUri(request.getRequestURL().toString());
192
193 final URI baseUri = absoluteUriBuilder.replacePath(baseUriPath).path("/").build();
194
195 final URI requestUri = absoluteUriBuilder.replacePath(request.getRequestURI()).replaceQuery(
196 request.getQueryString()).build();
197
198 service(baseUri, requestUri, request, response);
199 }
200
201 @Override
202 protected void initiate(ResourceConfig resourceConfig, WebApplication webApplication)
203 {
204 webApplication.initiate(resourceConfig, new OsgiComponentProviderFactory(resourceConfig, plugin));
205 }
206 }
207 }