View Javadoc

1   package org.apache.velocity.tools.view;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.InputStream;
26  import java.io.IOException;
27  import java.lang.reflect.Constructor;
28  import javax.servlet.FilterConfig;
29  import javax.servlet.ServletConfig;
30  import javax.servlet.ServletContext;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpSession;
33  import org.apache.velocity.tools.ClassUtils;
34  import org.apache.velocity.tools.Toolbox;
35  import org.apache.velocity.tools.config.FactoryConfiguration;
36  import org.apache.velocity.tools.config.FileFactoryConfiguration;
37  import org.apache.velocity.tools.config.PropertiesFactoryConfiguration;
38  import org.apache.velocity.tools.config.XmlFactoryConfiguration;
39  
40  /**
41   * <p>A set of utility methods for supporting and using
42   * VelocityTools in the servlet environment.</p>
43   *
44   * @version $Id: ServletUtils.java 471244 2006-11-04 18:34:38Z henning $
45   */
46  public class ServletUtils
47  {
48      public static final String VELOCITY_VIEW_KEY =
49          VelocityView.class.getName();
50      public static final String SHARED_CONFIG_PARAM =
51          "org.apache.velocity.tools.shared.config";
52      public static final String ALT_VELOCITY_VIEW_KEY =
53          "org.apache.velocity.tools.view.class";
54      /**
55       * Key used to access a live {@link FactoryConfiguration} previously
56       * placed in the ServletContext attributes.
57       */
58      public static final String CONFIGURATION_KEY =
59          "org.apache.velocity.tools";
60  
61      public static final ServletUtils INSTANCE = new ServletUtils();
62  
63      protected ServletUtils() {}
64  
65      public ServletUtils getInstance()
66      {
67          return INSTANCE;
68      }
69  
70      /**
71       * Retrieves the path for the specified request regardless of
72       * whether this is a direct request or an include by the
73       * RequestDispatcher.
74       */
75      public static String getPath(HttpServletRequest request)
76      {
77          // If we get here from RequestDispatcher.include(), getServletPath()
78          // will return the original (wrong) URI requested.  The following special
79          // attribute holds the correct path.  See section 8.3 of the Servlet
80          // 2.3 specification.
81          String path = (String)request.getAttribute("javax.servlet.include.servlet_path");
82          // also take into account the PathInfo stated on SRV.4.4 Request Path Elements
83          String info = (String)request.getAttribute("javax.servlet.include.path_info");
84          if (path == null)
85          {
86              path = request.getServletPath();
87              info = request.getPathInfo();
88          }
89          if (info != null)
90          {
91              path += info;
92          }
93          return path;
94      }
95  
96  
97      /**
98       * Returns the shared {@link VelocityView} for the specified
99       * {@link ServletConfig}'s context. If one has not yet been created, it
100      * will create, store it for future access, and then return it.
101      */
102     public static VelocityView getVelocityView(ServletConfig config)
103     {
104         return getVelocityView(new JeeServletConfig(config));
105     }
106 
107 
108     /**
109      * Returns the shared {@link VelocityView} for the specified
110      * {@link FilterConfig}'s context. If one has not yet been created, it
111      * will create, store it for future access, and then return it.
112      */
113     public static VelocityView getVelocityView(FilterConfig config)
114     {
115         return getVelocityView(new JeeFilterConfig(config));
116     }
117 
118     /**
119      * Returns the shared {@link VelocityView} for the specified
120      * {@link JeeConfig}'s context. If one has not yet been created, it
121      * will create, store it for future access, and then return it.
122      */
123     public static VelocityView getVelocityView(JeeConfig config)
124     {
125         // check for an init-param telling this servlet/filter NOT
126         // to share its VelocityView with others.  by default, we
127         // play nice and share the VelocityView with the other kids.
128         String shared = config.findInitParameter(SHARED_CONFIG_PARAM);
129         if (shared != null && shared.equals("false"))
130         {
131             // just create a new, non-shared VelocityView
132             return createView(config);
133         }
134 
135         ServletContext application = config.getServletContext();
136 
137         // check for an already initialized VelocityView to use
138         VelocityView view = getVelocityView(application, false);
139         if (view == null)
140         {
141             // only create a new one if we don't already have one
142             view = createView(config);
143 
144             // and store it in the application attributes, so other
145             // servlets, filters, or tags can use it
146             application.setAttribute(VELOCITY_VIEW_KEY, view);
147         }
148         return view;
149     }
150 
151     private static VelocityView createView(JeeConfig config)
152     {
153         String cls = config.findInitParameter(ALT_VELOCITY_VIEW_KEY);
154         if (cls == null)
155         {
156             return new VelocityView(config);
157         }
158         try
159         {
160             return createView(ClassUtils.getClass(cls), config);
161         }
162         catch (ClassNotFoundException cnfe)
163         {
164             throw new IllegalArgumentException("Could not find class "+cls, cnfe);
165         }
166     }
167 
168     private static VelocityView createView(Class klass, JeeConfig config)
169     {
170         if (!VelocityView.class.isAssignableFrom(klass))
171         {
172             throw new IllegalArgumentException(klass+" must extend "+VelocityView.class);
173         }
174         try
175         {
176             Constructor ctor = klass.getConstructor(JeeConfig.class);
177             return (VelocityView)ctor.newInstance(config);
178         }
179         catch (NoSuchMethodException nsme)
180         {
181             throw new IllegalArgumentException(klass+" must have a constructor that takes "+JeeConfig.class, nsme);
182         }
183         catch (Exception e)
184         {
185             throw new RuntimeException("Could not instantiate "+klass+" with "+config, e);
186         }
187     }
188 
189     /**
190      * Returns the shared {@link VelocityView} for the specified
191      * {@link ServletContext}. If one has not yet been created,
192      * it will create one, store it for future access, and then return it.
193      */
194     public static VelocityView getVelocityView(ServletContext application)
195     {
196         return getVelocityView(new JeeContextConfig(application));
197     }
198 
199     /**
200      * Returns the shared {@link VelocityView} for the specified
201      * {@link ServletContext}. If one has not yet been created and
202      * the second parameter is <code>true</code>, then it will
203      * create one, store it for future access, and return it.
204      */
205     public static VelocityView getVelocityView(ServletContext application,
206                                                boolean createIfMissing) {
207         VelocityView view =
208             (VelocityView)application.getAttribute(VELOCITY_VIEW_KEY);
209         if (view == null && createIfMissing)
210         {
211             return getVelocityView(application);
212         }
213         return view;
214     }
215 
216 
217     public static Object findTool(String key, ServletContext application)
218     {
219         return findTool(key, VelocityView.DEFAULT_TOOLBOX_KEY, application);
220     }
221 
222     public static Object findTool(String key, String toolboxKey,
223                                   ServletContext application)
224     {
225         Toolbox toolbox = (Toolbox)application.getAttribute(toolboxKey);
226         if (toolbox != null)
227         {
228             return toolbox.get(key);
229         }
230         return null;
231     }
232 
233     public static Object findTool(String key, HttpServletRequest request)
234     {
235         return findTool(key, request, null);
236     }
237 
238     public static Object findTool(String key, String toolboxKey,
239                                   HttpServletRequest request)
240     {
241         return findTool(key, toolboxKey, request, null);
242     }
243 
244     public static Object findTool(String key, HttpServletRequest request,
245                                   ServletContext application)
246     {
247         return findTool(key, VelocityView.DEFAULT_TOOLBOX_KEY,
248                         request, application);
249     }
250 
251     public static Object findTool(String key, String toolboxKey,
252                                   HttpServletRequest request,
253                                   ServletContext application)
254     {
255         String path = getPath(request);
256 
257         Toolbox toolbox = (Toolbox)request.getAttribute(toolboxKey);
258         if (toolbox != null)
259         {
260             Object tool = toolbox.get(key, path);
261             if (tool != null)
262             {
263                 return tool;
264             }
265         }
266 
267         HttpSession session = request.getSession(false);
268         if (session != null)
269         {
270             toolbox = (Toolbox)session.getAttribute(toolboxKey);
271             if (toolbox != null)
272             {
273                 Object tool = toolbox.get(key, path);
274                 if (tool != null)
275                 {
276                     return tool;
277                 }
278             }
279 
280             if (application == null)
281             {
282                 application = session.getServletContext();
283             }
284         }
285 
286         if (application != null)
287         {
288             toolbox = (Toolbox)application.getAttribute(toolboxKey);
289             if (toolbox != null)
290             {
291                 return toolbox.get(key, path);
292             }
293         }
294 
295         return null;
296     }
297 
298     public static InputStream getInputStream(String path, ServletContext application)
299     {
300         // first, search the classpath
301         InputStream inputStream = ClassUtils.getResourceAsStream(path, ServletUtils.class);
302         if (inputStream == null)
303         {
304             // then, try the servlet context
305             inputStream = application.getResourceAsStream(path);
306 
307             if (inputStream == null)
308             {
309                 // then, try the file system directly
310                 File file = new File(path);
311                 if (file.exists())
312                 {
313                     try
314                     {
315                         inputStream = new FileInputStream(file);
316                     }
317                     catch (FileNotFoundException fnfe)
318                     {
319                         // we should not be able to get here
320                         // since we already checked whether the file exists
321                         throw new IllegalStateException(fnfe);
322                     }
323                 }
324             }
325         }
326         return inputStream;
327     }
328 
329     public static FactoryConfiguration getConfiguration(ServletContext application)
330     {
331         Object obj = application.getAttribute(CONFIGURATION_KEY);
332         if (obj instanceof FactoryConfiguration)
333         {
334             FactoryConfiguration injected = (FactoryConfiguration)obj;
335             // make note of where we found this
336             String source = injected.getSource();
337             String addnote = " from ServletContext.getAttribute("+CONFIGURATION_KEY+")";
338             if (!source.endsWith(addnote))
339             {
340                 injected.setSource(source+addnote);
341             }
342             return injected;
343         }
344         return null;
345     }
346 
347     public static FactoryConfiguration getConfiguration(String path,
348                                                         ServletContext application)
349     {
350         return getConfiguration(path, application, path.endsWith("toolbox.xml"));
351     }
352 
353     public static FactoryConfiguration getConfiguration(String path,
354                                                         ServletContext application,
355                                                         boolean deprecationSupportMode)
356     {
357         // first make sure we can even get such a file
358         InputStream inputStream = getInputStream(path, application);
359         if (inputStream == null)
360         {
361             return null;
362         }
363 
364         // then make sure it's a file type we recognize
365         FileFactoryConfiguration config = null;
366         String source = "ServletUtils.getConfiguration("+path+",ServletContext[,depMode="+deprecationSupportMode+"])";
367         if (path.endsWith(".xml"))
368         {
369             config = new XmlFactoryConfiguration(deprecationSupportMode, source);
370         }
371         else if (path.endsWith(".properties"))
372         {
373             config = new PropertiesFactoryConfiguration(source);
374         }
375         else
376         {
377             String msg = "Unknown configuration file type: " + path +
378                          "\nOnly .xml and .properties configuration files are supported at this time.";
379             throw new UnsupportedOperationException(msg);
380         }
381 
382         // now, try to read the file
383         try
384         {
385             config.read(inputStream);
386         }
387         catch (IOException ioe)
388         {
389             throw new RuntimeException("Failed to load configuration at: "+path, ioe);
390         }
391         finally
392         {
393             try
394             {
395                 if (inputStream != null)
396                 {
397                     inputStream.close();
398                 }
399             }
400             catch (IOException ioe)
401             {
402                 throw new RuntimeException("Failed to close input stream for "+path, ioe);
403             }
404         }
405         return config;
406     }
407 
408     /**
409      * Returns a mutex (lock object) unique to the specified session
410      * and stored under the specified key to allow for reliable
411      * synchronization on the session.
412      */
413     public static Object getMutex(HttpSession session, String key, Object caller)
414     {
415         // yes, this uses double-checked locking, but it is safe here
416         // since partial initialization of the lock is not an issue
417         Object lock = session.getAttribute(key);
418         if (lock == null)
419         {
420             // one thread per caller at a time
421             synchronized(caller)
422             {
423                 // in case another thread already came thru
424                 lock = session.getAttribute(key);
425                 if (lock == null)
426                 {
427                     // use a small, serializable object
428                     // that is unlikely to be unfortunately optimized
429                     lock = new SessionMutex();
430                     session.setAttribute(key, lock);
431                 }
432             }
433         }
434         return lock;
435     }
436 
437     private static class SessionMutex implements java.io.Serializable
438     {
439     }
440 
441 }