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

org.springframework.web.servlet.FrameworkServlet Maven / Gradle / Ivy

There is a newer version: 5.3.34
Show newest version
/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed 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 org.springframework.web.servlet;

import java.io.IOException;
import java.security.Principal;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.ServletRequestHandledEvent;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.util.NestedServletException;
import org.springframework.web.util.WebUtils;

/**
 * Base servlet for Spring's web framework. Provides integration with
 * a Spring application context, in a JavaBean-based overall solution.
 *
 * 

This class offers the following functionality: *

    *
  • Manages a {@link org.springframework.web.context.WebApplicationContext} * instance per servlet. The servlet's configuration is determined by beans * in the servlet's namespace. *
  • Publishes events on request processing, whether or not a request is * successfully handled. *
* *

Subclasses must implement {@link #doService} to handle requests. Because this extends * {@link HttpServletBean} rather than HttpServlet directly, bean properties are * automatically mapped onto it. Subclasses can override {@link #initFrameworkServlet()} * for custom initialization. * *

Detects a "contextClass" parameter at the servlet init-param level, * falling back to the default context class, * {@link org.springframework.web.context.support.XmlWebApplicationContext}, * if not found. Note that, with the default FrameworkServlet, * a custom context class needs to implement the * {@link org.springframework.web.context.ConfigurableWebApplicationContext} SPI. * *

Passes a "contextConfigLocation" servlet init-param to the context instance, * parsing it into potentially multiple file paths which can be separated by any * number of commas and spaces, like "test-servlet.xml, myServlet.xml". * If not explicitly specified, the context implementation is supposed to build a * default location from the namespace of the servlet. * *

Note: In case of multiple config locations, later bean definitions will * override ones defined in earlier loaded files, at least when using Spring's * default ApplicationContext implementation. This can be leveraged to * deliberately override certain bean definitions via an extra XML file. * *

The default namespace is "'servlet-name'-servlet", e.g. "test-servlet" for a * servlet-name "test" (leading to a "/WEB-INF/test-servlet.xml" default location * with XmlWebApplicationContext). The namespace can also be set explicitly via * the "namespace" servlet init-param. * * @author Rod Johnson * @author Juergen Hoeller * @see #doService * @see #setContextClass * @see #setContextConfigLocation * @see #setNamespace */ public abstract class FrameworkServlet extends HttpServletBean implements ApplicationListener { /** * Suffix for WebApplicationContext namespaces. If a servlet of this class is * given the name "test" in a context, the namespace used by the servlet will * resolve to "test-servlet". */ public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet"; /** * Default context class for FrameworkServlet. * @see org.springframework.web.context.support.XmlWebApplicationContext */ public static final Class DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; /** * Prefix for the ServletContext attribute for the WebApplicationContext. * The completion is the servlet name. */ public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT."; /** WebApplicationContext implementation class to use */ private Class contextClass = DEFAULT_CONTEXT_CLASS; /** Namespace for this servlet */ private String namespace; /** Explicit context config location */ private String contextConfigLocation; /** Should we publish the context as a ServletContext attribute? */ private boolean publishContext = true; /** Should we publish a ServletRequestHandledEvent at the end of each request? */ private boolean publishEvents = true; /** WebApplicationContext for this servlet */ private WebApplicationContext webApplicationContext; /** Flag used to detect whether onRefresh has already been called */ private boolean refreshEventReceived = false; /** * Set a custom context class. This class must be of type * {@link org.springframework.web.context.WebApplicationContext}. *

When using the default FrameworkServlet implementation, * the context class must also implement the * {@link org.springframework.web.context.ConfigurableWebApplicationContext} * interface. * @see #createWebApplicationContext */ public void setContextClass(Class contextClass) { this.contextClass = contextClass; } /** * Return the custom context class. */ public Class getContextClass() { return this.contextClass; } /** * Set a custom namespace for this servlet, * to be used for building a default context config location. */ public void setNamespace(String namespace) { this.namespace = namespace; } /** * Return the namespace for this servlet, falling back to default scheme if * no custom namespace was set: e.g. "test-servlet" for a servlet named "test". */ public String getNamespace() { return (this.namespace != null) ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX; } /** * Set the context config location explicitly, instead of relying on the default * location built from the namespace. This location string can consist of * multiple locations separated by any number of commas and spaces. */ public void setContextConfigLocation(String contextConfigLocation) { this.contextConfigLocation = contextConfigLocation; } /** * Return the explicit context config location, if any. */ public String getContextConfigLocation() { return this.contextConfigLocation; } /** * Set whether to publish this servlet's context as a ServletContext attribute, * available to all objects in the web container. Default is "true". *

This is especially handy during testing, although it is debatable whether * it's good practice to let other application objects access the context this way. */ public void setPublishContext(boolean publishContext) { this.publishContext = publishContext; } /** * Return whether to publish this servlet's context as a ServletContext attribute. */ public boolean isPublishContext() { return this.publishContext; } /** * Set whether this servlet should publish a ServletRequestHandledEvent at the end * of each request. Default is "true"; can be turned off for a slight performance * improvement, provided that no ApplicationListeners rely on such events. * @see org.springframework.web.context.support.ServletRequestHandledEvent */ public void setPublishEvents(boolean publishEvents) { this.publishEvents = publishEvents; } /** * Return whether this servlet should publish a ServletRequestHandledEvent * at the end of each request. */ public boolean isPublishEvents() { return this.publishEvents; } /** * Overridden method of {@link HttpServletBean}, invoked after any bean properties * have been set. Creates this servlet's WebApplicationContext. */ protected final void initServletBean() throws ServletException, BeansException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { logger.error("Context initialization failed", ex); throw ex; } catch (BeansException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } } /** * Initialize and publish the WebApplicationContext for this servlet. *

Delegates to {@link #createWebApplicationContext} for actual creation * of the context. Can be overridden in subclasses. * @return the WebApplicationContext instance * @throws BeansException if the context couldn't be initialized * @see #setContextClass * @see #setContextConfigLocation */ protected WebApplicationContext initWebApplicationContext() throws BeansException { WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = createWebApplicationContext(parent); if (!this.refreshEventReceived) { // Apparently not a ConfigurableApplicationContext with refresh support: // triggering initial onRefresh manually here. onRefresh(wac); } if (isPublishContext()) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (logger.isDebugEnabled()) { logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; } /** * Instantiate the WebApplicationContext for this servlet, either a default * {@link org.springframework.web.context.support.XmlWebApplicationContext} * or a {@link #setContextClass custom context class}, if set. *

This implementation expects custom contexts to implement the * {@link org.springframework.web.context.ConfigurableWebApplicationContext} * interface. Can be overridden in subclasses. *

Do not forget to register this servlet instance as application listener on the * created context (for triggering its {@link #onRefresh callback}, and to call * {@link org.springframework.context.ConfigurableApplicationContext#refresh()} * before returning the context instance. * @param parent the parent ApplicationContext to use, or null if none * @return the WebApplicationContext for this servlet * @throws BeansException if the context couldn't be initialized * @see org.springframework.web.context.support.XmlWebApplicationContext */ protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + getContextClass().getName() + "'" + ", using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(getContextClass())) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + getContextClass().getName() + "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass()); wac.setParent(parent); wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); if (getContextConfigLocation() != null) { wac.setConfigLocations( StringUtils.tokenizeToStringArray( getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS)); } wac.addApplicationListener(this); wac.refresh(); return wac; } /** * Return the ServletContext attribute name for this servlet's WebApplicationContext. *

The default implementation returns * SERVLET_CONTEXT_PREFIX + servlet name. * @see #SERVLET_CONTEXT_PREFIX * @see #getServletName */ public String getServletContextAttributeName() { return SERVLET_CONTEXT_PREFIX + getServletName(); } /** * Return this servlet's WebApplicationContext. */ public final WebApplicationContext getWebApplicationContext() { return this.webApplicationContext; } /** * This method will be invoked after any bean properties have been set and * the WebApplicationContext has been loaded. The default implementation is empty; * subclasses may override this method to perform any initialization they require. * @throws ServletException in case of an initialization exception * @throws BeansException if thrown by ApplicationContext methods */ protected void initFrameworkServlet() throws ServletException, BeansException { } /** * Refresh this servlet's application context, as well as the * dependent state of the servlet. * @throws BeansException in case of errors * @see #getWebApplicationContext() * @see org.springframework.context.ConfigurableApplicationContext#refresh() */ public void refresh() throws BeansException { WebApplicationContext wac = getWebApplicationContext(); if (!(wac instanceof ConfigurableApplicationContext)) { throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac); } ((ConfigurableApplicationContext) wac).refresh(); } /** * ApplicationListener endpoint that receives events from this servlet's * WebApplicationContext. *

The default implementation calls {@link #onRefresh} in case of a * {@link org.springframework.context.event.ContextRefreshedEvent}, * triggering a refresh of this servlet's context-dependent state. * @param event the incoming ApplicationContext event */ public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent) { this.refreshEventReceived = true; onRefresh(((ContextRefreshedEvent) event).getApplicationContext()); } } /** * Template method which can be overridden to add servlet-specific refresh work. * Called after successful context refresh. *

This implementation is empty. * @param context the current WebApplicationContext * @throws BeansException in case of errors * @see #refresh() */ protected void onRefresh(ApplicationContext context) throws BeansException { // For subclasses: do nothing by default. } /** * Delegate GET requests to processRequest/doService. *

Will also be invoked by HttpServlet's default implementation of doHead, * with a NoBodyResponse that just captures the content length. * @see #doService * @see #doHead */ protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Delegate POST requests to {@link #processRequest}. * @see #doService */ protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Delegate PUT requests to {@link #processRequest}. * @see #doService */ protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Delegate DELETE requests to {@link #processRequest}. * @see #doService */ protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } /** * Process this request, publishing an event regardless of the outcome. *

The actual event handling is performed by the abstract * {@link #doService} template method. */ protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; try { doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { if (failureCause != null) { logger.debug("Could not complete request", failureCause); } else { logger.debug("Successfully completed request"); } if (isPublishEvents()) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause)); } } } /** * Determine the username for the given request. * Default implementation takes the name of the UserPrincipal, if any. * Can be overridden in subclasses. * @param request current HTTP request * @return the username, or null if none * @see javax.servlet.http.HttpServletRequest#getUserPrincipal */ protected String getUsernameForRequest(HttpServletRequest request) { Principal userPrincipal = request.getUserPrincipal(); return (userPrincipal != null ? userPrincipal.getName() : null); } /** * Subclasses must implement this method to do the work of request handling, * receiving a centralized callback for GET, POST, PUT and DELETE. *

The contract is essentially the same as that for the commonly overridden * doGet or doPost methods of HttpServlet. *

This class intercepts calls to ensure that exception handling and * event publication takes place. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure * @see javax.servlet.http.HttpServlet#doGet * @see javax.servlet.http.HttpServlet#doPost */ protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception; /** * Close the WebApplicationContext of this servlet. * @see org.springframework.context.ConfigurableApplicationContext#close() */ public void destroy() { getServletContext().log( "Destroying Spring FrameworkServlet '" + getServletName() + "'"); if (this.webApplicationContext instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) this.webApplicationContext).close(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy