KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > servlet > VelocityServlet


1 package org.apache.velocity.servlet;
2
3 /*
4  * Copyright 2000-2001,2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import java.io.IOException JavaDoc;
20 import java.io.PrintWriter JavaDoc;
21 import java.io.StringWriter JavaDoc;
22 import java.io.OutputStreamWriter JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileNotFoundException JavaDoc;
25 import java.io.UnsupportedEncodingException JavaDoc;
26
27 import java.util.Properties JavaDoc;
28
29 import javax.servlet.ServletConfig JavaDoc;
30 import javax.servlet.ServletContext JavaDoc;
31 import javax.servlet.ServletException JavaDoc;
32 import javax.servlet.ServletOutputStream JavaDoc;
33
34 import javax.servlet.http.HttpServlet JavaDoc;
35 import javax.servlet.http.HttpServletRequest JavaDoc;
36 import javax.servlet.http.HttpServletResponse JavaDoc;
37
38 import org.apache.velocity.Template;
39 import org.apache.velocity.runtime.RuntimeConstants;
40 import org.apache.velocity.runtime.RuntimeSingleton;
41 import org.apache.velocity.io.VelocityWriter;
42 import org.apache.velocity.util.SimplePool;
43
44 import org.apache.velocity.context.Context;
45 import org.apache.velocity.VelocityContext;
46
47 import org.apache.velocity.app.Velocity;
48
49 import org.apache.velocity.exception.ResourceNotFoundException;
50 import org.apache.velocity.exception.ParseErrorException;
51 import org.apache.velocity.exception.MethodInvocationException;
52
53 /**
54  * Base class which simplifies the use of Velocity with Servlets.
55  * Extend this class, implement the <code>handleRequest()</code> method,
56  * and add your data to the context. Then call
57  * <code>getTemplate("myTemplate.wm")</code>.
58  *
59  * This class puts some things into the context object that you should
60  * be aware of:
61  * <pre>
62  * "req" - The HttpServletRequest object
63  * "res" - The HttpServletResponse object
64  * </pre>
65  *
66  * There are other methods you can override to access, alter or control
67  * any part of the request processing chain. Please see the javadocs for
68  * more information on :
69  * <ul>
70  * <li> loadConfiguration() : for setting up the Velocity runtime
71  * <li> createContext() : for creating and loading the Context
72  * <li> setContentType() : for changing the content type on a request
73  * by request basis
74  * <li> handleRequest() : you <b>must</b> implement this
75  * <li> mergeTemplate() : the template rendering process
76  * <li> requestCleanup() : post rendering resource or other cleanup
77  * <li> error() : error handling
78  * </ul>
79  * <br>
80  * If you put a contentType object into the context within either your
81  * serlvet or within your template, then that will be used to override
82  * the default content type specified in the properties file.
83  *
84  * "contentType" - The value for the Content-Type: header
85  *
86  * @author Dave Bryson
87  * @author <a HREF="mailto:jon@latchkey.com">Jon S. Stevens</a>
88  * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
89  * @author <a HREF="kjohnson@transparent.com">Kent Johnson</a>
90  * @author <a HREF="dlr@finemaltcoding.com">Daniel Rall</a>
91  * $Id: VelocityServlet.java,v 1.52.4.1 2004/03/03 23:23:03 geirm Exp $
92  */

