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       * <p>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.</p>
64       *
65       * <p>A consequence of this ordering is that servlets may, in their own
66       * context, override but not overwrite attributes from the wrapped context.</p>
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<String>();
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       * <p>Sets an attribute in the local attribute map, leaving the wrapped
100      * context untouched.</p>
101      *
102      * <p>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.</p>
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     @Override
224     public Servlet getServlet(String name) throws ServletException {
225         return context.getServlet(name);
226     }
227 
228     @Override
229     public String getServletContextName() {
230         return context.getServletContextName();
231     }
232 
233     @Override
234     public Enumeration<String> getServletNames() {
235         return context.getServletNames();
236     }
237 
238     @Override
239     public Enumeration<Servlet> getServlets() {
240         return context.getServlets();
241     }
242 
243     @Override
244     public void log(Exception exception, String msg) {
245         context.log(exception, msg);
246     }
247 
248     @Override
249     public void log(String message, Throwable throwable) {
250         context.log(message, throwable);
251     }
252 
253     @Override
254     public void log(String msg) {
255         context.log(msg);
256     }
257 
258     //---- Servlet 3.0 methods ----
259 
260     @Override
261     public boolean setInitParameter(String name, String value) {
262         if (initParams.containsKey(name)) {
263             return false;
264         }
265         initParams.put(name, value);
266         return true;
267     }
268 
269     @Override
270     public int getEffectiveMajorVersion() {
271         return context.getEffectiveMajorVersion();
272     }
273 
274     @Override
275     public int getEffectiveMinorVersion() {
276         return context.getEffectiveMinorVersion();
277     }
278 
279     @Override
280     public SessionCookieConfig getSessionCookieConfig() {
281         return context.getSessionCookieConfig();
282     }
283 
284     @Override
285     public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {
286         context.setSessionTrackingModes(sessionTrackingModes);
287     }
288 
289     @Override
290     public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
291         return context.getDefaultSessionTrackingModes();
292     }
293 
294     @Override
295     public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
296         return context.getEffectiveSessionTrackingModes();
297     }
298 
299     @Override
300     public JspConfigDescriptor getJspConfigDescriptor() {
301         return context.getJspConfigDescriptor();
302     }
303 
304     @Override
305     public ClassLoader getClassLoader() {
306         return context.getClassLoader();
307     }
308 
309     @Override
310     public void declareRoles(String... roleNames) {
311         context.declareRoles(roleNames);
312     }
313 
314     @Override
315     public ServletRegistration.Dynamic addServlet(String servletName, String className) {
316         servletModuleManager.addServlet(plugin, servletName, className);
317         return null;
318     }
319 
320     @Override
321     public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
322         // atlassian-plugins limitation - HttpServlet only
323         if (!(servlet instanceof HttpServlet)) {
324             throw new IllegalArgumentException("only javax.servlet.http.HttpServlet is supported by atlassian-plugins for javax.servlet.ServletContext#addServlet(String, javax.servlet.Servlet)}");
325         }
326 
327         servletModuleManager.addServlet(plugin, servletName, (HttpServlet) servlet, this);
328         return null;
329     }
330 
331     @Override
332     public ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) {
333         servletModuleManager.addServlet(plugin, servletName, servletClass.getName());
334         return null;
335     }
336 
337     @Override
338     public <T extends Servlet> T createServlet(Class<T> clazz) {
339         throw new UnsupportedOperationException();
340     }
341 
342     @Override
343     public ServletRegistration getServletRegistration(String servletName) {
344         throw new UnsupportedOperationException();
345     }
346 
347     @Override
348     public Map<String, ? extends ServletRegistration> getServletRegistrations() {
349         throw new UnsupportedOperationException();
350     }
351 
352     @Override
353     public FilterRegistration.Dynamic addFilter(String filterName, String className) {
354         throw new UnsupportedOperationException();
355     }
356 
357     @Override
358     public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {
359         throw new UnsupportedOperationException();
360     }
361 
362     @Override
363     public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) {
364         throw new UnsupportedOperationException();
365     }
366 
367     @Override
368     public <T extends Filter> T createFilter(Class<T> clazz) {
369         throw new UnsupportedOperationException();
370     }
371 
372     @Override
373     public FilterRegistration getFilterRegistration(String filterName) {
374         throw new UnsupportedOperationException();
375     }
376 
377     @Override
378     public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
379         throw new UnsupportedOperationException();
380     }
381 
382     @Override
383     public void addListener(String className) {
384         throw new UnsupportedOperationException();
385     }
386 
387     @Override
388     public <T extends EventListener> void addListener(T t) {
389         throw new UnsupportedOperationException();
390     }
391 
392     @Override
393     public void addListener(Class<? extends EventListener> listenerClass) {
394         throw new UnsupportedOperationException();
395     }
396 
397     @Override
398     public <T extends EventListener> T createListener(Class<T> clazz) {
399         throw new UnsupportedOperationException();
400     }
401 }