View Javadoc
1   package com.atlassian.plugin.servlet;
2   
3   import com.atlassian.plugin.servlet.descriptors.BaseServletModuleDescriptor;
4   import org.apache.commons.lang.StringUtils;
5   
6   import javax.servlet.http.HttpServletRequest;
7   import javax.servlet.http.HttpServletRequestWrapper;
8   import javax.servlet.http.HttpSession;
9   
10  /**
11   * A request wrapper for requests bound for servlets declared in plugins.  Does the necessary path
12   * munging for requests so that they look like they are
13   * <p>
14   * Also wraps the HttpSession in order to work around the Weblogic Session Attribute serialization problem (see PLUG-515)
15   */
16  public class PluginHttpRequestWrapper extends HttpServletRequestWrapper {
17      private final String basePath;
18      private HttpServletRequest delegate;
19  
20      public PluginHttpRequestWrapper(HttpServletRequest request, BaseServletModuleDescriptor<?> descriptor) {
21          super(request);
22          this.delegate = request;
23          this.basePath = findBasePath(descriptor);
24      }
25  
26      public String getServletPath() {
27          String servletPath = super.getServletPath();
28          if (basePath != null) {
29              servletPath += basePath;
30          }
31          return servletPath;
32      }
33  
34      public String getPathInfo() {
35          String pathInfo = super.getPathInfo();
36          if (pathInfo != null && basePath != null) {
37              if (basePath.equals(pathInfo)) {
38                  return null;
39              } else if (pathInfo.startsWith(basePath)) {
40                  return pathInfo.substring(basePath.length());
41              }
42          }
43          return pathInfo;
44      }
45  
46      /**
47       * A <a href="http://bluxte.net/blog/2006-03/29-40-33.html">commenter</a> based on the
48       * <a href="http://java.sun.com/products/servlet/2.1/html/introduction.fm.html#1499">servlet mapping spec</a>
49       * defined the mapping processing as:
50       *
51       * <ol>
52       * <li>A string beginning with a '/' character and ending with a '/*' postfix is used for path mapping.</li>
53       * <li>A string beginning with a'*.' prefix is used as an extension mapping.</li>
54       * <li>A string containing only the '/' character indicates the "default" servlet of the application. In this
55       * case the servlet path is the request URI minus the context path and the path info is null.</li>
56       * <li>All other strings are used for exact matches only.</li>
57       * </ol>
58       *
59       * To find the base path we're really only interested in the first and last case.  Everything else will just get a
60       * null base path.  So we'll iterate through the list of paths specified and for the ones that match (1) above,
61       * check if the path info returned by the super class matches.  If it does, we return that base path, otherwise we
62       * move onto the next one.
63       */
64      private String findBasePath(BaseServletModuleDescriptor<?> descriptor) {
65          String pathInfo = super.getPathInfo();
66  
67          if (pathInfo != null) {
68              // Exact match
69              for (String basePath : descriptor.getPaths()) {
70                  if (basePath.equals(pathInfo)) {
71                      return basePath;
72                  }
73              }
74  
75              // Prefix match
76              final String[] pathInfoComponents = StringUtils.split(pathInfo, '/');
77              for (String basePath : descriptor.getPaths()) {
78                  if (isPathMapping(basePath)) {
79                      final String mappingRootPath = getMappingRootPath(basePath);
80                      final String[] mappingRootPathComponents = StringUtils.split(mappingRootPath, '/');
81  
82                      if (arrayStartsWith(pathInfoComponents, mappingRootPathComponents)) {
83                          return mappingRootPath;
84                      }
85                  }
86              }
87          }
88          return null;
89      }
90  
91      private static boolean arrayStartsWith(String[] array, String[] prefixArray) {
92          // prefix array cannot be longer than the array.
93          if (prefixArray.length > array.length) {
94              return false;
95          }
96  
97          // Assume the last bit less likely to match.
98          for (int i = prefixArray.length - 1; i >= 0; i--) {
99              if (!prefixArray[i].equals(array[i])) {
100                 return false;
101             }
102         }
103 
104         return true;
105     }
106 
107     private boolean isPathMapping(String path) {
108         return path.startsWith("/") && path.endsWith("/*");
109     }
110 
111     private String getMappingRootPath(String pathMapping) {
112         return pathMapping.substring(0, pathMapping.length() - "/*".length());
113     }
114 
115     @Override
116     public HttpSession getSession() {
117         return this.getSession(true);
118     }
119 
120     @Override
121     public HttpSession getSession(final boolean create) {
122         HttpSession session = delegate.getSession(create);
123         if (session == null) {
124             // The delegate returned a null session - so do we.
125             return null;
126         } else {
127             // Wrap this non-null HttpSession
128             return new PluginHttpSessionWrapper(session);
129         }
130     }
131 }