93 public abstract class VelocityServlet extends HttpServlet JavaDoc
94 {
95     /**
96      * The context key for the HTTP request object.
97      */

98     public static final String JavaDoc REQUEST = "req";
99
100     /**
101      * The context key for the HTTP response object.
102      */

103     public static final String JavaDoc RESPONSE = "res";
104
105     /**
106      * The HTTP content type context key.
107      */

108     public static final String JavaDoc CONTENT_TYPE = "default.contentType";
109
110     /**
111      * The default content type for the response
112      */

113     public static final String JavaDoc DEFAULT_CONTENT_TYPE = "text/html";
114     
115   
116     /**
117      * Encoding for the output stream
118      */

119     public static final String JavaDoc DEFAULT_OUTPUT_ENCODING = "ISO-8859-1";
120
121     /**
122      * The default content type, itself defaulting to {@link
123      * #DEFAULT_CONTENT_TYPE} if not configured.
124      */

125     private static String JavaDoc defaultContentType;
126
127     /**
128      * This is the string that is looked for when getInitParameter is
129      * called (<code>org.apache.velocity.properties</code>).
130      */

131     protected static final String JavaDoc INIT_PROPS_KEY =
132         "org.apache.velocity.properties";
133
134     /**
135      * Use of this properties key has been deprecated, and will be
136      * removed in Velocity version 1.5.
137      */

138     private static final String JavaDoc OLD_INIT_PROPS_KEY = "properties";
139
140     /**
141      * Cache of writers
142      */

143    
144     private static SimplePool writerPool = new SimplePool(40);
145    
146     /**
147      * Performs initialization of this servlet. Called by the servlet
148      * container on loading.
149      *
150      * @param config The servlet configuration to apply.
151      *
152      * @exception ServletException
153      */

154     public void init( ServletConfig JavaDoc config )
155         throws ServletException JavaDoc
156     {
157         super.init( config );
158
159         /*
160          * do whatever we have to do to init Velocity
161          */

162         initVelocity( config );
163
164         /*
165          * Now that Velocity is initialized, cache some config.
166          */

167         defaultContentType = RuntimeSingleton.getString(CONTENT_TYPE,
168                                                         DEFAULT_CONTENT_TYPE);
169     }
170
171     /**
172      * Initializes the Velocity runtime, first calling
173      * loadConfiguration(ServletConvig) to get a
174      * java.util.Properties of configuration information
175      * and then calling Velocity.init(). Override this
176      * to do anything to the environment before the
177      * initialization of the singelton takes place, or to
178      * initialize the singleton in other ways.
179      */

180     protected void initVelocity( ServletConfig JavaDoc config )
181          throws ServletException JavaDoc
182     {
183         try
184         {
185             /*
186              * call the overridable method to allow the
187              * derived classes a shot at altering the configuration
188              * before initializing Runtime
189              */

190
191             Properties JavaDoc props = loadConfiguration( config );
192   
193             Velocity.init( props );
194         }
195         catch( Exception JavaDoc e )
196         {
197             throw new ServletException JavaDoc("Error initializing Velocity: " + e, e);
198         }
199     }
200      
201     /**
202      * Loads the configuration information and returns that
203      * information as a Properties, which will be used to
204      * initialize the Velocity runtime.
205      * <br><br>
206      * Currently, this method gets the initialization parameter
207      * VelocityServlet.INIT_PROPS_KEY, which should be a file containing
208      * the configuration information.
209      * <br><br>
210      * To configure your Servlet Spec 2.2 compliant servlet runner to pass
211      * this to you, put the following in your WEB-INF/web.xml file
212      * <br>
213      * <pre>
214      * &lt;servlet&gt;
215      * &lt;servlet-name&gt; YourServlet &lt/servlet-name&gt;
216      * &lt;servlet-class&gt; your.package.YourServlet &lt;/servlet-class&gt;
217      * &lt;init-param&gt;
218      * &lt;param-name&gt; org.apache.velocity.properties &lt;/param-name&gt;
219      * &lt;param-value&gt; velocity.properties &lt;/param-value&gt;
220      * &lt;/init-param&gt;
221      * &lt;/servlet&gt;
222      * </pre>
223      *
224      * Alternately, if you wish to configure an entire context in this
225      * fashion, you may use the following:
226      * <br>
227      * <pre>
228      * &lt;context-param&gt;
229      * &lt;param-name&gt; org.apache.velocity.properties &lt;/param-name&gt;
230      * &lt;param-value&gt; velocity.properties &lt;/param-value&gt;
231      * &lt;description&gt; Path to Velocity configuration &lt;/description&gt;
232      * &lt;/context-param&gt;
233      * </pre>
234      *
235      * Derived classes may do the same, or take advantage of this code to do the loading for them via :
236      * <pre>
237      * Properties p = super.loadConfiguration( config );
238      * </pre>
239      * and then add or modify the configuration values from the file.
240      * <br>
241      *
242      * @param config ServletConfig passed to the servlets init() function
243      * Can be used to access the real path via ServletContext (hint)
244      * @return java.util.Properties loaded with configuration values to be used
245      * to initialize the Velocity runtime.
246      * @throws FileNotFoundException if a specified file is not found.
247      * @throws IOException I/O problem accessing the specified file, if specified.
248      */

249     protected Properties JavaDoc loadConfiguration(ServletConfig JavaDoc config)
250         throws IOException JavaDoc, FileNotFoundException JavaDoc
251     {
252         // This is a little overly complex because of legacy support
253
// for the initialization properties key "properties".
254
// References to OLD_INIT_PROPS_KEY should be removed at
255
// Velocity version 1.5.
256
String JavaDoc propsFile = config.getInitParameter(INIT_PROPS_KEY);
257         if (propsFile == null || propsFile.length() == 0)
258         {
259             ServletContext JavaDoc sc = config.getServletContext();
260             propsFile = config.getInitParameter(OLD_INIT_PROPS_KEY);
261             if (propsFile == null || propsFile.length() == 0)
262             {
263                 propsFile = sc.getInitParameter(INIT_PROPS_KEY);
264                 if (propsFile == null || propsFile.length() == 0)
265                 {
266                     propsFile = sc.getInitParameter(OLD_INIT_PROPS_KEY);
267                     if (propsFile != null && propsFile.length() > 0)
268                     {
269                         sc.log("Use of the properties initialization " +
270                                "parameter '" + OLD_INIT_PROPS_KEY + "' has " +
271                                "been deprecated by '" + INIT_PROPS_KEY + '\'');
272                     }
273                 }
274             }
275             else
276             {
277                 sc.log("Use of the properties initialization parameter '" +
278                        OLD_INIT_PROPS_KEY + "' has been deprecated by '" +
279                        INIT_PROPS_KEY + '\'');
280             }
281         }
282         
283         /*
284          * This will attempt to find the location of the properties
285          * file from the relative path to the WAR archive (ie:
286          * docroot). Since JServ returns null for getRealPath()
287          * because it was never implemented correctly, then we know we
288          * will not have an issue with using it this way. I don't know
289          * if this will break other servlet engines, but it probably
290          * shouldn't since WAR files are the future anyways.
291          */

292
293         Properties JavaDoc p = new Properties JavaDoc();
294         
295         if ( propsFile != null )
296         {
297             String JavaDoc realPath = getServletContext().getRealPath(propsFile);
298         
299             if ( realPath != null )
300             {
301                 propsFile = realPath;
302             }
303
304             p.load( new FileInputStream JavaDoc(propsFile) );
305         }
306
307         return p;
308     }
309           
310     /**
311      * Handles HTTP <code>GET</code> requests by calling {@link
312      * #doRequest()}.
313      */

314     public void doGet( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response )
315         throws ServletException JavaDoc, IOException JavaDoc
316     {
317         doRequest(request, response);
318     }
319
320     /**
321      * Handles HTTP <code>POST</code> requests by calling {@link
322      * #doRequest()}.
323      */

324     public void doPost( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response )
325         throws ServletException JavaDoc, IOException JavaDoc
326     {
327         doRequest(request, response);
328     }
329
330     /**
331      * Handles all requests (by default).
332      *
333      * @param request HttpServletRequest object containing client request
334      * @param response HttpServletResponse object for the response
335      */

336     protected void doRequest(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response )
337          throws ServletException JavaDoc, IOException JavaDoc
338     {
339         Context JavaDoc context = null;
340         try
341         {
342             /*
343              * first, get a context
344              */

345
346             context = createContext( request, response );
347             
348             /*
349              * set the content type
350              */

351
352             setContentType( request, response );
353
354             /*
355              * let someone handle the request
356              */

357
358             Template template = handleRequest( request, response, context );
359             /*
360              * bail if we can't find the template
361              */

362
363             if ( template == null )
364             {
365                 return;
366             }
367
368             /*
369              * now merge it
370              */

371
372             mergeTemplate( template, context, response );
373         }
374         catch (Exception JavaDoc e)
375         {
376             /*
377              * call the error handler to let the derived class
378              * do something useful with this failure.
379              */

380
381             error( request, response, e);
382         }
383         finally
384         {
385             /*
386              * call cleanup routine to let a derived class do some cleanup
387              */

388
389             requestCleanup( request, response, context );
390         }
391     }
392
393     /**
394      * A cleanup routine which is called at the end of the {@link
395      * #doRequest(HttpServletRequest, HttpServletResponse)}
396      * processing sequence, allowing a derived class to do resource
397      * cleanup or other end of process cycle tasks.
398      *
399      * @param request servlet request from client
400      * @param response servlet reponse
401      * @param context context created by the createContext() method
402      */

403     protected void requestCleanup( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Context JavaDoc context )
404     {
405         return;
406     }
407
408     /**
409      * merges the template with the context. Only override this if you really, really
410      * really need to. (And don't call us with questions if it breaks :)
411      *
412      * @param template template object returned by the handleRequest() method
413      * @param context context created by the createContext() method
414      * @param response servlet reponse (use this to get the output stream or Writer
415      */

416     protected void mergeTemplate( Template template, Context JavaDoc context, HttpServletResponse JavaDoc response )
417         throws ResourceNotFoundException, ParseErrorException,
418                MethodInvocationException, IOException JavaDoc, UnsupportedEncodingException JavaDoc, Exception JavaDoc
419     {
420         ServletOutputStream JavaDoc output = response.getOutputStream();
421         VelocityWriter vw = null;
422         // ASSUMPTION: response.setContentType() has been called.
423
String JavaDoc encoding = response.getCharacterEncoding();
424         
425         try
426         {
427             vw = (VelocityWriter) writerPool.get();
428             
429             if (vw == null)
430             {
431                 vw = new VelocityWriter(new OutputStreamWriter JavaDoc(output,
432                                                                encoding),
433                                         4 * 1024, true);
434             }
435             else
436             {
437                 vw.recycle(new OutputStreamWriter JavaDoc(output, encoding));
438             }
439            
440             template.merge(context, vw);
441         }
442         finally
443         {
444             try
445             {
446                 if (vw != null)
447                 {
448                     /*
449                      * flush and put back into the pool
450                      * don't close to allow us to play
451                      * nicely with others.
452                      */

453                     vw.flush();
454
455                     /*
456                      * Clear the VelocityWriter's reference to its
457                      * internal OutputStreamWriter to allow the latter
458                      * to be GC'd while vw is pooled.
459                      */

460                     vw.recycle(null);
461
462                     writerPool.put(vw);
463                 }
464             }
465             catch (Exception JavaDoc e)
466             {
467                 // do nothing
468
}
469         }
470     }
471
472     /**
473      * Sets the content type of the response, defaulting to {@link
474      * #defaultContentType} if not overriden. Delegates to {@link
475      * #chooseCharacterEncoding(HttpServletRequest)} to select the
476      * appropriate character encoding.
477      *
478      * @param request The servlet request from the client.
479      * @param response The servlet reponse to the client.
480      */

481     protected void setContentType(HttpServletRequest JavaDoc request,
482                                   HttpServletResponse JavaDoc response)
483     {
484         String JavaDoc contentType = defaultContentType;
485         int index = contentType.lastIndexOf(';') + 1;
486         if (index <= 0 || (index < contentType.length() &&
487                            contentType.indexOf("charset", index) == -1))
488         {
489             // Append the character encoding which we'd like to use.
490
String JavaDoc encoding = chooseCharacterEncoding(request);
491             //System.out.println("Chose output encoding of '" +
492
// encoding + '\'');
493
if (!DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding))
494             {
495                 contentType += "; charset=" + encoding;
496             }
497         }
498         response.setContentType(contentType);
499         //System.out.println("Response Content-Type set to '" +
500
// contentType + '\'');
501
}
502
503     /**
504      * Chooses the output character encoding to be used as the value
505      * for the "charset=" portion of the HTTP Content-Type header (and
506      * thus returned by <code>response.getCharacterEncoding()</code>).
507      * Called by {@link #setContentType(HttpServletRequest,
508      * HttpServletResponse)} if an encoding isn't already specified by
509      * Content-Type. By default, chooses the value of
510      * RuntimeSingleton's <code>output.encoding</code> property.
511      *
512      * @param request The servlet request from the client.
513      */

