View Javadoc
1   package com.atlassian.plugin.servlet;
2   
3   import com.atlassian.plugin.Plugin;
4   
5   import javax.servlet.Filter;
6   import javax.servlet.FilterRegistration;
7   import javax.servlet.RequestDispatcher;
8   import javax.servlet.Servlet;
9   import javax.servlet.ServletContext;
10  import javax.servlet.ServletException;
11  import javax.servlet.ServletRegistration;
12  import javax.servlet.SessionCookieConfig;
13  import javax.servlet.SessionTrackingMode;
14  import javax.servlet.descriptor.JspConfigDescriptor;
15  import javax.servlet.http.HttpServlet;
16  import java.io.InputStream;
17  import java.lang.reflect.InvocationTargetException;
18  import java.lang.reflect.Method;
19  import java.net.MalformedURLException;
20  import java.net.URL;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.Enumeration;
24  import java.util.EventListener;
25  import java.util.HashSet;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentMap;
29  
30  /**
31   * A wrapper around servlet context that allows plugin servlets to add
32   * attributes which will not be shared/clobbered by other plugins.
33   */
34  public class PluginServletContextWrapper implements ServletContext {
35      private final ServletModuleManager servletModuleManager;
36      private final Plugin plugin;
37      private final ServletContext context;
38      private final ConcurrentMap<String, Object> attributes;
39      private final Map<String, String> initParams;
40  
41      private final Method methodGetContextPath;
42  
43      public PluginServletContextWrapper(ServletModuleManager servletModuleManager, Plugin plugin, ServletContext context, ConcurrentMap<String, Object> attributes, Map<String, String> initParams) {
44          Method tmpMethod = null;
45          this.servletModuleManager = servletModuleManager;
46          this.plugin = plugin;
47          this.context = context;
48          this.attributes = attributes;
49          this.initParams = initParams;
50  
51          Class<?> cls = context.getClass();
52          try {
53              tmpMethod = cls.getMethod("getContextPath");
54          } catch (NoSuchMethodException e) {
55              // no problem, Servlet 2.4 or earlier found
56          }
57          methodGetContextPath = tmpMethod;
58      }
59  
60      /**
61       * Gets the named attribute. The attribute is first looked for in the local
62       * attribute map, if it is not found there it is looked for in the wrapped
63       * contexts attribute map. If it is not there, null is returned.
64       * <p>
65       * A consequence of this ordering is that servlets may, in their own
66       * context, override but not overwrite attributes from the wrapped context.
67       */
68      @Override
69      public Object getAttribute(String name) {
70          Object attr = attributes.get(name);
71          if (attr == null)
72              attr = context.getAttribute(name);
73  
74          return attr;
75      }
76  
77      /**
78       * @return an enumeration of all the attributes from the wrapped
79       * context as well as the local attributes.
80       */
81      @Override
82      public Enumeration<String> getAttributeNames() {
83          Collection<String> names = new HashSet<>();
84          names.addAll(attributes.keySet());
85          names.addAll(Collections.list(context.getAttributeNames()));
86          return Collections.enumeration(names);
87      }
88  
89      /**
90       * Removes an attribute from the local context. Leaves the wrapped context
91       * completely untouched.
92       */
93      @Override
94      public void removeAttribute(String name) {
95          attributes.remove(name);
96      }
97  
98      /**
99       * Sets an attribute in the local attribute map, leaving the wrapped
100      * context untouched.
101      * <p>
102      * Servlets may use this and the lookup ordering of the
103      * <code>getAttribute()</code> method to effectively override the value
104      * of an attribute in the wrapped servlet context with their own value and
105      * this overridden value will only be seen in the plugins own scope.
106      */
107     @Override
108     public void setAttribute(String name, Object object) {
109         attributes.put(name, object);
110     }
111 
112     /**
113      * @return the init parameter from the servlet module descriptor.
114      */
115     @Override
116     public String getInitParameter(String name) {
117         return initParams.get(name);
118     }
119 
120     /**
121      * @return an enumeration of the init parameters from the servlet module
122      * descriptor.
123      */
124     @Override
125     public Enumeration<String> getInitParameterNames() {
126         return Collections.enumeration(initParams.keySet());
127     }
128 
129     /**
130      * @return the resource from the plugin classloader if it exists, otherwise the
131      * resource is looked up from the wrapped context and returned
132      */
133     @Override
134     public URL getResource(String path) throws MalformedURLException {
135         URL url = plugin.getResource(path);
136         if (url == null) {
137             url = context.getResource(path);
138         }
139         return url;
140     }
141 
142     /**
143      * @return the resource stream from the plugin classloader if it exists, otherwise
144      * the resource stream is attempted to be retrieved from the wrapped context
145      */
146     @Override
147     public InputStream getResourceAsStream(String path) {
148         InputStream in = plugin.getResourceAsStream(path);
149         if (in == null) {
150             in = context.getResourceAsStream(path);
151         }
152         return in;
153     }
154 
155     /**
156      * @return null so that servlet plugins can't escape their box
157      */
158     @Override
159     public ServletContext getContext(String uripath) {
160         return null;
161     }
162 
163     @Override
164     public String getContextPath() {
165 
166         // all this crap to deal with Servlet 2.4 containers better
167         if (methodGetContextPath != null) {
168             try {
169                 return (String) methodGetContextPath.invoke(context);
170             } catch (IllegalAccessException e) {
171                 throw new RuntimeException("Cannot access this method", e);
172             } catch (InvocationTargetException e) {
173                 throw new RuntimeException("Unable to execute getContextPath()", e.getCause());
174             }
175         } else {
176             throw new UnsupportedOperationException("This servlet context doesn't support 2.5 methods");
177         }
178 
179     }
180 
181     //---- All methods below simply delegate to the wrapped servlet context ----
182 
183     @Override
184     public int getMajorVersion() {
185         return context.getMajorVersion();
186     }
187 
188     @Override
189     public String getMimeType(String file) {
190         return context.getMimeType(file);
191     }
192 
193     @Override
194     public int getMinorVersion() {
195         return context.getMinorVersion();
196     }
197 
198     @Override
199     public RequestDispatcher getNamedDispatcher(String name) {
200         return context.getNamedDispatcher(name);
201     }
202 
203     @Override
204     public String getRealPath(String path) {
205         return context.getRealPath(path);
206     }
207 
208     @Override
209     public RequestDispatcher getRequestDispatcher(String path) {
210         return context.getRequestDispatcher(path);
211     }
212 
213     @Override
214     public Set<String> getResourcePaths(String arg0) {
215         return context.getResourcePaths(arg0);
216     }
217 
218     @Override
219     public String getServerInfo() {
220         return context.getServerInfo();
221     }
222 
223     @SuppressWarnings("deprecation")
224     @Override
225     public Servlet getServlet(String name) throws ServletException {
226         return context.getServlet(name);
227     }
228 
229     @Override
230     public String getServletContextName() {
231         return context.getServletContextName();
232     }
233 
234     @SuppressWarnings("deprecation")
235     @Override
236     public Enumeration<String> getServletNames() {
237         return context.getServletNames();
238     }
239 
240     @SuppressWarnings("deprecation")
241     @Override
242     public Enumeration<Servlet> getServlets() {
243         return context.getServlets();
244     }
245 
246     @SuppressWarnings("deprecation")
247     @Override
248     public void log(Exception exception, String msg) {
249         context.log(exception, msg);
250     }
251 
252     @Override
253     public void log(String message, Throwable throwable) {
254         context.log(message, throwable);
255     }
256 
257     @Override
258     public void log(String msg) {
259         context.log(msg);
260     }
261 
262     //---- Servlet 3.0 methods ----
263 
264     @Override
265     public boolean setInitParameter(String name, String value) {
266         if (initParams.containsKey(name)) {
267             return false;
268         }
269         initParams.put(name, value);
270         return true;
271     }
272 
273     @Override
274     public int getEffectiveMajorVersion() {
275         return context.getEffectiveMajorVersion();
276     }
277 
278     @Override
279     public int getEffectiveMinorVersion() {
280         return context.getEffectiveMinorVersion();
281     }
282 
283     @Override
284     public SessionCookieConfig getSessionCookieConfig() {
285         return context.getSessionCookieConfig();
286     }
287 
288     @Override
289     public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {
290         context.setSessionTrackingModes(sessionTrackingModes);
291     }
292 
293     @Override
294     public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
295         return context.getDefaultSessionTrackingModes();
296     }
297 
298     @Override
299     public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
300         return context.getEffectiveSessionTrackingModes();
301     }
302 
303     @Override
304     public JspConfigDescriptor getJspConfigDescriptor() {
305         return context.getJspConfigDescriptor();
306     }
307 
308     @Override
309     public ClassLoader getClassLoader() {
310         return context.getClassLoader();
311     }
312 
313     @Override
314     public void declareRoles(String... roleNames) {
315         context.declareRoles(roleNames);
316     }
317 
318     @Override
319     public ServletRegistration.Dynamic addServlet(String servletName, String className) {
320         servletModuleManager.addServlet(plugin, servletName, className);
321         return null;
322     }
323 
324     @Override
325     public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
326         // atlassian-plugins limitation - HttpServlet only
327         if (!(servlet instanceof HttpServlet)) {
328             throw new IllegalArgumentException("only javax.servlet.http.HttpServlet is supported by atlassian-plugins for javax.servlet.ServletContext#addServlet(String, javax.servlet.Servlet)}");
329         }
330 
331         servletModuleManager.addServlet(plugin, servletName, (HttpServlet) servlet, this);
332         return null;
333     }
334 
335     @Override
336     public ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) {
337         servletModuleManager.addServlet(plugin, servletName, servletClass.getName());
338         return null;
339     }
340 
341     @Override
342     public <T extends Servlet> T createServlet(Class<T> clazz) {
343         throw new UnsupportedOperationException();
344     }
345 
346     @Override
347     public ServletRegistration getServletRegistration(String servletName) {
348         throw new UnsupportedOperationException();
349     }
350 
351     @Override
352     public Map<String, ? extends ServletRegistration> getServletRegistrations() {
353         throw new UnsupportedOperationException();
354     }
355 
356     @Override
357     public FilterRegistration.Dynamic addFilter(String filterName, String className) {
358         throw new UnsupportedOperationException();
359     }
360 
361     @Override
362     public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {
363         throw new UnsupportedOperationException();
364     }
365 
366     @Override
367     public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) {
368         throw new UnsupportedOperationException();
369     }
370 
371     @Override
372     public <T extends Filter> T createFilter(Class<T> clazz) {
373         throw new UnsupportedOperationException();
374     }
375 
376     @Override
377     public FilterRegistration getFilterRegistration(String filterName) {
378         throw new UnsupportedOperationException();
379     }
380 
381     @Override
382     public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
383         throw new UnsupportedOperationException();
384     }
385 
386     @Override
387     public void addListener(String className) {
388         throw new UnsupportedOperationException();
389     }
390 
391     @Override
392     public <T extends EventListener> void addListener(T t) {
393         throw new UnsupportedOperationException();
394     }
395 
396     @Override
397     public void addListener(Class<? extends EventListener> listenerClass) {
398         throw new UnsupportedOperationException();
399     }
400 
401     @Override
402     public <T extends EventListener> T createListener(Class<T> clazz) {
403         throw new UnsupportedOperationException();
404     }
405 
406     //---- Servlet 3.1 methods ----
407 
408     @Override
409     public String getVirtualServerName() {
410         return context.getVirtualServerName();
411     }
412 }