1   package com.atlassian.plugins.rest.module;
2   
3   import com.atlassian.plugin.AutowireCapablePlugin;
4   import com.atlassian.plugin.osgi.factory.OsgiPlugin;
5   import com.atlassian.plugins.rest.common.filter.ExtensionJerseyFilter;
6   import com.google.common.base.Preconditions;
7   import com.sun.jersey.api.core.ResourceConfig;
8   import com.sun.jersey.spi.container.WebApplication;
9   import com.sun.jersey.spi.container.servlet.ServletContainer;
10  import com.sun.jersey.spi.container.servlet.WebConfig;
11  import org.apache.commons.lang.StringUtils;
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   * A delegating servlet that swaps the context class loader with a {@link ChainingClassLoader} that delegates to both
28   * the class loader of the {@link RestModuleDescriptor} and the current context class loader.
29   */
30  class RestDelegatingServletFilter implements Filter
31  {
32      /**
33       * Overriden servlet container to use our own {@link OsgiResourceConfig}.
34       */
35      private final ServletContainer servletContainer;
36      private final ResourceConfigManager resourceConfigManager;
37  
38      RestDelegatingServletFilter(OsgiPlugin plugin, RestApiContext restContextPath)
39      {
40          resourceConfigManager = new ResourceConfigManager(plugin, plugin.getBundle());
41          this.servletContainer = new JerseyOsgiServletContainer(plugin, restContextPath, resourceConfigManager);
42      }
43  
44      public void init(FilterConfig config) throws ServletException
45      {
46          final ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
47          try
48          {
49              Thread.currentThread().setContextClassLoader(new ChainingClassLoader(RestModuleDescriptor.class.getClassLoader(), currentThreadClassLoader));
50              servletContainer.init(config);
51          }
52          finally
53          {
54              Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
55          }
56      }
57  
58      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
59      {
60          final ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
61          try
62          {
63              Thread.currentThread().setContextClassLoader(new ChainingClassLoader(RestModuleDescriptor.class.getClassLoader(), currentThreadClassLoader));
64              servletContainer.doFilter(request, response, chain);
65          }
66          finally
67          {
68              Thread.currentThread().setContextClassLoader(currentThreadClassLoader);
69          }
70      }
71  
72      public void destroy()
73      {
74          servletContainer.destroy();
75          resourceConfigManager.destroy();
76      }
77  
78      /**
79       *
80       */
81      private static class JerseyOsgiServletContainer extends ServletContainer
82      {
83          private final AutowireCapablePlugin plugin;
84          private final RestApiContext restApiContext;
85          private final ResourceConfigManager resourceConfigManager;
86  
87          private static final String PARAM_EXTENSION_FILTER_EXCLUDES = "extension.filter.excludes";
88  
89          public JerseyOsgiServletContainer(AutowireCapablePlugin plugin, RestApiContext restApiContext, final ResourceConfigManager resourceConfigManager)
90          {
91              this.resourceConfigManager = Preconditions.checkNotNull(resourceConfigManager);
92              this.plugin = Preconditions.checkNotNull(plugin);
93              this.restApiContext = Preconditions.checkNotNull(restApiContext);
94          }
95  
96          @Override
97          protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props, WebConfig webConfig) throws ServletException
98          {
99              // get the excludes parameter: support the old param name for Atlassian Gadgets
100             final String deprecatedName = "com.atlassian.plugins.rest.module.filter.ExtensionJerseyFilter" + "#excludes";
101             final String excludeParam = webConfig.getInitParameter(deprecatedName) != null ?
102                     webConfig.getInitParameter(deprecatedName) : webConfig.getInitParameter(PARAM_EXTENSION_FILTER_EXCLUDES);
103 
104             final String[] excludes = StringUtils.split(excludeParam, " ,;");
105 
106             return resourceConfigManager.createResourceConfig(props, excludes, restApiContext.getPackages());
107         }
108 
109         @Override
110         public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
111                 throws IOException, ServletException
112         {
113             // This is overridden so that we can change the base URI, and keep the context path the same
114             String baseUriPath;
115             if (request.getRequestURI().contains(restApiContext.getPathToLatest()))
116             {
117                  baseUriPath = request.getContextPath() + restApiContext.getPathToLatest();
118             }
119             else
120             {
121                 baseUriPath = request.getContextPath() + restApiContext.getPathToVersion();
122             }
123             final UriBuilder absoluteUriBuilder = UriBuilder.fromUri(request.getRequestURL().toString());
124 
125             final URI baseUri = absoluteUriBuilder.replacePath(baseUriPath).path("/").build();
126 
127             final URI requestUri = absoluteUriBuilder.replacePath(request.getRequestURI()).replaceQuery(
128                     request.getQueryString()).build();
129 
130             service(baseUri, requestUri, request, response);
131         }
132 
133         @Override
134         protected void initiate(ResourceConfig resourceConfig, WebApplication webApplication)
135         {
136             webApplication.initiate(resourceConfig, new OsgiComponentProviderFactory(resourceConfig, plugin));
137         }
138     }
139 }