514     protected String JavaDoc chooseCharacterEncoding(HttpServletRequest JavaDoc request)
515     {
516         return RuntimeSingleton.getString(RuntimeConstants.OUTPUT_ENCODING,
517                                           DEFAULT_OUTPUT_ENCODING);
518     }
519
520     /**
521      * Returns a context suitable to pass to the handleRequest() method
522      * <br><br>
523      * Default implementation will create a VelocityContext object,
524      * put the HttpServletRequest and HttpServletResponse
525      * into the context accessable via the keys VelocityServlet.REQUEST and
526      * VelocityServlet.RESPONSE, respectively.
527      *
528      * @param request servlet request from client
529      * @param response servlet reponse to client
530      *
531      * @return context
532      */

533     protected Context JavaDoc createContext(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response )
534     {
535         /*
536          * create a new context
537          */

538
539         VelocityContext context = new VelocityContext();
540         
541         /*
542          * put the request/response objects into the context
543          * wrap the HttpServletRequest to solve the introspection
544          * problems
545          */

546            
547         context.put( REQUEST, request );
548         context.put( RESPONSE, response );
549
550         return context;
551     }
552
553     /**
554      * Retrieves the requested template.
555      *
556      * @param name The file name of the template to retrieve relative to the
557      * template root.
558      * @return The requested template.
559      * @throws ResourceNotFoundException if template not found
560      * from any available source.
561      * @throws ParseErrorException if template cannot be parsed due
562      * to syntax (or other) error.
563      * @throws Exception if an error occurs in template initialization
564      */

