src/content/engine/1.5/xref/org/apache/velocity/servlet/VelocityServlet.html [1:719]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1 package org.apache.velocity.servlet;
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.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.OutputStreamWriter;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.io.UnsupportedEncodingException;
28 import java.util.Properties;
29
30 import javax.servlet.ServletConfig;
31 import javax.servlet.ServletContext;
32 import javax.servlet.ServletException;
33 import javax.servlet.ServletOutputStream;
34 import javax.servlet.http.HttpServlet;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.apache.velocity.Template;
39 import org.apache.velocity.VelocityContext;
40 import org.apache.velocity.app.Velocity;
41 import org.apache.velocity.context.Context;
42 import org.apache.velocity.exception.MethodInvocationException;
43 import org.apache.velocity.exception.ParseErrorException;
44 import org.apache.velocity.exception.ResourceNotFoundException;
45 import org.apache.velocity.io.VelocityWriter;
46 import org.apache.velocity.runtime.RuntimeConstants;
47 import org.apache.velocity.runtime.RuntimeSingleton;
48 import org.apache.velocity.util.SimplePool;
49
50 /**
51 * Base class which simplifies the use of Velocity with Servlets.
52 * Extend this class, implement the <code>handleRequest()</code> method,
53 * and add your data to the context. Then call
54 * <code>getTemplate("myTemplate.wm")</code>.
55 *
56 * This class puts some things into the context object that you should
57 * be aware of:
58 * <pre>
59 * "req" - The HttpServletRequest object
60 * "res" - The HttpServletResponse object
61 * </pre>
62 *
63 * There are other methods you can override to access, alter or control
64 * any part of the request processing chain. Please see the javadocs for
65 * more information on :
66 * <ul>
67 * <li> loadConfiguration() : for setting up the Velocity runtime
68 * <li> createContext() : for creating and loading the Context
69 * <li> setContentType() : for changing the content type on a request
70 * by request basis
71 * <li> handleRequest() : you <b>must</b> implement this
72 * <li> mergeTemplate() : the template rendering process
73 * <li> requestCleanup() : post rendering resource or other cleanup
74 * <li> error() : error handling
75 * </ul>
76 * <br>
77 * If you put a String with key "contentType" object into the context within either your
78 * servlet or within your template, then that will be used to override
79 * the default content type specified in the properties file.
80 *
81 * @deprecated This servlet has been replaced by VelocityViewServlet,
82 * available from the Velocity-Tools sub-project. VelocityViewServlet
83 * provides support for quick, clean MVC web development.
84 * VelocityServlet will be removed in a future version of Velocity.
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 463298 2006-10-12 16:10:32Z henning $
92 */
93 public abstract class VelocityServlet extends HttpServlet
94 {
95 /**
96 * The context key for the HTTP request object.
97 */
98 public static final String REQUEST = "req";
99
100 /**
101 * The context key for the HTTP response object.
102 */
103 public static final String RESPONSE = "res";
104
105 /**
106 * The HTTP content type context key.
107 */
108 public static final String CONTENT_TYPE = "default.contentType";
109
110 /**
111 * The default content type for the response
112 */
113 public static final String DEFAULT_CONTENT_TYPE = "text/html";
114
115
116 /**
117 * Encoding for the output stream
118 */
119 public static final String 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 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 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 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 config )
155 throws ServletException
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 VelocityServlet.defaultContentType =
168 RuntimeSingleton.getString(CONTENT_TYPE, 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 * @param config
180 * @throws ServletException
181 */
182 protected void initVelocity( ServletConfig config )
183 throws ServletException
184 {
185 try
186 {
187 /*
188 * call the overridable method to allow the
189 * derived classes a shot at altering the configuration
190 * before initializing Runtime
191 */
192
193 Properties props = loadConfiguration( config );
194
195 Velocity.init( props );
196 }
197 catch( Exception e )
198 {
199 throw new ServletException("Error initializing Velocity: " + e, e);
200 }
201 }
202
203 /**
204 * Loads the configuration information and returns that
205 * information as a Properties, which will be used to
206 * initialize the Velocity runtime.
207 * <br><br>
208 * Currently, this method gets the initialization parameter
209 * VelocityServlet.INIT_PROPS_KEY, which should be a file containing
210 * the configuration information.
211 * <br><br>
212 * To configure your Servlet Spec 2.2 compliant servlet runner to pass
213 * this to you, put the following in your WEB-INF/web.xml file
214 * <br>
215 * <pre>
216 * <servlet>
217 * <servlet-name> YourServlet </servlet-name>
218 * <servlet-class> your.package.YourServlet </servlet-class>
219 * <init-param>
220 * <param-name> org.apache.velocity.properties </param-name>
221 * <param-value> velocity.properties </param-value>
222 * </init-param>
223 * </servlet>
224 * </pre>
225 *
226 * Alternately, if you wish to configure an entire context in this
227 * fashion, you may use the following:
228 * <br>
229 * <pre>
230 * <context-param>
231 * <param-name> org.apache.velocity.properties </param-name>
232 * <param-value> velocity.properties </param-value>
233 * <description> Path to Velocity configuration </description>
234 * </context-param>
235 * </pre>
236 *
237 * Derived classes may do the same, or take advantage of this code to do the loading for them via :
238 * <pre>
239 * Properties p = super.loadConfiguration( config );
240 * </pre>
241 * and then add or modify the configuration values from the file.
242 * <br>
243 *
244 * @param config ServletConfig passed to the servlets init() function
245 * Can be used to access the real path via ServletContext (hint)
246 * @return java.util.Properties loaded with configuration values to be used
247 * to initialize the Velocity runtime.
248 * @throws FileNotFoundException if a specified file is not found.
249 * @throws IOException I/O problem accessing the specified file, if specified.
250 * @deprecated Use VelocityViewServlet from the Velocity Tools
251 * library instead.
252 */
253 protected Properties loadConfiguration(ServletConfig config)
254 throws IOException, FileNotFoundException
255 {
256 // This is a little overly complex because of legacy support
257 // for the initialization properties key "properties".
258 // References to OLD_INIT_PROPS_KEY should be removed at
259 // Velocity version 1.5.
260 String propsFile = config.getInitParameter(INIT_PROPS_KEY);
261 if (propsFile == null || propsFile.length() == 0)
262 {
263 ServletContext sc = config.getServletContext();
264 propsFile = config.getInitParameter(OLD_INIT_PROPS_KEY);
265 if (propsFile == null || propsFile.length() == 0)
266 {
267 propsFile = sc.getInitParameter(INIT_PROPS_KEY);
268 if (propsFile == null || propsFile.length() == 0)
269 {
270 propsFile = sc.getInitParameter(OLD_INIT_PROPS_KEY);
271 if (propsFile != null && propsFile.length() > 0)
272 {
273 sc.log("Use of the properties initialization " +
274 "parameter '" + OLD_INIT_PROPS_KEY + "' has " +
275 "been deprecated by '" + INIT_PROPS_KEY + '\'');
276 }
277 }
278 }
279 else
280 {
281 sc.log("Use of the properties initialization parameter '" +
282 OLD_INIT_PROPS_KEY + "' has been deprecated by '" +
283 INIT_PROPS_KEY + '\'');
284 }
285 }
286
287 /*
288 * This will attempt to find the location of the properties
289 * file from the relative path to the WAR archive (ie:
290 * docroot). Since JServ returns null for getRealPath()
291 * because it was never implemented correctly, then we know we
292 * will not have an issue with using it this way. I don't know
293 * if this will break other servlet engines, but it probably
294 * shouldn't since WAR files are the future anyways.
295 */
296
297 Properties p = new Properties();
298
299 if ( propsFile != null )
300 {
301 p.load(getServletContext().getResourceAsStream(propsFile));
302 }
303
304 return p;
305 }
306
307 /**
308 * Handles HTTP <code>GET</code> requests by calling {@link
309 * #doRequest(HttpServletRequest, HttpServletResponse)}.
310 * @param request
311 * @param response
312 * @throws ServletException
313 * @throws IOException
314 */
315 public void doGet( HttpServletRequest request, HttpServletResponse response )
316 throws ServletException, IOException
317 {
318 doRequest(request, response);
319 }
320
321 /**
322 * Handles HTTP <code>POST</code> requests by calling {@link
323 * #doRequest(HttpServletRequest, HttpServletResponse)}.
324 * @param request
325 * @param response
326 * @throws ServletException
327 * @throws IOException
328 */
329 public void doPost( HttpServletRequest request, HttpServletResponse response )
330 throws ServletException, IOException
331 {
332 doRequest(request, response);
333 }
334
335 /**
336 * Handles all requests (by default).
337 *
338 * @param request HttpServletRequest object containing client request
339 * @param response HttpServletResponse object for the response
340 * @throws ServletException
341 * @throws IOException
342 */
343 protected void doRequest(HttpServletRequest request, HttpServletResponse response )
344 throws ServletException, IOException
345 {
346 Context context = null;
347 try
348 {
349 /*
350 * first, get a context
351 */
352
353 context = createContext( request, response );
354
355 /*
356 * set the content type
357 */
358
359 setContentType( request, response );
360
361 /*
362 * let someone handle the request
363 */
364
365 Template template = handleRequest( request, response, context );
366 /*
367 * bail if we can't find the template
368 */
369
370 if ( template == null )
371 {
372 return;
373 }
374
375 /*
376 * now merge it
377 */
378
379 mergeTemplate( template, context, response );
380 }
381 catch (Exception e)
382 {
383 /*
384 * call the error handler to let the derived class
385 * do something useful with this failure.
386 */
387
388 error( request, response, e);
389 }
390 finally
391 {
392 /*
393 * call cleanup routine to let a derived class do some cleanup
394 */
395
396 requestCleanup( request, response, context );
397 }
398 }
399
400 /**
401 * A cleanup routine which is called at the end of the {@link
402 * #doRequest(HttpServletRequest, HttpServletResponse)}
403 * processing sequence, allowing a derived class to do resource
404 * cleanup or other end of process cycle tasks.
405 *
406 * @param request servlet request from client
407 * @param response servlet reponse
408 * @param context context created by the createContext() method
409 */
410 protected void requestCleanup( HttpServletRequest request, HttpServletResponse response, Context context )
411 {
412 }
413
414 /**
415 * merges the template with the context. Only override this if you really, really
416 * really need to. (And don't call us with questions if it breaks :)
417 *
418 * @param template template object returned by the handleRequest() method
419 * @param context context created by the createContext() method
420 * @param response servlet reponse (use this to get the output stream or Writer
421 * @throws ResourceNotFoundException
422 * @throws ParseErrorException
423 * @throws MethodInvocationException
424 * @throws IOException
425 * @throws UnsupportedEncodingException
426 * @throws Exception
427 */
428 protected void mergeTemplate( Template template, Context context, HttpServletResponse response )
429 throws ResourceNotFoundException, ParseErrorException,
430 MethodInvocationException, IOException, UnsupportedEncodingException, Exception
431 {
432 ServletOutputStream output = response.getOutputStream();
433 VelocityWriter vw = null;
434 // ASSUMPTION: response.setContentType() has been called.
435 String encoding = response.getCharacterEncoding();
436
437 try
438 {
439 vw = (VelocityWriter) writerPool.get();
440
441 if (vw == null)
442 {
443 vw = new VelocityWriter(new OutputStreamWriter(output,
444 encoding),
445 4 * 1024, true);
446 }
447 else
448 {
449 vw.recycle(new OutputStreamWriter(output, encoding));
450 }
451
452 template.merge(context, vw);
453 }
454 finally
455 {
456 if (vw != null)
457 {
458 try
459 {
460 /*
461 * flush and put back into the pool
462 * don't close to allow us to play
463 * nicely with others.
464 */
465 vw.flush();
466 }
467 catch (IOException e)
468 {
469 // do nothing
470 }
471
472 /*
473 * Clear the VelocityWriter's reference to its
474 * internal OutputStreamWriter to allow the latter
475 * to be GC'd while vw is pooled.
476 */
477 vw.recycle(null);
478 writerPool.put(vw);
479 }
480 }
481 }
482
483 /**
484 * Sets the content type of the response, defaulting to {@link
485 * #defaultContentType} if not overriden. Delegates to {@link
486 * #chooseCharacterEncoding(HttpServletRequest)} to select the
487 * appropriate character encoding.
488 *
489 * @param request The servlet request from the client.
490 * @param response The servlet reponse to the client.
491 */
492 protected void setContentType(HttpServletRequest request,
493 HttpServletResponse response)
494 {
495 String contentType = VelocityServlet.defaultContentType;
496 int index = contentType.lastIndexOf(';') + 1;
497 if (index <= 0 || (index < contentType.length() &&
498 contentType.indexOf("charset", index) == -1))
499 {
500 // Append the character encoding which we'd like to use.
501 String encoding = chooseCharacterEncoding(request);
502 //RuntimeSingleton.debug("Chose output encoding of '" +
503 // encoding + '\'');
504 if (!DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding))
505 {
506 contentType += "; charset=" + encoding;
507 }
508 }
509 response.setContentType(contentType);
510 //RuntimeSingleton.debug("Response Content-Type set to '" +
511 // contentType + '\'');
512 }
513
514 /**
515 * Chooses the output character encoding to be used as the value
516 * for the "charset=" portion of the HTTP Content-Type header (and
517 * thus returned by <code>response.getCharacterEncoding()</code>).
518 * Called by {@link #setContentType(HttpServletRequest,
519 * HttpServletResponse)} if an encoding isn't already specified by
520 * Content-Type. By default, chooses the value of
521 * RuntimeSingleton's <code>output.encoding</code> property.
522 *
523 * @param request The servlet request from the client.
524 * @return The chosen character encoding.
525 */
526 protected String chooseCharacterEncoding(HttpServletRequest request)
527 {
528 return RuntimeSingleton.getString(RuntimeConstants.OUTPUT_ENCODING,
529 DEFAULT_OUTPUT_ENCODING);
530 }
531
532 /**
533 * Returns a context suitable to pass to the handleRequest() method
534 * <br><br>
535 * Default implementation will create a VelocityContext object,
536 * put the HttpServletRequest and HttpServletResponse
537 * into the context accessable via the keys VelocityServlet.REQUEST and
538 * VelocityServlet.RESPONSE, respectively.
539 *
540 * @param request servlet request from client
541 * @param response servlet reponse to client
542 *
543 * @return context
544 */
545 protected Context createContext(HttpServletRequest request, HttpServletResponse response )
546 {
547 /*
548 * create a new context
549 */
550
551 VelocityContext context = new VelocityContext();
552
553 /*
554 * put the request/response objects into the context
555 * wrap the HttpServletRequest to solve the introspection
556 * problems
557 */
558
559 context.put( REQUEST, request );
560 context.put( RESPONSE, response );
561
562 return context;
563 }
564
565 /**
566 * Retrieves the requested template.
567 *
568 * @param name The file name of the template to retrieve relative to the
569 * template root.
570 * @return The requested template.
571 * @throws ResourceNotFoundException if template not found
572 * from any available source.
573 * @throws ParseErrorException if template cannot be parsed due
574 * to syntax (or other) error.
575 * @throws Exception if an error occurs in template initialization
576 */
577 public Template getTemplate( String name )
578 throws ResourceNotFoundException, ParseErrorException, Exception
579 {
580 return RuntimeSingleton.getTemplate(name);
581 }
582
583 /**
584 * Retrieves the requested template with the specified
585 * character encoding.
586 *
587 * @param name The file name of the template to retrieve relative to the
588 * template root.
589 * @param encoding the character encoding of the template
590 *
591 * @return The requested template.
592 * @throws ResourceNotFoundException if template not found
593 * from any available source.
594 * @throws ParseErrorException if template cannot be parsed due
595 * to syntax (or other) error.
596 * @throws Exception if an error occurs in template initialization
597 *
598 * @since Velocity v1.1
599 */
600 public Template getTemplate( String name, String encoding )
601 throws ResourceNotFoundException, ParseErrorException, Exception
602 {
603 return RuntimeSingleton.getTemplate( name, encoding );
604 }
605
606 /**
607 * Implement this method to add your application data to the context,
608 * calling the <code>getTemplate()</code> method to produce your return
609 * value.
610 * <br><br>
611 * In the event of a problem, you may handle the request directly
612 * and return <code>null</code> or throw a more meaningful exception
613 * for the error handler to catch.
614 *
615 * @param request servlet request from client
616 * @param response servlet reponse
617 * @param ctx The context to add your data to.
618 * @return The template to merge with your context or null, indicating
619 * that you handled the processing.
620 * @throws Exception
621 *
622 * @since Velocity v1.1
623 */
624 protected Template handleRequest( HttpServletRequest request, HttpServletResponse response, Context ctx )
625 throws Exception
626 {
627 /*
628 * invoke handleRequest
629 */
630
631 Template t = handleRequest( ctx );
632
633 /*
634 * if it returns null, this is the 'old' deprecated
635 * way, and we want to mimic the behavior for a little
636 * while anyway
637 */
638
639 if (t == null)
640 {
641 throw new Exception ("handleRequest(Context) returned null - no template selected!" );
642 }
643
644 return t;
645 }
646
647 /**
648 * Implement this method to add your application data to the context,
649 * calling the <code>getTemplate()</code> method to produce your return
650 * value.
651 * <br><br>
652 * In the event of a problem, you may simple return <code>null</code>
653 * or throw a more meaningful exception.
654 *
655 * @deprecated Use
656 * {@link #handleRequest( HttpServletRequest request,
657 * HttpServletResponse response, Context ctx )}
658 *
659 * @param ctx The context to add your data to.
660 * @return The template to merge with your context.
661 * @throws Exception
662 */
663 protected Template handleRequest( Context ctx )
664 throws Exception
665 {
666 throw new Exception ("You must override VelocityServlet.handleRequest( Context) "
667 + " or VelocityServlet.handleRequest( HttpServletRequest, "
668 + " HttpServletResponse, Context)" );
669 }
670
671 /**
672 * Invoked when there is an error thrown in any part of doRequest() processing.
673 * <br><br>
674 * Default will send a simple HTML response indicating there was a problem.
675 *
676 * @param request original HttpServletRequest from servlet container.
677 * @param response HttpServletResponse object from servlet container.
678 * @param cause Exception that was thrown by some other part of process.
679 * @throws ServletException
680 * @throws IOException
681 */
682 protected void error( HttpServletRequest request, HttpServletResponse response, Exception cause )
683 throws ServletException, IOException
684 {
685 StringBuffer html = new StringBuffer();
686 html.append("<html>");
687 html.append("<title>Error</title>");
688 html.append("<body bgcolor=\"#ffffff\">");
689 html.append("<h2>VelocityServlet: Error processing the template</h2>");
690 html.append("<pre>");
691 String why = cause.getMessage();
692 if (why != null && why.trim().length() > 0)
693 {
694 html.append(why);
695 html.append("<br>");
696 }
697
698 StringWriter sw = new StringWriter();
699 cause.printStackTrace( new PrintWriter( sw ) );
700
701 html.append( sw.toString() );
702 html.append("</pre>");
703 html.append("</body>");
704 html.append("</html>");
705 response.getOutputStream().print( html.toString() );
706 }
707 }
1 package org.apache.velocity.servlet;
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.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.OutputStreamWriter;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.io.UnsupportedEncodingException;
28 import java.util.Properties;
29
30 import javax.servlet.ServletConfig;
31 import javax.servlet.ServletContext;
32 import javax.servlet.ServletException;
33 import javax.servlet.ServletOutputStream;
34 import javax.servlet.http.HttpServlet;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.apache.velocity.Template;
39 import org.apache.velocity.VelocityContext;
40 import org.apache.velocity.app.Velocity;
41 import org.apache.velocity.context.Context;
42 import org.apache.velocity.exception.MethodInvocationException;
43 import org.apache.velocity.exception.ParseErrorException;
44 import org.apache.velocity.exception.ResourceNotFoundException;
45 import org.apache.velocity.io.VelocityWriter;
46 import org.apache.velocity.runtime.RuntimeConstants;
47 import org.apache.velocity.runtime.RuntimeSingleton;
48 import org.apache.velocity.util.SimplePool;
49
50 /**
51 * Base class which simplifies the use of Velocity with Servlets.
52 * Extend this class, implement the <code>handleRequest()</code> method,
53 * and add your data to the context. Then call
54 * <code>getTemplate("myTemplate.wm")</code>.
55 *
56 * This class puts some things into the context object that you should
57 * be aware of:
58 * <pre>
59 * "req" - The HttpServletRequest object
60 * "res" - The HttpServletResponse object
61 * </pre>
62 *
63 * There are other methods you can override to access, alter or control
64 * any part of the request processing chain. Please see the javadocs for
65 * more information on :
66 * <ul>
67 * <li> loadConfiguration() : for setting up the Velocity runtime
68 * <li> createContext() : for creating and loading the Context
69 * <li> setContentType() : for changing the content type on a request
70 * by request basis
71 * <li> handleRequest() : you <b>must</b> implement this
72 * <li> mergeTemplate() : the template rendering process
73 * <li> requestCleanup() : post rendering resource or other cleanup
74 * <li> error() : error handling
75 * </ul>
76 * <br>
77 * If you put a String with key "contentType" object into the context within either your
78 * servlet or within your template, then that will be used to override
79 * the default content type specified in the properties file.
80 *
81 * @deprecated This servlet has been replaced by VelocityViewServlet,
82 * available from the Velocity-Tools sub-project. VelocityViewServlet
83 * provides support for quick, clean MVC web development.
84 * VelocityServlet will be removed in a future version of Velocity.
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 463298 2006-10-12 16:10:32Z henning $
92 */
93 public abstract class VelocityServlet extends HttpServlet
94 {
95 /**
96 * The context key for the HTTP request object.
97 */
98 public static final String REQUEST = "req";
99
100 /**
101 * The context key for the HTTP response object.
102 */
103 public static final String RESPONSE = "res";
104
105 /**
106 * The HTTP content type context key.
107 */
108 public static final String CONTENT_TYPE = "default.contentType";
109
110 /**
111 * The default content type for the response
112 */
113 public static final String DEFAULT_CONTENT_TYPE = "text/html";
114
115
116 /**
117 * Encoding for the output stream
118 */
119 public static final String 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 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 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 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 config )
155 throws ServletException
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 VelocityServlet.defaultContentType =
168 RuntimeSingleton.getString(CONTENT_TYPE, 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 * @param config
180 * @throws ServletException
181 */
182 protected void initVelocity( ServletConfig config )
183 throws ServletException
184 {
185 try
186 {
187 /*
188 * call the overridable method to allow the
189 * derived classes a shot at altering the configuration
190 * before initializing Runtime
191 */
192
193 Properties props = loadConfiguration( config );
194
195 Velocity.init( props );
196 }
197 catch( Exception e )
198 {
199 throw new ServletException("Error initializing Velocity: " + e, e);
200 }
201 }
202
203 /**
204 * Loads the configuration information and returns that
205 * information as a Properties, which will be used to
206 * initialize the Velocity runtime.
207 * <br><br>
208 * Currently, this method gets the initialization parameter
209 * VelocityServlet.INIT_PROPS_KEY, which should be a file containing
210 * the configuration information.
211 * <br><br>
212 * To configure your Servlet Spec 2.2 compliant servlet runner to pass
213 * this to you, put the following in your WEB-INF/web.xml file
214 * <br>
215 * <pre>
216 * <servlet>
217 * <servlet-name> YourServlet </servlet-name>
218 * <servlet-class> your.package.YourServlet </servlet-class>
219 * <init-param>
220 * <param-name> org.apache.velocity.properties </param-name>
221 * <param-value> velocity.properties </param-value>
222 * </init-param>
223 * </servlet>
224 * </pre>
225 *
226 * Alternately, if you wish to configure an entire context in this
227 * fashion, you may use the following:
228 * <br>
229 * <pre>
230 * <context-param>
231 * <param-name> org.apache.velocity.properties </param-name>
232 * <param-value> velocity.properties </param-value>
233 * <description> Path to Velocity configuration </description>
234 * </context-param>
235 * </pre>
236 *
237 * Derived classes may do the same, or take advantage of this code to do the loading for them via :
238 * <pre>
239 * Properties p = super.loadConfiguration( config );
240 * </pre>
241 * and then add or modify the configuration values from the file.
242 * <br>
243 *
244 * @param config ServletConfig passed to the servlets init() function
245 * Can be used to access the real path via ServletContext (hint)
246 * @return java.util.Properties loaded with configuration values to be used
247 * to initialize the Velocity runtime.
248 * @throws FileNotFoundException if a specified file is not found.
249 * @throws IOException I/O problem accessing the specified file, if specified.
250 * @deprecated Use VelocityViewServlet from the Velocity Tools
251 * library instead.
252 */
253 protected Properties loadConfiguration(ServletConfig config)
254 throws IOException, FileNotFoundException
255 {
256 // This is a little overly complex because of legacy support
257 // for the initialization properties key "properties".
258 // References to OLD_INIT_PROPS_KEY should be removed at
259 // Velocity version 1.5.
260 String propsFile = config.getInitParameter(INIT_PROPS_KEY);
261 if (propsFile == null || propsFile.length() == 0)
262 {
263 ServletContext sc = config.getServletContext();
264 propsFile = config.getInitParameter(OLD_INIT_PROPS_KEY);
265 if (propsFile == null || propsFile.length() == 0)
266 {
267 propsFile = sc.getInitParameter(INIT_PROPS_KEY);
268 if (propsFile == null || propsFile.length() == 0)
269 {
270 propsFile = sc.getInitParameter(OLD_INIT_PROPS_KEY);
271 if (propsFile != null && propsFile.length() > 0)
272 {
273 sc.log("Use of the properties initialization " +
274 "parameter '" + OLD_INIT_PROPS_KEY + "' has " +
275 "been deprecated by '" + INIT_PROPS_KEY + '\'');
276 }
277 }
278 }
279 else
280 {
281 sc.log("Use of the properties initialization parameter '" +
282 OLD_INIT_PROPS_KEY + "' has been deprecated by '" +
283 INIT_PROPS_KEY + '\'');
284 }
285 }
286
287 /*
288 * This will attempt to find the location of the properties
289 * file from the relative path to the WAR archive (ie:
290 * docroot). Since JServ returns null for getRealPath()
291 * because it was never implemented correctly, then we know we
292 * will not have an issue with using it this way. I don't know
293 * if this will break other servlet engines, but it probably
294 * shouldn't since WAR files are the future anyways.
295 */
296
297 Properties p = new Properties();
298
299 if ( propsFile != null )
300 {
301 p.load(getServletContext().getResourceAsStream(propsFile));
302 }
303
304 return p;
305 }
306
307 /**
308 * Handles HTTP <code>GET</code> requests by calling {@link
309 * #doRequest(HttpServletRequest, HttpServletResponse)}.
310 * @param request
311 * @param response
312 * @throws ServletException
313 * @throws IOException
314 */
315 public void doGet( HttpServletRequest request, HttpServletResponse response )
316 throws ServletException, IOException
317 {
318 doRequest(request, response);
319 }
320
321 /**
322 * Handles HTTP <code>POST</code> requests by calling {@link
323 * #doRequest(HttpServletRequest, HttpServletResponse)}.
324 * @param request
325 * @param response
326 * @throws ServletException
327 * @throws IOException
328 */
329 public void doPost( HttpServletRequest request, HttpServletResponse response )
330 throws ServletException, IOException
331 {
332 doRequest(request, response);
333 }
334
335 /**
336 * Handles all requests (by default).
337 *
338 * @param request HttpServletRequest object containing client request
339 * @param response HttpServletResponse object for the response
340 * @throws ServletException
341 * @throws IOException
342 */
343 protected void doRequest(HttpServletRequest request, HttpServletResponse response )
344 throws ServletException, IOException
345 {
346 Context context = null;
347 try
348 {
349 /*
350 * first, get a context
351 */
352
353 context = createContext( request, response );
354
355 /*
356 * set the content type
357 */
358
359 setContentType( request, response );
360
361 /*
362 * let someone handle the request
363 */
364
365 Template template = handleRequest( request, response, context );
366 /*
367 * bail if we can't find the template
368 */
369
370 if ( template == null )
371 {
372 return;
373 }
374
375 /*
376 * now merge it
377 */
378
379 mergeTemplate( template, context, response );
380 }
381 catch (Exception e)
382 {
383 /*
384 * call the error handler to let the derived class
385 * do something useful with this failure.
386 */
387
388 error( request, response, e);
389 }
390 finally
391 {
392 /*
393 * call cleanup routine to let a derived class do some cleanup
394 */
395
396 requestCleanup( request, response, context );
397 }
398 }
399
400 /**
401 * A cleanup routine which is called at the end of the {@link
402 * #doRequest(HttpServletRequest, HttpServletResponse)}
403 * processing sequence, allowing a derived class to do resource
404 * cleanup or other end of process cycle tasks.
405 *
406 * @param request servlet request from client
407 * @param response servlet reponse
408 * @param context context created by the createContext() method
409 */
410 protected void requestCleanup( HttpServletRequest request, HttpServletResponse response, Context context )
411 {
412 }
413
414 /**
415 * merges the template with the context. Only override this if you really, really
416 * really need to. (And don't call us with questions if it breaks :)
417 *
418 * @param template template object returned by the handleRequest() method
419 * @param context context created by the createContext() method
420 * @param response servlet reponse (use this to get the output stream or Writer
421 * @throws ResourceNotFoundException
422 * @throws ParseErrorException
423 * @throws MethodInvocationException
424 * @throws IOException
425 * @throws UnsupportedEncodingException
426 * @throws Exception
427 */
428 protected void mergeTemplate( Template template, Context context, HttpServletResponse response )
429 throws ResourceNotFoundException, ParseErrorException,
430 MethodInvocationException, IOException, UnsupportedEncodingException, Exception
431 {
432 ServletOutputStream output = response.getOutputStream();
433 VelocityWriter vw = null;
434 // ASSUMPTION: response.setContentType() has been called.
435 String encoding = response.getCharacterEncoding();
436
437 try
438 {
439 vw = (VelocityWriter) writerPool.get();
440
441 if (vw == null)
442 {
443 vw = new VelocityWriter(new OutputStreamWriter(output,
444 encoding),
445 4 * 1024, true);
446 }
447 else
448 {
449 vw.recycle(new OutputStreamWriter(output, encoding));
450 }
451
452 template.merge(context, vw);
453 }
454 finally
455 {
456 if (vw != null)
457 {
458 try
459 {
460 /*
461 * flush and put back into the pool
462 * don't close to allow us to play
463 * nicely with others.
464 */
465 vw.flush();
466 }
467 catch (IOException e)
468 {
469 // do nothing
470 }
471
472 /*
473 * Clear the VelocityWriter's reference to its
474 * internal OutputStreamWriter to allow the latter
475 * to be GC'd while vw is pooled.
476 */
477 vw.recycle(null);
478 writerPool.put(vw);
479 }
480 }
481 }
482
483 /**
484 * Sets the content type of the response, defaulting to {@link
485 * #defaultContentType} if not overriden. Delegates to {@link
486 * #chooseCharacterEncoding(HttpServletRequest)} to select the
487 * appropriate character encoding.
488 *
489 * @param request The servlet request from the client.
490 * @param response The servlet reponse to the client.
491 */
492 protected void setContentType(HttpServletRequest request,
493 HttpServletResponse response)
494 {
495 String contentType = VelocityServlet.defaultContentType;
496 int index = contentType.lastIndexOf(';') + 1;
497 if (index <= 0 || (index < contentType.length() &&
498 contentType.indexOf("charset", index) == -1))
499 {
500 // Append the character encoding which we'd like to use.
501 String encoding = chooseCharacterEncoding(request);
502 //RuntimeSingleton.debug("Chose output encoding of '" +
503 // encoding + '\'');
504 if (!DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding))
505 {
506 contentType += "; charset=" + encoding;
507 }
508 }
509 response.setContentType(contentType);
510 //RuntimeSingleton.debug("Response Content-Type set to '" +
511 // contentType + '\'');
512 }
513
514 /**
515 * Chooses the output character encoding to be used as the value
516 * for the "charset=" portion of the HTTP Content-Type header (and
517 * thus returned by <code>response.getCharacterEncoding()</code>).
518 * Called by {@link #setContentType(HttpServletRequest,
519 * HttpServletResponse)} if an encoding isn't already specified by
520 * Content-Type. By default, chooses the value of
521 * RuntimeSingleton's <code>output.encoding</code> property.
522 *
523 * @param request The servlet request from the client.
524 * @return The chosen character encoding.
525 */
526 protected String chooseCharacterEncoding(HttpServletRequest request)
527 {
528 return RuntimeSingleton.getString(RuntimeConstants.OUTPUT_ENCODING,
529 DEFAULT_OUTPUT_ENCODING);
530 }
531
532 /**
533 * Returns a context suitable to pass to the handleRequest() method
534 * <br><br>
535 * Default implementation will create a VelocityContext object,
536 * put the HttpServletRequest and HttpServletResponse
537 * into the context accessable via the keys VelocityServlet.REQUEST and
538 * VelocityServlet.RESPONSE, respectively.
539 *
540 * @param request servlet request from client
541 * @param response servlet reponse to client
542 *
543 * @return context
544 */
545 protected Context createContext(HttpServletRequest request, HttpServletResponse response )
546 {
547 /*
548 * create a new context
549 */
550
551 VelocityContext context = new VelocityContext();
552
553 /*
554 * put the request/response objects into the context
555 * wrap the HttpServletRequest to solve the introspection
556 * problems
557 */
558
559 context.put( REQUEST, request );
560 context.put( RESPONSE, response );
561
562 return context;
563 }
564
565 /**
566 * Retrieves the requested template.
567 *
568 * @param name The file name of the template to retrieve relative to the
569 * template root.
570 * @return The requested template.
571 * @throws ResourceNotFoundException if template not found
572 * from any available source.
573 * @throws ParseErrorException if template cannot be parsed due
574 * to syntax (or other) error.
575 * @throws Exception if an error occurs in template initialization
576 */
577 public Template getTemplate( String name )
578 throws ResourceNotFoundException, ParseErrorException, Exception
579 {
580 return RuntimeSingleton.getTemplate(name);
581 }
582
583 /**
584 * Retrieves the requested template with the specified
585 * character encoding.
586 *
587 * @param name The file name of the template to retrieve relative to the
588 * template root.
589 * @param encoding the character encoding of the template
590 *
591 * @return The requested template.
592 * @throws ResourceNotFoundException if template not found
593 * from any available source.
594 * @throws ParseErrorException if template cannot be parsed due
595 * to syntax (or other) error.
596 * @throws Exception if an error occurs in template initialization
597 *
598 * @since Velocity v1.1
599 */
600 public Template getTemplate( String name, String encoding )
601 throws ResourceNotFoundException, ParseErrorException, Exception
602 {
603 return RuntimeSingleton.getTemplate( name, encoding );
604 }
605
606 /**
607 * Implement this method to add your application data to the context,
608 * calling the <code>getTemplate()</code> method to produce your return
609 * value.
610 * <br><br>
611 * In the event of a problem, you may handle the request directly
612 * and return <code>null</code> or throw a more meaningful exception
613 * for the error handler to catch.
614 *
615 * @param request servlet request from client
616 * @param response servlet reponse
617 * @param ctx The context to add your data to.
618 * @return The template to merge with your context or null, indicating
619 * that you handled the processing.
620 * @throws Exception
621 *
622 * @since Velocity v1.1
623 */
624 protected Template handleRequest( HttpServletRequest request, HttpServletResponse response, Context ctx )
625 throws Exception
626 {
627 /*
628 * invoke handleRequest
629 */
630
631 Template t = handleRequest( ctx );
632
633 /*
634 * if it returns null, this is the 'old' deprecated
635 * way, and we want to mimic the behavior for a little
636 * while anyway
637 */
638
639 if (t == null)
640 {
641 throw new Exception ("handleRequest(Context) returned null - no template selected!" );
642 }
643
644 return t;
645 }
646
647 /**
648 * Implement this method to add your application data to the context,
649 * calling the <code>getTemplate()</code> method to produce your return
650 * value.
651 * <br><br>
652 * In the event of a problem, you may simple return <code>null</code>
653 * or throw a more meaningful exception.
654 *
655 * @deprecated Use
656 * {@link #handleRequest( HttpServletRequest request,
657 * HttpServletResponse response, Context ctx )}
658 *
659 * @param ctx The context to add your data to.
660 * @return The template to merge with your context.
661 * @throws Exception
662 */
663 protected Template handleRequest( Context ctx )
664 throws Exception
665 {
666 throw new Exception ("You must override VelocityServlet.handleRequest( Context) "
667 + " or VelocityServlet.handleRequest( HttpServletRequest, "
668 + " HttpServletResponse, Context)" );
669 }
670
671 /**
672 * Invoked when there is an error thrown in any part of doRequest() processing.
673 * <br><br>
674 * Default will send a simple HTML response indicating there was a problem.
675 *
676 * @param request original HttpServletRequest from servlet container.
677 * @param response HttpServletResponse object from servlet container.
678 * @param cause Exception that was thrown by some other part of process.
679 * @throws ServletException
680 * @throws IOException
681 */
682 protected void error( HttpServletRequest request, HttpServletResponse response, Exception cause )
683 throws ServletException, IOException
684 {
685 StringBuffer html = new StringBuffer();
686 html.append("<html>");
687 html.append("<title>Error</title>");
688 html.append("<body bgcolor=\"#ffffff\">");
689 html.append("<h2>VelocityServlet: Error processing the template</h2>");
690 html.append("<pre>");
691 String why = cause.getMessage();
692 if (why != null && why.trim().length() > 0)
693 {
694 html.append(why);
695 html.append("<br>");
696 }
697
698 StringWriter sw = new StringWriter();
699 cause.printStackTrace( new PrintWriter( sw ) );
700
701 html.append( sw.toString() );
702 html.append("</pre>");
703 html.append("</body>");
704 html.append("</html>");
705 response.getOutputStream().print( html.toString() );
706 }
707 }