View Javadoc

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