All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.servlet.AbstractHttpServlet Maven / Gradle / Ivy

There is a newer version: 3.0.21
Show newest version
/*
 *  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.
 */
package groovy.servlet;

import groovy.util.ResourceConnector;
import groovy.util.ResourceException;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;

/**
 * A base class dealing with common HTTP servlet API housekeeping aspects.
 * 

*

Resource name mangling (pattern replacement)

*

* Also implements Groovy's {@link groovy.util.ResourceConnector} in a dynamic * manner. It allows you to modify the resource name that is searched for with a * replace all operation. See {@link java.util.regex.Pattern} and * {@link java.util.regex.Matcher} for details. * The servlet init parameter names are: *

 * {@value #INIT_PARAM_RESOURCE_NAME_REGEX} = empty - defaults to null
 * resource.name.replacement = empty - defaults to null
 * resource.name.replace.all = true (default) | false means replaceFirst()
 * 
* Note: If you specify a regex, you have to specify a replacement string too! * Otherwise an exception gets raised. *

*

Logging and bug-hunting options

*

* This implementation provides a verbosity flag switching log statements. * The servlet init parameter name is: *

 * verbose = false(default) | true
 * 
*

* In order to support class-loading-troubles-debugging with Tomcat 4 or * higher, you can log the class loader responsible for loading some classes. * See GROOVY-861 for details. * The servlet init parameter name is: *

 * log.GROOVY861 = false(default) | true
 * 
*

* If you experience class-loading-troubles with Tomcat 4 (or higher) or any * other servlet container using custom class loader setups, you can fallback * to use (slower) reflection in Groovy's MetaClass implementation. Please * contact the dev team with your problem! Thanks. * The servlet init parameter name is: *

 * reflection = false(default) | true
 * 
* * @author Christian Stein * @author Roshan Dawrani (roshandawrani) */ public abstract class AbstractHttpServlet extends HttpServlet implements ResourceConnector { public static final String INIT_PARAM_RESOURCE_NAME_REGEX = "resource.name.regex"; public static final String INIT_PARAM_RESOURCE_NAME_REGEX_FLAGS = "resource.name.regex.flags"; /** * Content type of the HTTP response. */ public static final String CONTENT_TYPE_TEXT_HTML = "text/html"; /** * Servlet API include key name: path_info */ public static final String INC_PATH_INFO = "javax.servlet.include.path_info"; /* *** Not used, yet. See comments in getScriptUri(HttpServletRequest). *** * Servlet API include key name: request_uri */ public static final String INC_REQUEST_URI = "javax.servlet.include.request_uri"; /** * Servlet API include key name: servlet_path */ public static final String INC_SERVLET_PATH = "javax.servlet.include.servlet_path"; /** * Servlet (or the web application) context. */ protected ServletContext servletContext; /** * Either null or a compiled pattern read from "{@value #INIT_PARAM_RESOURCE_NAME_REGEX}" * and used in {@link AbstractHttpServlet#getScriptUri(HttpServletRequest)}. */ protected Pattern resourceNamePattern; /** * The replacement used by the resource name matcher. */ protected String resourceNameReplacement; /** * The replace method to use on the matcher. *
     * true - replaceAll(resourceNameReplacement); (default)
     * false - replaceFirst(resourceNameReplacement);
     * 
*/ protected boolean resourceNameReplaceAll; /** * Controls almost all log output. */ protected boolean verbose; /** * Encoding to use, becomes charset part of contentType. */ protected String encoding = "UTF-8"; /** * Mirrors the static value of the reflection flag in MetaClass. * See AbstractHttpServlet#logGROOVY861 */ protected boolean reflection; /** * Debug flag logging the class the class loader of the request. */ private boolean logGROOVY861; /** a.fink: it was in {@link #removeNamePrefix}, but was extracted to var for optimization*/ protected String namePrefix; /** * Initializes all fields with default values. */ public AbstractHttpServlet () { this.servletContext = null; this.resourceNameReplacement = null; this.resourceNameReplaceAll = true; this.verbose = false; this.reflection = false; this.logGROOVY861 = false; } protected void generateNamePrefixOnce () { URI uri = null; String realPath = servletContext.getRealPath("/"); if (realPath != null) { uri = new File(realPath).toURI();}//prevent NPE if in .war try { URL res = servletContext.getResource("/"); if (res != null) { uri = res.toURI(); } } catch (MalformedURLException ignore) { } catch (URISyntaxException ignore) { } if (uri != null) { try { namePrefix = uri.toURL().toExternalForm(); return; } catch (MalformedURLException e) { log("generateNamePrefixOnce [ERROR] Malformed URL for base path / == '"+ uri +'\'', e); } } namePrefix = ""; } protected String removeNamePrefix(String name) throws ResourceException { if (namePrefix == null) { generateNamePrefixOnce(); } if (name.startsWith(namePrefix)) {//usualy name has text return name.substring(namePrefix.length()); } return name; } /** * Interface method for ResourceContainer. This is used by the GroovyScriptEngine. */ public URLConnection getResourceConnection (String name) throws ResourceException { name = removeNamePrefix(name).replace('\\', '/'); //remove the leading / as we are trying with a leading / now if (name.startsWith("WEB-INF/groovy/")) { name = name.substring(15);//just for uniformity } else if (name.startsWith("/")) { name = name.substring(1); } /* * Try to locate the resource and return an opened connection to it. */ try { URL url = servletContext.getResource('/' + name); if (url == null) { url = servletContext.getResource("/WEB-INF/groovy/" + name); } if (url == null) { throw new ResourceException("Resource \"" + name + "\" not found!"); } return url.openConnection(); } catch (IOException e) { throw new ResourceException("Problems getting resource named \"" + name + "\"!", e); } } /** * Returns the include-aware uri of the script or template file. * * @param request the http request to analyze * @return the include-aware uri either parsed from request attributes or * hints provided by the servlet container */ protected String getScriptUri(HttpServletRequest request) { /* * Log some debug information for https://issues.apache.org/jira/browse/GROOVY-861 */ if (logGROOVY861) { log("Logging request class and its class loader:"); log(" c = request.getClass() :\"" + request.getClass() + "\""); log(" l = c.getClassLoader() :\"" + request.getClass().getClassLoader() + "\""); log(" l.getClass() :\"" + request.getClass().getClassLoader().getClass() + "\""); /* * Keep logging, if we're verbose. Else turn it off. */ logGROOVY861 = verbose; } // // NOTE: This piece of code is heavily inspired by Apaches Jasper2! // // http://cvs.apache.org/viewcvs.cgi/jakarta-tomcat-jasper/jasper2/ \ // src/share/org/apache/jasper/servlet/JspServlet.java?view=markup // // Why doesn't it use request.getRequestURI() or INC_REQUEST_URI? // String uri = null; String info = null; // // Check to see if the requested script/template source file has been the // target of a RequestDispatcher.include(). // uri = (String) request.getAttribute(INC_SERVLET_PATH); if (uri != null) { // // Requested script/template file has been target of // RequestDispatcher.include(). Its path is assembled from the relevant // javax.servlet.include.* request attributes and returned! // info = (String) request.getAttribute(INC_PATH_INFO); if (info != null) { uri += info; } return applyResourceNameMatcher(uri); } // // Requested script/template file has not been the target of a // RequestDispatcher.include(). Reconstruct its path from the request's // getServletPath() and getPathInfo() results. // uri = request.getServletPath(); info = request.getPathInfo(); if (info != null) { uri += info; } /* * TODO : Enable auto ".groovy" extension replacing here! * http://cvs.groovy.codehaus.org/viewrep/groovy/groovy/groovy-core/src/main/groovy/servlet/GroovyServlet.java?r=1.10#l259 */ return applyResourceNameMatcher(uri); } protected String applyResourceNameMatcher (String uri) { if (resourceNamePattern != null) {// mangle resource name with the compiled pattern. Matcher matcher = resourceNamePattern.matcher(uri); String replaced; if (resourceNameReplaceAll) { replaced = matcher.replaceAll(resourceNameReplacement); } else { replaced = matcher.replaceFirst(resourceNameReplacement); } if (!uri.equals(replaced)) { if (verbose) { log("Replaced resource name \"" + uri + "\" with \"" + replaced + "\"."); } return replaced; } } return uri; } /** * Parses the http request for the real script or template source file. * * @param request * the http request to analyze * @return a file object using an absolute file path name, or null if the * servlet container cannot translate the virtual path to a real * path for any reason (such as when the content is being made * available from a .war archive). */ protected File getScriptUriAsFile(HttpServletRequest request) { String uri = getScriptUri(request); String real = servletContext.getRealPath(uri); if (real == null) { return null; } return new File(real).getAbsoluteFile(); } /** * Overrides the generic init method to set some debug flags. * * @param config the servlet configuration provided by the container * @throws ServletException if init() method defined in super class * javax.servlet.GenericServlet throws it */ public void init(ServletConfig config) throws ServletException { /* * Never forget super.init()! */ super.init(config); /* * Grab the servlet context. */ this.servletContext = config.getServletContext(); // Get verbosity hint. String value = config.getInitParameter("verbose"); if (value != null) { this.verbose = Boolean.valueOf(value); } // get encoding value = config.getInitParameter("encoding"); if (value != null) { this.encoding = value; } // And now the real init work... if (verbose) { log("Parsing init parameters..."); } String regex = config.getInitParameter(INIT_PARAM_RESOURCE_NAME_REGEX); if (regex != null) { String replacement = config.getInitParameter("resource.name.replacement"); if (replacement == null) { Exception npex = new NullPointerException("resource.name.replacement"); String message = "Init-param 'resource.name.replacement' not specified!"; log(message, npex); throw new ServletException(message, npex); } else if ("EMPTY_STRING".equals(replacement)) {// is prohibited replacement = ""; } int flags = 0; // TODO : Parse pattern compile flags (literal names). String flagsStr = config.getInitParameter(INIT_PARAM_RESOURCE_NAME_REGEX_FLAGS); if (flagsStr != null && flagsStr.length() > 0) { flags = Integer.decode(flagsStr.trim());//throws NumberFormatException } resourceNamePattern = Pattern.compile(regex, flags); this.resourceNameReplacement = replacement; String all = config.getInitParameter("resource.name.replace.all"); if (all != null) { this.resourceNameReplaceAll = Boolean.valueOf(all.trim()); } } value = config.getInitParameter("logGROOVY861"); if (value != null) { this.logGROOVY861 = Boolean.valueOf(value); // nothing else to do here } /* * If verbose, log the parameter values. */ if (verbose) { log("(Abstract) init done. Listing some parameter name/value pairs:"); log("verbose = " + verbose); // this *is* verbose! ;) log("reflection = " + reflection); log("logGROOVY861 = " + logGROOVY861); log(INIT_PARAM_RESOURCE_NAME_REGEX + " = " + resourceNamePattern);//toString == pattern log("resource.name.replacement = " + resourceNameReplacement); } } /** * Override this method to set your variables to the Groovy binding. *

* All variables bound the binding are passed to the template source text, * e.g. the HTML file, when the template is merged. *

* The binding provided by TemplateServlet does already include some default * variables. As of this writing, they are (copied from * {@link groovy.servlet.ServletBinding}): *

    *
  • "request" : HttpServletRequest
  • *
  • "response" : HttpServletResponse
  • *
  • "context" : ServletContext
  • *
  • "application" : ServletContext
  • *
  • "session" : request.getSession(false)
  • *
*

* And via implicit hard-coded keywords: *

    *
  • "out" : response.getWriter()
  • *
  • "sout" : response.getOutputStream()
  • *
  • "html" : new MarkupBuilder(response.getWriter())
  • *
*

* The binding also provides convenient methods: *

    *
  • "forward(String path)" : request.getRequestDispatcher(path).forward(request, response);
  • *
  • "include(String path)" : request.getRequestDispatcher(path).include(request, response);
  • *
  • "redirect(String location)" : response.sendRedirect(location);
  • *
*

*

Example binding all servlet context variables: *


     * class MyServlet extends TemplateServlet {
     *
     *   protected void setVariables(ServletBinding binding) {
     *     // Bind a simple variable
     *     binding.setVariable("answer", new Long(42));
     *
     *     // Bind all servlet context attributes...
     *     ServletContext context = (ServletContext) binding.getVariable("context");
     *     Enumeration enumeration = context.getAttributeNames();
     *     while (enumeration.hasMoreElements()) {
     *       String name = (String) enumeration.nextElement();
     *       binding.setVariable(name, context.getAttribute(name));
     *     }
     *   }
     * }
     * 
* * @param binding to be modified */ protected void setVariables(ServletBinding binding) { // empty } }