565     public Template getTemplate( String JavaDoc name )
566         throws ResourceNotFoundException, ParseErrorException, Exception JavaDoc
567     {
568         return RuntimeSingleton.getTemplate(name);
569     }
570     
571     /**
572      * Retrieves the requested template with the specified
573      * character encoding.
574      *
575      * @param name The file name of the template to retrieve relative to the
576      * template root.
577      * @param encoding the character encoding of the template
578      *
579      * @return The requested template.
580      * @throws ResourceNotFoundException if template not found
581      * from any available source.
582      * @throws ParseErrorException if template cannot be parsed due
583      * to syntax (or other) error.
584      * @throws Exception if an error occurs in template initialization
585      *
586      * @since Velocity v1.1
587      */

588     public Template getTemplate( String JavaDoc name, String JavaDoc encoding )
589         throws ResourceNotFoundException, ParseErrorException, Exception JavaDoc
590     {
591         return RuntimeSingleton.getTemplate( name, encoding );
592     }
593
594     /**
595      * Implement this method to add your application data to the context,
596      * calling the <code>getTemplate()</code> method to produce your return
597      * value.
598      * <br><br>
599      * In the event of a problem, you may handle the request directly
600      * and return <code>null</code> or throw a more meaningful exception
601      * for the error handler to catch.
602      *
603      * @param request servlet request from client
604      * @param response servlet reponse
605      * @param ctx The context to add your data to.
606      * @return The template to merge with your context or null, indicating
607      * that you handled the processing.
608      *
609      * @since Velocity v1.1
610      */

