001: package org.apache.velocity.tools.view.servlet;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.InputStream;
023: import java.io.IOException;
024: import java.io.OutputStreamWriter;
025: import java.io.PrintWriter;
026: import java.io.StringWriter;
027: import java.io.UnsupportedEncodingException;
028: import java.io.Writer;
029: import java.util.Properties;
030: import javax.servlet.ServletConfig;
031: import javax.servlet.ServletContext;
032: import javax.servlet.ServletException;
033: import javax.servlet.http.HttpServlet;
034: import javax.servlet.http.HttpServletRequest;
035: import javax.servlet.http.HttpServletResponse;
036:
037: import org.apache.commons.collections.ExtendedProperties;
038: import org.apache.commons.lang.StringEscapeUtils;
039: import org.apache.velocity.Template;
040: import org.apache.velocity.app.VelocityEngine;
041: import org.apache.velocity.context.Context;
042: import org.apache.velocity.exception.MethodInvocationException;
043: import org.apache.velocity.exception.ParseErrorException;
044: import org.apache.velocity.exception.ResourceNotFoundException;
045: import org.apache.velocity.io.VelocityWriter;
046: import org.apache.velocity.runtime.RuntimeConstants;
047: import org.apache.velocity.tools.generic.log.LogSystemCommonsLog;
048: import org.apache.velocity.tools.view.ToolboxManager;
049: import org.apache.velocity.tools.view.context.ChainedContext;
050: import org.apache.velocity.util.SimplePool;
051:
052: /**
053: * <p>A servlet to process Velocity templates. This is comparable to the
054: * the JspServlet for JSP-based applications.</p>
055: *
056: * <p>The servlet provides the following features:</p>
057: * <ul>
058: * <li>renders Velocity templates</li>
059: * <li>provides support for an auto-loaded, configurable toolbox</li>
060: * <li>provides transparent access to the servlet request attributes,
061: * servlet session attributes and servlet context attributes by
062: * auto-searching them</li>
063: * <li>logs to the logging facility of the servlet API</li>
064: * </ul>
065: *
066: * <p>VelocityViewServlet supports the following configuration parameters
067: * in web.xml:</p>
068: * <dl>
069: * <dt>org.apache.velocity.toolbox</dt>
070: * <dd>Path and name of the toolbox configuration file. The path must be
071: * relative to the web application root directory. If this parameter is
072: * not found, the servlet will check for a toolbox file at
073: * '/WEB-INF/toolbox.xml'.</dd>
074: * <dt>org.apache.velocity.properties</dt>
075: * <dd>Path and name of the Velocity configuration file. The path must be
076: * relative to the web application root directory. If this parameter
077: * is not present, Velocity will check for a properties file at
078: * '/WEB-INF/velocity.properties'. If no file is found there, then
079: * Velocity is initialized with the settings in the classpath at
080: * 'org.apache.velocity.tools.view.servlet.velocity.properties'.</dd>
081: * </dl>
082: *
083: * <p>There are methods you may wish to override to access, alter or control
084: * any part of the request processing chain. Please see the javadocs for
085: * more information on :
086: * <ul>
087: * <li> {@link #loadConfiguration} : <br>for loading Velocity properties and
088: * configuring the Velocity runtime
089: * <li> {@link #setContentType} : <br>for changing the content type on a request
090: * by request basis
091: * <li> {@link #requestCleanup} : <br>post rendering resource or other cleanup
092: * <li> {@link #error} : <br>error handling
093: * </ul>
094: * </p>
095: *
096: * @author Dave Bryson
097: * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
098: * @author <a href="mailto:sidler@teamup.com">Gabe Sidler</a>
099: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
100: * @author <a href="mailto:kjohnson@transparent.com">Kent Johnson</a>
101: * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
102: * @author Nathan Bubna
103: *
104: * @version $Id: VelocityViewServlet.java 488468 2006-12-19 00:19:30Z nbubna $
105: */
106:
107: public class VelocityViewServlet extends HttpServlet {
108:
109: /** serial version id */
110: private static final long serialVersionUID = -3329444102562079189L;
111:
112: /** The HTTP content type context key. */
113: public static final String CONTENT_TYPE = "default.contentType";
114:
115: /** The default content type for the response */
116: public static final String DEFAULT_CONTENT_TYPE = "text/html";
117:
118: /** Default encoding for the output stream */
119: public static final String DEFAULT_OUTPUT_ENCODING = "ISO-8859-1";
120:
121: /**
122: * Key used to access the ServletContext in
123: * the Velocity application attributes.
124: */
125: public static final String SERVLET_CONTEXT_KEY = ServletContext.class
126: .getName();
127:
128: /**
129: * Default Runtime properties.
130: */
131: public static final String DEFAULT_TOOLS_PROPERTIES = "/org/apache/velocity/tools/view/servlet/velocity.properties";
132:
133: /**
134: * Key used to access the toolbox configuration file path from the
135: * Servlet or webapp init parameters ("org.apache.velocity.toolbox").
136: */
137: protected static final String TOOLBOX_KEY = "org.apache.velocity.toolbox";
138:
139: /**
140: * This is the string that is looked for when getInitParameter is
141: * called ("org.apache.velocity.properties").
142: */
143: protected static final String INIT_PROPS_KEY = "org.apache.velocity.properties";
144:
145: /**
146: * Default toolbox configuration file path. If no alternate value for
147: * this is specified, the servlet will look here.
148: */
149: protected static final String DEFAULT_TOOLBOX_PATH = "/WEB-INF/toolbox.xml";
150:
151: /**
152: * Default velocity properties file path. If no alternate value for
153: * this is specified, the servlet will look here.
154: */
155: protected static final String DEFAULT_PROPERTIES_PATH = "/WEB-INF/velocity.properties";
156:
157: /** A reference to the toolbox manager. */
158: protected ToolboxManager toolboxManager = null;
159:
160: /** Cache of writers */
161: private static SimplePool writerPool = new SimplePool(40);
162:
163: /* The engine used to process templates. */
164: private VelocityEngine velocity = null;
165:
166: /**
167: * The default content type. When necessary, includes the
168: * character set to use when encoding textual output.
169: */
170: private String defaultContentType;
171:
172: /**
173: * Whether we've logged a deprecation warning for
174: * ServletResponse's <code>getOutputStream()</code>.
175: * @since VelocityTools 1.1
176: */
177: private boolean warnOfOutputStreamDeprecation = true;
178:
179: /**
180: * <p>Initializes servlet, toolbox and Velocity template engine.
181: * Called by the servlet container on loading.</p>
182: *
183: * <p>NOTE: If no charset is specified in the default.contentType
184: * property (in your velocity.properties) and you have specified
185: * an output.encoding property, then that will be used as the
186: * charset for the default content-type of pages served by this
187: * servlet.</p>
188: *
189: * @param config servlet configuation
190: */
191: public void init(ServletConfig config) throws ServletException {
192: super .init(config);
193:
194: // do whatever we have to do to init Velocity
195: initVelocity(config);
196:
197: // init this servlet's toolbox (if any)
198: initToolbox(config);
199:
200: // we can get these now that velocity is initialized
201: defaultContentType = (String) getVelocityProperty(CONTENT_TYPE,
202: DEFAULT_CONTENT_TYPE);
203:
204: String encoding = (String) getVelocityProperty(
205: RuntimeConstants.OUTPUT_ENCODING,
206: DEFAULT_OUTPUT_ENCODING);
207:
208: // For non Latin-1 encodings, ensure that the charset is
209: // included in the Content-Type header.
210: if (!DEFAULT_OUTPUT_ENCODING.equalsIgnoreCase(encoding)) {
211: int index = defaultContentType.lastIndexOf("charset");
212: if (index < 0) {
213: // the charset specifier is not yet present in header.
214: // append character encoding to default content-type
215: defaultContentType += "; charset=" + encoding;
216: } else {
217: // The user may have configuration issues.
218: velocity
219: .warn("VelocityViewServlet: Charset was already "
220: + "specified in the Content-Type property. "
221: + "Output encoding property will be ignored.");
222: }
223: }
224:
225: velocity.info("VelocityViewServlet: Default content-type is: "
226: + defaultContentType);
227: }
228:
229: /**
230: * Looks up an init parameter with the specified key in either the
231: * ServletConfig or, failing that, in the ServletContext.
232: */
233: protected String findInitParameter(ServletConfig config, String key) {
234: // check the servlet config
235: String param = config.getInitParameter(key);
236:
237: if (param == null || param.length() == 0) {
238: // check the servlet context
239: ServletContext servletContext = config.getServletContext();
240: param = servletContext.getInitParameter(key);
241: }
242: return param;
243: }
244:
245: /**
246: * Simplifies process of getting a property from VelocityEngine,
247: * because the VelocityEngine interface sucks compared to the singleton's.
248: * Use of this method assumes that {@link #initVelocity(ServletConfig)}
249: * has already been called.
250: */
251: protected String getVelocityProperty(String key, String alternate) {
252: String prop = (String) velocity.getProperty(key);
253: if (prop == null || prop.length() == 0) {
254: return alternate;
255: }
256: return prop;
257: }
258:
259: /**
260: * Returns the underlying VelocityEngine being used.
261: */
262: protected VelocityEngine getVelocityEngine() {
263: return velocity;
264: }
265:
266: /**
267: * Sets the underlying VelocityEngine
268: */
269: protected void setVelocityEngine(VelocityEngine ve) {
270: if (ve == null) {
271: throw new NullPointerException(
272: "Cannot set the VelocityEngine to null");
273: }
274: this .velocity = ve;
275: }
276:
277: /**
278: * Initializes the ServletToolboxManager for this servlet's
279: * toolbox (if any).
280: *
281: * @param config servlet configuation
282: */
283: protected void initToolbox(ServletConfig config)
284: throws ServletException {
285: /* check the servlet config and context for a toolbox param */
286: String file = findInitParameter(config, TOOLBOX_KEY);
287: if (file == null) {
288: // ok, look in the default location
289: file = DEFAULT_TOOLBOX_PATH;
290: velocity
291: .debug("VelocityViewServlet: No toolbox entry in configuration."
292: + " Looking for '"
293: + DEFAULT_TOOLBOX_PATH
294: + "'");
295: }
296:
297: /* try to get a manager for this toolbox file */
298: toolboxManager = ServletToolboxManager.getInstance(
299: getServletContext(), file);
300: }
301:
302: /**
303: * Initializes the Velocity runtime, first calling
304: * loadConfiguration(ServletConfig) to get a
305: * org.apache.commons.collections.ExtendedProperties
306: * of configuration information
307: * and then calling velocityEngine.init(). Override this
308: * to do anything to the environment before the
309: * initialization of the singleton takes place, or to
310: * initialize the singleton in other ways.
311: *
312: * @param config servlet configuration parameters
313: */
314: protected void initVelocity(ServletConfig config)
315: throws ServletException {
316: velocity = new VelocityEngine();
317: setVelocityEngine(velocity);
318:
319: // register this engine to be the default handler of log messages
320: // if the user points commons-logging to the LogSystemCommonsLog
321: LogSystemCommonsLog.setVelocityEngine(velocity);
322:
323: velocity.setApplicationAttribute(SERVLET_CONTEXT_KEY,
324: getServletContext());
325:
326: // Try reading the VelocityTools default configuration
327: try {
328: ExtendedProperties defaultProperties = loadDefaultProperties();
329: velocity.setExtendedProperties(defaultProperties);
330: } catch (Exception e) {
331: log(
332: "VelocityViewServlet: Unable to read Velocity Servlet configuration file: ",
333: e);
334:
335: // This is a fatal error...
336: throw new ServletException(e);
337: }
338:
339: // Try reading an overriding user Velocity configuration
340: try {
341: ExtendedProperties p = loadConfiguration(config);
342: velocity.setExtendedProperties(p);
343: } catch (Exception e) {
344: log(
345: "VelocityViewServlet: Unable to read Velocity configuration file: ",
346: e);
347: log("VelocityViewServlet: Using default Velocity configuration.");
348: }
349:
350: // now all is ready - init Velocity
351: try {
352: velocity.init();
353: } catch (Exception e) {
354: log("VelocityViewServlet: PANIC! unable to init()", e);
355: throw new ServletException(e);
356: }
357: }
358:
359: private ExtendedProperties loadDefaultProperties() {
360: InputStream inputStream = null;
361: ExtendedProperties defaultProperties = new ExtendedProperties();
362:
363: try {
364: inputStream = getClass().getResourceAsStream(
365: DEFAULT_TOOLS_PROPERTIES);
366: if (inputStream != null) {
367: defaultProperties.load(inputStream);
368: }
369: } catch (IOException ioe) {
370: log("Cannot load default extendedProperties!", ioe);
371: } finally {
372: try {
373: if (inputStream != null) {
374: inputStream.close();
375: }
376: } catch (IOException ioe) {
377: log("Cannot close default extendedProperties!", ioe);
378: }
379: }
380: return defaultProperties;
381: }
382:
383: /**
384: * Loads the configuration information and returns that
385: * information as an ExtendedProperties, which will be used to
386: * initialize the Velocity runtime.
387: * <br><br>
388: * Currently, this method gets the initialization parameter
389: * VelocityServlet.INIT_PROPS_KEY, which should be a file containing
390: * the configuration information.
391: * <br><br>
392: * To configure your Servlet Spec 2.2 compliant servlet runner to pass
393: * this to you, put the following in your WEB-INF/web.xml file
394: * <br>
395: * <pre>
396: * <servlet>
397: * <servlet-name> YourServlet </servlet-name>
398: * <servlet-class> your.package.YourServlet </servlet-class>
399: * <init-param>
400: * <param-name> org.apache.velocity.properties </param-name>
401: * <param-value> velocity.properties </param-value>
402: * </init-param>
403: * </servlet>
404: * </pre>
405: *
406: * Alternately, if you wish to configure an entire context in this
407: * fashion, you may use the following:
408: * <br>
409: * <pre>
410: * <context-param>
411: * <param-name> org.apache.velocity.properties </param-name>
412: * <param-value> velocity.properties </param-value>
413: * <description> Path to Velocity configuration </description>
414: * </context-param>
415: * </pre>
416: *
417: * Derived classes may do the same, or take advantage of this code to do the loading for them via :
418: * <pre>
419: * ExtendedProperties p = super.loadConfiguration(config);
420: * </pre>
421: * and then add or modify the configuration values from the file.
422: * <br>
423: *
424: * @param config ServletConfig passed to the servlets init() function
425: * Can be used to access the real path via ServletContext (hint)
426: * @return ExtendedProperties loaded with configuration values to be used
427: * to initialize the Velocity runtime.
428: * @throws IOException I/O problem accessing the specified file, if specified.
429: */
430: protected ExtendedProperties loadConfiguration(ServletConfig config)
431: throws IOException {
432: // grab the path to the custom props file (if any)
433: String propsFile = findInitParameter(config, INIT_PROPS_KEY);
434: if (propsFile == null) {
435: // ok, look in the default location for custom props
436: propsFile = DEFAULT_PROPERTIES_PATH;
437: velocity
438: .debug("VelocityViewServlet: Looking for custom properties at '"
439: + DEFAULT_PROPERTIES_PATH + "'");
440: }
441:
442: ExtendedProperties p = new ExtendedProperties();
443: InputStream is = getServletContext().getResourceAsStream(
444: propsFile);
445: if (is != null) {
446: // load the properties from the input stream
447: p.load(is);
448: velocity
449: .info("VelocityViewServlet: Using custom properties at '"
450: + propsFile + "'");
451: } else {
452: velocity
453: .debug("VelocityViewServlet: No custom properties found. "
454: + "Using default Velocity configuration.");
455: }
456: return p;
457: }
458:
459: /**
460: * Handles GET - calls doRequest()
461: */
462: public void doGet(HttpServletRequest request,
463: HttpServletResponse response) throws ServletException,
464: IOException {
465: doRequest(request, response);
466: }
467:
468: /**
469: * Handle a POST request - calls doRequest()
470: */
471: public void doPost(HttpServletRequest request,
472: HttpServletResponse response) throws ServletException,
473: IOException {
474: doRequest(request, response);
475: }
476:
477: /**
478: * Handles with both GET and POST requests
479: *
480: * @param request HttpServletRequest object containing client request
481: * @param response HttpServletResponse object for the response
482: */
483: protected void doRequest(HttpServletRequest request,
484: HttpServletResponse response) throws ServletException,
485: IOException {
486: Context context = null;
487: try {
488: // first, get a context
489: context = createContext(request, response);
490:
491: // set the content type
492: setContentType(request, response);
493:
494: // get the template
495: Template template = handleRequest(request, response,
496: context);
497:
498: // bail if we can't find the template
499: if (template == null) {
500: velocity
501: .warn("VelocityViewServlet: couldn't find template to match request.");
502: return;
503: }
504:
505: // merge the template and context
506: mergeTemplate(template, context, response);
507: } catch (Exception e) {
508: // log the exception
509: velocity
510: .error("VelocityViewServlet: Exception processing the template: "
511: + e);
512:
513: // call the error handler to let the derived class
514: // do something useful with this failure.
515: error(request, response, e);
516: } finally {
517: // call cleanup routine to let a derived class do some cleanup
518: requestCleanup(request, response, context);
519: }
520: }
521:
522: /**
523: * Cleanup routine called at the end of the request processing sequence
524: * allows a derived class to do resource cleanup or other end of
525: * process cycle tasks. This default implementation does nothing.
526: *
527: * @param request servlet request from client
528: * @param response servlet reponse
529: * @param context Context created by the {@link #createContext}
530: */
531: protected void requestCleanup(HttpServletRequest request,
532: HttpServletResponse response, Context context) {
533: }
534:
535: /**
536: * <p>Handle the template processing request.</p>
537: *
538: * @param request client request
539: * @param response client response
540: * @param ctx VelocityContext to fill
541: *
542: * @return Velocity Template object or null
543: */
544: protected Template handleRequest(HttpServletRequest request,
545: HttpServletResponse response, Context ctx) throws Exception {
546: String path = ServletUtils.getPath(request);
547: return getTemplate(path);
548: }
549:
550: /**
551: * Sets the content type of the response. This is available to be overriden
552: * by a derived class.
553: *
554: * <p>The default implementation is :
555: * <pre>
556: *
557: * response.setContentType(defaultContentType);
558: *
559: * </pre>
560: * where defaultContentType is set to the value of the default.contentType
561: * property, or "text/html" if that is not set.</p>
562: *
563: * @param request servlet request from client
564: * @param response servlet reponse to client
565: */
566: protected void setContentType(HttpServletRequest request,
567: HttpServletResponse response) {
568: response.setContentType(defaultContentType);
569: }
570:
571: /**
572: * <p>Creates and returns an initialized Velocity context.</p>
573: *
574: * A new context of class {@link ChainedContext} is created and
575: * initialized.
576: *
577: * @param request servlet request from client
578: * @param response servlet reponse to client
579: */
580: protected Context createContext(HttpServletRequest request,
581: HttpServletResponse response) {
582: ChainedContext ctx = new ChainedContext(velocity, request,
583: response, getServletContext());
584:
585: /* if we have a toolbox manager, get a toolbox from it */
586: if (toolboxManager != null) {
587: ctx.setToolbox(toolboxManager.getToolbox(ctx));
588: }
589: return ctx;
590: }
591:
592: /**
593: * Retrieves the requested template.
594: *
595: * @param name The file name of the template to retrieve relative to the
596: * template root.
597: * @return The requested template.
598: * @throws ResourceNotFoundException if template not found
599: * from any available source.
600: * @throws ParseErrorException if template cannot be parsed due
601: * to syntax (or other) error.
602: * @throws Exception if an error occurs in template initialization
603: */
604: public Template getTemplate(String name)
605: throws ResourceNotFoundException, ParseErrorException,
606: Exception {
607: return velocity.getTemplate(name);
608: }
609:
610: /**
611: * Retrieves the requested template with the specified character encoding.
612: *
613: * @param name The file name of the template to retrieve relative to the
614: * template root.
615: * @param encoding the character encoding of the template
616: * @return The requested template.
617: * @throws ResourceNotFoundException if template not found
618: * from any available source.
619: * @throws ParseErrorException if template cannot be parsed due
620: * to syntax (or other) error.
621: * @throws Exception if an error occurs in template initialization
622: */
623: public Template getTemplate(String name, String encoding)
624: throws ResourceNotFoundException, ParseErrorException,
625: Exception {
626: return velocity.getTemplate(name, encoding);
627: }
628:
629: /**
630: * Merges the template with the context. Only override this if you really, really
631: * really need to. (And don't call us with questions if it breaks :)
632: *
633: * @param template template object returned by the handleRequest() method
634: * @param context Context created by the {@link #createContext}
635: * @param response servlet reponse (used to get a Writer)
636: */
637: protected void mergeTemplate(Template template, Context context,
638: HttpServletResponse response)
639: throws ResourceNotFoundException, ParseErrorException,
640: MethodInvocationException, IOException,
641: UnsupportedEncodingException, Exception {
642: VelocityWriter vw = null;
643: Writer writer = getResponseWriter(response);
644: try {
645: vw = (VelocityWriter) writerPool.get();
646: if (vw == null) {
647: vw = new VelocityWriter(writer, 4 * 1024, true);
648: } else {
649: vw.recycle(writer);
650: }
651: performMerge(template, context, vw);
652: } finally {
653: if (vw != null) {
654: try {
655: // flush and put back into the pool
656: // don't close to allow us to play
657: // nicely with others.
658: vw.flush();
659: /* This hack sets the VelocityWriter's internal ref to the
660: * PrintWriter to null to keep memory free while
661: * the writer is pooled. See bug report #18951 */
662: vw.recycle(null);
663: writerPool.put(vw);
664: } catch (Exception e) {
665: velocity.debug("VelocityViewServlet: "
666: + "Trouble releasing VelocityWriter: "
667: + e.getMessage());
668: }
669: }
670: }
671: }
672:
673: /**
674: * This is here so developers may override it and gain access to the
675: * Writer which the template will be merged into. See
676: * <a href="http://issues.apache.org/jira/browse/VELTOOLS-7">VELTOOLS-7</a>
677: * for discussion of this.
678: *
679: * @param template template object returned by the handleRequest() method
680: * @param context Context created by the {@link #createContext}
681: * @param writer a VelocityWriter that the template is merged into
682: */
683: protected void performMerge(Template template, Context context,
684: Writer writer) throws ResourceNotFoundException,
685: ParseErrorException, MethodInvocationException, Exception {
686: template.merge(context, writer);
687: }
688:
689: /**
690: * Invoked when there is an error thrown in any part of doRequest() processing.
691: * <br><br>
692: * Default will send a simple HTML response indicating there was a problem.
693: *
694: * @param request original HttpServletRequest from servlet container.
695: * @param response HttpServletResponse object from servlet container.
696: * @param e Exception that was thrown by some other part of process.
697: */
698: protected void error(HttpServletRequest request,
699: HttpServletResponse response, Exception e)
700: throws ServletException {
701: try {
702: StringBuffer html = new StringBuffer();
703: html.append("<html>\n");
704: html.append("<head><title>Error</title></head>\n");
705: html.append("<body>\n");
706: html
707: .append("<h2>VelocityViewServlet : Error processing a template for path '");
708: html.append(ServletUtils.getPath(request));
709: html.append("'</h2>\n");
710:
711: Throwable cause = e;
712:
713: String why = cause.getMessage();
714: if (why != null && why.trim().length() > 0) {
715: html.append(StringEscapeUtils.escapeHtml(why));
716: html.append("\n<br>\n");
717: }
718:
719: // if it's an MIE, i want the real stack trace!
720: if (cause instanceof MethodInvocationException) {
721: // get the real cause
722: cause = ((MethodInvocationException) cause)
723: .getWrappedThrowable();
724: }
725:
726: StringWriter sw = new StringWriter();
727: cause.printStackTrace(new PrintWriter(sw));
728:
729: html.append("<pre>\n");
730: html.append(StringEscapeUtils.escapeHtml(sw.toString()));
731: html.append("</pre>\n");
732: html.append("</body>\n");
733: html.append("</html>");
734: getResponseWriter(response).write(html.toString());
735: } catch (Exception e2) {
736: // clearly something is quite wrong.
737: // let's log the new exception then give up and
738: // throw a servlet exception that wraps the first one
739: velocity
740: .error("VelocityViewServlet: Exception while printing error screen: "
741: + e2);
742: throw new ServletException(e);
743: }
744: }
745:
746: /**
747: * <p>Procure a Writer with correct encoding which can be used
748: * even if HttpServletResponse's <code>getOutputStream()</code> method
749: * has already been called.</p>
750: *
751: * <p>This is a transitional method which will be removed in a
752: * future version of Velocity. It is not recommended that you
753: * override this method.</p>
754: *
755: * @param response The response.
756: * @return A <code>Writer</code>, possibly created using the
757: * <code>getOutputStream()</code>.
758: */
759: protected Writer getResponseWriter(HttpServletResponse response)
760: throws UnsupportedEncodingException, IOException {
761: Writer writer = null;
762: try {
763: writer = response.getWriter();
764: } catch (IllegalStateException e) {
765: // ASSUMPTION: We already called getOutputStream(), so
766: // calls to getWriter() fail. Use of OutputStreamWriter
767: // assures our desired character set
768: if (this .warnOfOutputStreamDeprecation) {
769: this .warnOfOutputStreamDeprecation = false;
770: velocity.warn("VelocityViewServlet: "
771: + "Use of ServletResponse's getOutputStream() "
772: + "method with VelocityViewServlet is "
773: + "deprecated -- support will be removed in "
774: + "an upcoming release");
775: }
776: // Assume the encoding has been set via setContentType().
777: String encoding = response.getCharacterEncoding();
778: if (encoding == null) {
779: encoding = DEFAULT_OUTPUT_ENCODING;
780: }
781: writer = new OutputStreamWriter(response.getOutputStream(),
782: encoding);
783: }
784: return writer;
785: }
786:
787: }
|