View Javadoc

1   package com.atlassian.plugin.descriptors.servlet;
2   
3   import java.io.InputStream;
4   import java.net.MalformedURLException;
5   import java.net.URL;
6   import java.util.Collection;
7   import java.util.Collections;
8   import java.util.Enumeration;
9   import java.util.HashSet;
10  import java.util.Set;
11  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
12  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
13  import java.lang.reflect.Method;
14  import java.lang.reflect.InvocationTargetException;
15  
16  import javax.servlet.RequestDispatcher;
17  import javax.servlet.Servlet;
18  import javax.servlet.ServletContext;
19  import javax.servlet.ServletException;
20  
21  /**
22   * A wrapper around servlet context that allows plugin servlets to add
23   * attributes which will not be shared/clobbered by other plugins.
24   */
25  public class PluginServletContextWrapper implements ServletContext
26  {
27      private final ServletModuleDescriptor descriptor;
28      private final ServletContext context;
29      private final ConcurrentMap attributes;
30      private final Method methodGetContextPath;
31      
32      public PluginServletContextWrapper(ServletModuleDescriptor descriptor, ServletContext context)
33      {
34          Method tmpMethod = null;
35          this.descriptor = descriptor;
36          this.context = context;
37          this.attributes = new ConcurrentHashMap();
38  
39          Class cls = context.getClass();
40          try
41          {
42              tmpMethod = cls.getMethod("getContextPath", new Class[0]);
43          } catch (NoSuchMethodException e)
44          {
45              // no problem, Servlet 2.4 or earlier found
46          }
47          methodGetContextPath = tmpMethod;
48      }
49  
50      /**
51       * <p>Gets the named attribute.  The attribute is first looked for in the local
52       * attribute map, if it is not found there it is looked for in the wrapped
53       * contexts attribute map.  If it is not there, null is returned.</p>
54       * 
55       * <p>A consequence of this ordering is that servlets may, in their own
56       * context, override but not overwrite attributes from the wrapped context.</p>
57       */
58      public Object getAttribute(String name)
59      {
60          Object attr = attributes.get(name);
61          if (attr == null)
62              attr = context.getAttribute(name);
63  
64          return attr;
65      }
66  
67      /**
68       * @return an enumeration of all the attributes from the wrapped 
69       *         context as well as the local attributes.
70       */
71      public Enumeration getAttributeNames()
72      {
73          Collection names = new HashSet();
74          names.addAll(attributes.keySet());
75          names.addAll(Collections.list(context.getAttributeNames()));
76          return Collections.enumeration(names);
77      }
78  
79      /**
80       * Removes an attribute from the local context.  Leaves the wrapped context
81       * completely untouched.
82       */
83      public void removeAttribute(String name)
84      {
85          attributes.remove(name);
86      }
87  
88      /**
89       * <p>Sets an attribute in the local attribute map, leaving the wrapped
90       * context untouched.</p>
91       * 
92       * <p>Servlets may use this and the lookup ordering of the
93       * <code>getAttribute()</code> method to effectively override the value
94       * of an attribute in the wrapped servlet context with their own value and 
95       * this overridden value will only be seen in the plugins own scope.</p>
96       */
97      public void setAttribute(String name, Object object)
98      {
99          attributes.put(name, object);
100     }
101 
102     /**
103      * @return the init parameter from the servlet module descriptor.
104      */
105     public String getInitParameter(String name)
106     {
107         return (String) descriptor.getInitParams().get(name);
108     }
109 
110     /**
111      * @return an enumeration of the init parameters from the servlet module
112      * descriptor.
113      */
114     public Enumeration getInitParameterNames()
115     {
116         return Collections.enumeration(descriptor.getInitParams().keySet());
117     }
118 
119     /**
120      * @return the resource from the plugin classloader if it exists, otherwise the 
121      *         resource is looked up from the wrapped context and returned
122      */
123     public URL getResource(String path) throws MalformedURLException
124     {
125         URL url = descriptor.getPlugin().getResource(path);
126         if (url == null)
127         {
128             url = context.getResource(path);
129         }
130         return url;
131     }
132 
133     /**
134      * @return the resource stream from the plugin classloader if it exists, otherwise
135      *         the resource stream is attempted to be retrieved from the wrapped context
136      */
137     public InputStream getResourceAsStream(String path)
138     {
139         InputStream in = descriptor.getPlugin().getResourceAsStream(path);
140         if (in == null)
141         {
142             in = context.getResourceAsStream(path);
143         }
144         return in;
145     }
146 
147     /**
148      * @return null so that servlet plugins can't escape their box
149      */
150     public ServletContext getContext(String uripath)
151     {
152         return null;
153     }
154 
155     //---- All methods below simply delegate to the wrapped servlet context ----
156 
157     public String getContextPath() {
158 
159         // all this crap to deal with Servlet 2.4 containers better
160         if (methodGetContextPath != null)
161         {
162             try
163             {
164                 return (String) methodGetContextPath.invoke(context, new Object[0]);
165             } catch (IllegalAccessException e)
166             {
167                 throw new RuntimeException("Cannot access this method", e);
168             } catch (InvocationTargetException e)
169             {
170                 throw new RuntimeException("Unable to execute getContextPath()", e.getCause());
171             }
172         } else
173         {
174             throw new UnsupportedOperationException("This servlet context doesn't support 2.5 methods");
175         }
176 
177     }
178 
179     public int getMajorVersion()
180     {
181         return context.getMajorVersion();
182     }
183 
184     public String getMimeType(String file)
185     {
186         return context.getMimeType(file);
187     }
188 
189     public int getMinorVersion()
190     {
191         return context.getMinorVersion();
192     }
193 
194     public RequestDispatcher getNamedDispatcher(String name)
195     {
196         return context.getNamedDispatcher(name);
197     }
198 
199     public String getRealPath(String path)
200     {
201         return context.getRealPath(path);
202     }
203 
204     public RequestDispatcher getRequestDispatcher(String path)
205     {
206         return context.getRequestDispatcher(path);
207     }
208 
209     public Set getResourcePaths(String arg0)
210     {
211         return context.getResourcePaths(arg0);
212     }
213 
214     public String getServerInfo()
215     {
216         return context.getServerInfo();
217     }
218 
219     public Servlet getServlet(String name) throws ServletException
220     {
221         return context.getServlet(name);
222     }
223 
224     public String getServletContextName()
225     {
226         return context.getServletContextName();
227     }
228 
229     public Enumeration getServletNames()
230     {
231         return context.getServletNames();
232     }
233 
234     public Enumeration getServlets()
235     {
236         return context.getServlets();
237     }
238 
239     public void log(Exception exception, String msg)
240     {
241         context.log(exception, msg);
242     }
243 
244     public void log(String message, Throwable throwable)
245     {
246         context.log(message, throwable);
247     }
248 
249     public void log(String msg)
250     {
251         context.log(msg);
252     }
253 
254 }