1 package com.atlassian.plugins.rest.module;
2
3 import com.atlassian.plugin.osgi.factory.OsgiPlugin;
4 import com.google.common.base.Preconditions;
5 import com.sun.jersey.api.core.DefaultResourceConfig;
6 import com.sun.jersey.api.core.ResourceConfig;
7 import com.sun.jersey.spi.container.WebApplication;
8 import com.sun.jersey.spi.container.servlet.ServletContainer;
9 import com.sun.jersey.spi.container.servlet.WebConfig;
10 import org.apache.commons.lang.StringUtils;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import javax.servlet.Filter;
15 import javax.servlet.FilterChain;
16 import javax.servlet.FilterConfig;
17 import javax.servlet.ServletException;
18 import javax.servlet.ServletRequest;
19 import javax.servlet.ServletResponse;
20 import javax.servlet.http.HttpServletRequest;
21 import javax.servlet.http.HttpServletResponse;
22 import javax.ws.rs.core.UriBuilder;
23 import java.io.IOException;
24 import java.net.URI;
25 import java.util.Map;
26
27
28
29
30
31 class RestDelegatingServletFilter implements Filter {
32 private static final Logger log = LoggerFactory.getLogger(RestDelegatingServletFilter.class);
33
34
35
36 private static final Slf4jBridge.Helper SLF4J_BRIDGE = Slf4jBridge.createHelper();
37
38
39
40
41 private final ServletContainer servletContainer;
42 private final ResourceConfigManager resourceConfigManager;
43
44
45
46
47
48
49
50
51
52
53 private volatile ClassLoader chainingClassLoader;
54
55 RestDelegatingServletFilter(OsgiPlugin plugin, RestApiContext restContextPath) {
56 resourceConfigManager = new ResourceConfigManager(plugin, plugin.getBundle());
57 this.servletContainer = new JerseyOsgiServletContainer(plugin, restContextPath, resourceConfigManager);
58 }
59
60 public void init(FilterConfig config) throws ServletException {
61 initChainingClassLoader();
62 initServletContainer(config);
63 }
64
65 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
66 ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
67 Thread.currentThread().setContextClassLoader(chainingClassLoader);
68 try {
69 servletContainer.doFilter(request, response, chain);
70 } finally {
71 Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
72 }
73 }
74
75 public void destroy() {
76 destroyServletContainer();
77 resourceConfigManager.destroy();
78 }
79
80 private void initChainingClassLoader() {
81 chainingClassLoader = new ChainingClassLoader(RestModuleDescriptor.class.getClassLoader(), Thread.currentThread().getContextClassLoader());
82 }
83
84 private void initServletContainer(FilterConfig config) throws ServletException {
85 ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
86 Thread.currentThread().setContextClassLoader(chainingClassLoader);
87 try {
88 SLF4J_BRIDGE.install();
89 servletContainer.init(config);
90 } finally {
91 Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
92 }
93 }
94
95 private void destroyServletContainer() {
96 ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
97 Thread.currentThread().setContextClassLoader(chainingClassLoader);
98 try {
99 servletContainer.destroy();
100 SLF4J_BRIDGE.uninstall();
101 } finally {
102 Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
103 }
104 }
105
106
107
108
109 private static class JerseyOsgiServletContainer extends ServletContainer {
110 private final OsgiPlugin plugin;
111 private final RestApiContext restApiContext;
112 private final ResourceConfigManager resourceConfigManager;
113
114 private static final String PARAM_EXTENSION_FILTER_EXCLUDES = "extension.filter.excludes";
115
116 public JerseyOsgiServletContainer(OsgiPlugin plugin, RestApiContext restApiContext, final ResourceConfigManager resourceConfigManager) {
117 this.resourceConfigManager = Preconditions.checkNotNull(resourceConfigManager);
118 this.plugin = Preconditions.checkNotNull(plugin);
119 this.restApiContext = Preconditions.checkNotNull(restApiContext);
120 }
121
122 @Override
123 protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props, WebConfig webConfig) throws ServletException {
124
125 final String deprecatedName = "com.atlassian.plugins.rest.module.filter.ExtensionJerseyFilter" + "#excludes";
126 final String excludeParam = webConfig.getInitParameter(deprecatedName) != null ?
127 webConfig.getInitParameter(deprecatedName) : webConfig.getInitParameter(PARAM_EXTENSION_FILTER_EXCLUDES);
128
129 final String[] excludes = StringUtils.split(excludeParam, " ,;");
130
131 final DefaultResourceConfig resourceConfig = resourceConfigManager.createResourceConfig(props, excludes, restApiContext.getPackages());
132
133 restApiContext.setConfig(resourceConfig);
134
135 return resourceConfig;
136 }
137
138 @Override
139 public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
140 throws IOException, ServletException {
141
142 String baseUriPath;
143 if (request.getRequestURI().contains(restApiContext.getPathToLatest())) {
144 baseUriPath = request.getContextPath() + restApiContext.getPathToLatest();
145
146
147 log.debug("Setting base uri for REST to 'latest'");
148 log.debug("Incoming URI : " + request.getRequestURI());
149 } else {
150 baseUriPath = request.getContextPath() + restApiContext.getPathToVersion();
151 }
152 final UriBuilder absoluteUriBuilder = UriBuilder.fromUri(request.getRequestURL().toString());
153
154 final URI baseUri = absoluteUriBuilder.replacePath(baseUriPath).path("/").build();
155
156 final URI requestUri = absoluteUriBuilder.replacePath(request.getRequestURI()).replaceQuery(
157 request.getQueryString()).build();
158
159 service(baseUri, requestUri, request, response);
160 }
161
162 @Override
163 protected void initiate(ResourceConfig resourceConfig, WebApplication webApplication) {
164 webApplication.initiate(resourceConfig, new OsgiComponentProviderFactory(resourceConfig, plugin));
165 }
166 }
167 }