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