Java tutorial
package org.apache.velocity.tools.view; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringEscapeUtils; import org.apache.velocity.Template; import org.apache.velocity.context.Context; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.log.Log; /** * <p>A servlet to process Velocity templates. This is comparable to the * the JspServlet for JSP-based applications.</p> * * <p>The servlet provides the following features:</p> * <ul> * <li>renders Velocity templates</li> * <li>provides support for an auto-loaded, configurable toolbox</li> * <li>provides transparent access to the servlet request attributes, * servlet session attributes and servlet context attributes by * auto-searching them</li> * <li>logs to the logging facility of the servlet API</li> * </ul> * * <p>VelocityViewServlet supports the following configuration parameters * in web.xml:</p> * <dl> * <dt>org.apache.velocity.tools</dt> * <dd>Path and name of the toolbox configuration file. The path must be * relative to the web application root directory. If this parameter is * not found, the servlet will check for a toolbox file at * '/WEB-INF/tools.xml'.</dd> * <dt>org.apache.velocity.properties</dt> * <dd>Path and name of the Velocity configuration file. The path must be * relative to the web application root directory. If this parameter * is not present, Velocity will check for a properties file at * '/WEB-INF/velocity.properties'. If no file is found there, then * Velocity is initialized with the settings in the classpath at * 'org.apache.velocity.tools.view.velocity.properties'.</dd> * <dt>org.apache.velocity.tools.shared.config</dt> * <dd>By default, this is {@code true}. If set to {@code false}, then * the {@link VelocityView} used by this servlet will not be shared * with {@link VelocityViewFilter}s, other VelocityViewServlets or * {@link org.apache.velocity.tools.view.jsp.VelocityViewTag}s in the * application.</dd> * <dt>org.apache.velocity.tools.loadDefaults</dt> * <dd>By default, this is {@code true}. If set to {@code false}, then * the default toolbox configuration will not be added to your (if any) * custom configuration. NOTE: The default configuration will also be * suppressed if you are using a deprecated toolbox.xml format and do not * explicitly set this to {@code true}.</dd> * <dt>org.apache.velocity.tools.cleanConfiguration</dt> * <dd>By default, this is {@code false}. If set to {@code true}, then * then the final toolbox configuration (the combination of any custom * one(s) provided by yourself and/or the default configuration(s)) * will have all invalid tools, properties, and/or data removed prior to * configuring the ToolboxFactory for this servlet by a * {@link org.apache.velocity.tools.config.ConfigurationCleaner}</dd> * <dt>org.apache.velocity.tools.bufferOutput</dt> * <dd>By default, the processed templates are merged directly into * the {@link HttpServletResponse}'s writer. If this parameter is * set to {@code true}, then the output of the merge process will be * buffered before being fed to the response. This allows the {@link #error} * method to be overridden to return a "500 Internal Server Error" or * at least not return any of the failed request content. Essentially, * setting this to {@code true} degrades performance in order to enable * a more "correct" error response"</dd> * </dd> * </dl> * * @version $Id: VelocityViewServlet.java 791530 2009-07-06 16:06:52Z nbubna $ */ public class VelocityViewServlet extends HttpServlet { public static final String BUFFER_OUTPUT_PARAM = "org.apache.velocity.tools.bufferOutput"; private static final long serialVersionUID = -3329444102562079189L; private transient VelocityView view; private boolean bufferOutput = false; /** * <p>Initializes servlet and VelocityView used to process requests. * Called by the servlet container on loading.</p> * * @param config servlet configuation */ public void init(ServletConfig config) throws ServletException { super.init(config); // init the VelocityView (if it hasn't been already) getVelocityView(); String buffer = findInitParameter(config, BUFFER_OUTPUT_PARAM); if (buffer != null && buffer.equals("true")) { this.bufferOutput = true; getLog().debug("VelocityViewServlet will buffer mergeTemplate output."); } } /** * Looks up an init parameter with the specified key in either the * ServletConfig or, failing that, in the ServletContext. */ protected String findInitParameter(ServletConfig config, String key) { // check the servlet config String param = config.getInitParameter(key); if (param == null || param.length() == 0) { // check the servlet context ServletContext servletContext = config.getServletContext(); param = servletContext.getInitParameter(key); } return param; } protected VelocityView getVelocityView() { if (this.view == null) { setVelocityView(ServletUtils.getVelocityView(getServletConfig())); assert (this.view != null); } return this.view; } protected void setVelocityView(VelocityView view) { this.view = view; } protected String getVelocityProperty(String name, String alternate) { return getVelocityView().getProperty(name, alternate); } protected Log getLog() { return getVelocityView().getLog(); } /** * Handles GET - calls doRequest() */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doRequest(request, response); } /** * Handle a POST request - calls doRequest() */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doRequest(request, response); } /** * Handles with both GET and POST requests * * @param request HttpServletRequest object containing client request * @param response HttpServletResponse object for the response */ protected void doRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { Context context = null; try { // then get a context context = createContext(request, response); // call standard extension point fillContext(context, request); setContentType(request, response); // get the template Template template = handleRequest(request, response, context); // merge the template and context into the response mergeTemplate(template, context, response); } catch (IOException e) { error(request, response, e); throw e; } catch (ResourceNotFoundException e) { manageResourceNotFound(request, response, e); } catch (RuntimeException e) { error(request, response, e); throw e; } finally { requestCleanup(request, response, context); } } /** * <p>This was a common extension point, but now it is usually * simpler to override {@link #fillContext} to add custom things * to the {@link Context} or override a {@link #getTemplate} * method to change how {@link Template}s are retrieved. * This is only recommended for more complicated use-cases.</p> * * @param request client request * @param response client response * @param ctx VelocityContext to fill * @return Velocity Template object or null */ protected Template handleRequest(HttpServletRequest request, HttpServletResponse response, Context ctx) { return getTemplate(request, response); } protected Context createContext(HttpServletRequest request, HttpServletResponse response) { return getVelocityView().createContext(request, response); } protected void fillContext(Context context, HttpServletRequest request) { // this implementation does nothing } /** * Sets the content type of the response. This is available to be overriden * by a derived class. * * <p>The default implementation is : * <code> * response.setContentType(getVelocityView().getDefaultContentType()); * </code> * where defaultContentType is set to the value of the default.contentType * property, or "text/html" if that was not set for the {@link VelocityView}. * </p> * * @param request servlet request from client * @param response servlet reponse to client */ protected void setContentType(HttpServletRequest request, HttpServletResponse response) { response.setContentType(getVelocityView().getDefaultContentType()); } protected Template getTemplate(HttpServletRequest request, HttpServletResponse response) { return getVelocityView().getTemplate(request, response); } protected Template getTemplate(String name) { return getVelocityView().getTemplate(name); } protected void mergeTemplate(Template template, Context context, HttpServletResponse response) throws IOException { Writer writer; if (this.bufferOutput) { writer = new StringWriter(); } else { writer = response.getWriter(); } getVelocityView().merge(template, context, writer); if (this.bufferOutput) { response.getWriter().write(writer.toString()); } } /** * Invoked when there is an error thrown in any part of doRequest() processing. * <br><br> * Default will send a simple HTML response indicating there was a problem. * * @param request original HttpServletRequest from servlet container. * @param response HttpServletResponse object from servlet container. * @param e Exception that was thrown by some other part of process. */ protected void error(HttpServletRequest request, HttpServletResponse response, Throwable e) { if (!response.isCommitted()) { return; } try { String path = ServletUtils.getPath(request); getLog().error("Error processing a template for path '" + path + "'", e); StringBuilder html = new StringBuilder(); html.append("<html>\n"); html.append("<head><title>Error</title></head>\n"); html.append("<body>\n"); html.append("<h2>VelocityView : Error processing a template for path '"); html.append(path); html.append("'</h2>\n"); Throwable cause = e; String why = cause.getMessage(); if (why != null && why.length() > 0) { html.append(StringEscapeUtils.escapeHtml(why)); html.append("\n<br>\n"); } //TODO: add line/column/template info for parse errors et al // if it's an MIE, i want the real stack trace! if (cause instanceof MethodInvocationException) { // get the real cause cause = ((MethodInvocationException) cause).getWrappedThrowable(); } StringWriter sw = new StringWriter(); cause.printStackTrace(new PrintWriter(sw)); html.append("<pre>\n"); html.append(StringEscapeUtils.escapeHtml(sw.toString())); html.append("</pre>\n"); html.append("</body>\n"); html.append("</html>"); response.getWriter().write(html.toString()); } catch (Exception e2) { // clearly something is quite wrong. // let's log the new exception then give up and // throw a runtime exception that wraps the first one String msg = "Exception while printing error screen"; getLog().error(msg, e2); throw new RuntimeException(msg, e); } } /** * Manages the {@link ResourceNotFoundException} to send an HTTP 404 result * when needed. * * @param request The request object. * @param response The response object. * @param e The exception to check. * @throws IOException If something goes wrong when sending the HTTP error. */ protected void manageResourceNotFound(HttpServletRequest request, HttpServletResponse response, ResourceNotFoundException e) throws IOException { String path = ServletUtils.getPath(request); if (getLog().isDebugEnabled()) { getLog().debug("Resource not found for path '" + path + "'", e); } String message = e.getMessage(); if (!response.isCommitted() && path != null && message != null && message.contains("'" + path + "'")) { response.sendError(HttpServletResponse.SC_NOT_FOUND, path); } else { error(request, response, e); throw e; } } /** * Cleanup routine called at the end of the request processing sequence * allows a derived class to do resource cleanup or other end of * process cycle tasks. This default implementation does nothing. * * @param request servlet request from client * @param response servlet response * @param context Context that was merged with the requested template */ protected void requestCleanup(HttpServletRequest request, HttpServletResponse response, Context context) { } }