611     protected Template handleRequest( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Context JavaDoc ctx )
612         throws Exception JavaDoc
613     {
614         /*
615          * invoke handleRequest
616          */

617
618         Template t = handleRequest( ctx );
619
620         /*
621          * if it returns null, this is the 'old' deprecated
622          * way, and we want to mimic the behavior for a little
623          * while anyway
624          */

625
626         if (t == null)
627         {
628             throw new Exception JavaDoc ("handleRequest(Context) returned null - no template selected!" );
629         }
630
631         return t;
632     }
633
634     /**
635      * Implement this method to add your application data to the context,
636      * calling the <code>getTemplate()</code> method to produce your return
637      * value.
638      * <br><br>
639      * In the event of a problem, you may simple return <code>null</code>
640      * or throw a more meaningful exception.
641      *
642      * @deprecated Use
643      * {@link #handleRequest( HttpServletRequest request,
644      * HttpServletResponse response, Context ctx )}
645      *
646      * @param ctx The context to add your data to.
647      * @return The template to merge with your context.
648      */

649     protected Template handleRequest( Context JavaDoc ctx )
650         throws Exception JavaDoc
651     {
652         throw new Exception JavaDoc ("You must override VelocityServlet.handleRequest( Context) "
653                              + " or VelocityServlet.handleRequest( HttpServletRequest, "
654                              + " HttpServletResponse, Context)" );
655     }
656  
657     /**
658      * Invoked when there is an error thrown in any part of doRequest() processing.
659      * <br><br>
660      * Default will send a simple HTML response indicating there was a problem.
661      *
662      * @param request original HttpServletRequest from servlet container.
663      * @param response HttpServletResponse object from servlet container.
664      * @param cause Exception that was thrown by some other part of process.
665      */

666     protected void error( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Exception JavaDoc cause )
667         throws ServletException JavaDoc, IOException JavaDoc
668     {
669         StringBuffer JavaDoc html = new StringBuffer JavaDoc();
670         html.append("<html>");
671         html.append("<title>Error</title>");
672         html.append("<body bgcolor=\"#ffffff\">");
673         html.append("<h2>VelocityServlet: Error processing the template</h2>");
674         html.append("<pre>");
675         String JavaDoc why = cause.getMessage();
676         if (why != null && why.trim().length() > 0)
677         {
678             html.append(why);
679             html.append("<br>");
680         }
681
682         StringWriter JavaDoc sw = new StringWriter JavaDoc();
683         cause.printStackTrace( new PrintWriter JavaDoc( sw ) );
684
685         html.append( sw.toString() );
686         html.append("</pre>");
687         html.append("</body>");
688         html.append("</html>");
689         response.getOutputStream().print( html.toString() );
690     }
691 }
692
Popular Tags