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