View Javadoc
1   package com.atlassian.plugin.servlet.filter;
2   
3   import com.atlassian.plugin.servlet.PluginHttpRequestWrapper;
4   import com.atlassian.plugin.servlet.descriptors.ServletFilterModuleDescriptor;
5   import com.atlassian.plugin.util.ClassLoaderStack;
6   
7   import javax.servlet.Filter;
8   import javax.servlet.FilterChain;
9   import javax.servlet.FilterConfig;
10  import javax.servlet.ServletException;
11  import javax.servlet.ServletRequest;
12  import javax.servlet.ServletResponse;
13  import javax.servlet.http.HttpServletRequest;
14  import java.io.IOException;
15  
16  /**
17   * We wrap the plugins filter so that we can set some things up before the plugins filter is called. Currently we do
18   * the following:
19   * <ul>
20   * <li>set the Threads classloader to the plugins classloader)</li>
21   * <li>wrap the request so that path info is right for the filters</li>
22   * </ul>
23   *
24   * @since 2.1.0
25   */
26  public class DelegatingPluginFilter implements Filter {
27      private final ServletFilterModuleDescriptor descriptor;
28      private final Filter filter;
29  
30      public DelegatingPluginFilter(ServletFilterModuleDescriptor descriptor) {
31          this.descriptor = descriptor;
32          this.filter = descriptor.getModule();
33      }
34  
35      public void init(FilterConfig filterConfig) throws ServletException {
36          ClassLoaderStack.push(descriptor.getPlugin().getClassLoader());
37          try {
38              filter.init(filterConfig);
39          } finally {
40              ClassLoaderStack.pop();
41          }
42      }
43  
44      public void doFilter(ServletRequest request, ServletResponse response, final FilterChain chain)
45              throws IOException, ServletException {
46          // Cache the class loader in case the chain below disabled the plugin making getClassLoader() inaccessible. This
47          // is a bit dodgy, but it's a concession to the fact that web actions result in plugin disablement. The post
48          // chain push only lasts until we exit back the outer push here.
49          final ClassLoader pluginClassLoader = descriptor.getPlugin().getClassLoader();
50          ClassLoaderStack.push(pluginClassLoader);
51          try {
52              // Reset the classloader during chain execution to prevent plugin's classloader being used for the duration
53              // of the request
54              FilterChain resetContextClassLoaderChain = (servletRequest, servletResponse) -> {
55                  ClassLoaderStack.pop();
56                  try {
57                      chain.doFilter(servletRequest, servletResponse);
58                  } finally {
59                      ClassLoaderStack.push(pluginClassLoader);
60                  }
61              };
62              filter.doFilter(createPluginHttpRequestWrapper((HttpServletRequest) request, descriptor), response, resetContextClassLoaderChain);
63          } finally {
64              ClassLoaderStack.pop();
65          }
66      }
67  
68      private PluginHttpRequestWrapper createPluginHttpRequestWrapper(HttpServletRequest request, ServletFilterModuleDescriptor descriptor) {
69          return request instanceof PluginHttpRequestWrapper ? (PluginHttpRequestWrapper) request
70                  : new PluginHttpRequestWrapper(request, descriptor);
71      }
72  
73      public void destroy() {
74          ClassLoaderStack.push(descriptor.getPlugin().getClassLoader());
75          try {
76              filter.destroy();
77          } finally {
78              ClassLoaderStack.pop();
79          }
80      }
81  
82      public Filter getDelegatingFilter() {
83          return filter;
84      }
85  }