001/* 002 * Copyright 2002-2015 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.mvc; 018 019import javax.servlet.RequestDispatcher; 020import javax.servlet.ServletException; 021import javax.servlet.http.HttpServletRequest; 022import javax.servlet.http.HttpServletResponse; 023 024import org.springframework.beans.factory.BeanNameAware; 025import org.springframework.web.servlet.ModelAndView; 026import org.springframework.web.util.WebUtils; 027 028/** 029 * Spring Controller implementation that forwards to a named servlet, 030 * i.e. the "servlet-name" in web.xml rather than a URL path mapping. 031 * A target servlet doesn't even need a "servlet-mapping" in web.xml 032 * in the first place: A "servlet" declaration is sufficient. 033 * 034 * <p>Useful to invoke an existing servlet via Spring's dispatching infrastructure, 035 * for example to apply Spring HandlerInterceptors to its requests. This will work 036 * even in a minimal Servlet container that does not support Servlet filters. 037 * 038 * <p><b>Example:</b> web.xml, mapping all "/myservlet" requests to a Spring dispatcher. 039 * Also defines a custom "myServlet", but <i>without</i> servlet mapping. 040 * 041 * <pre class="code"> 042 * <servlet> 043 * <servlet-name>myServlet</servlet-name> 044 * <servlet-class>mypackage.TestServlet</servlet-class> 045 * </servlet> 046 * 047 * <servlet> 048 * <servlet-name>myDispatcher</servlet-name> 049 * <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 050 * </servlet> 051 * 052 * <servlet-mapping> 053 * <servlet-name>myDispatcher</servlet-name> 054 * <url-pattern>/myservlet</url-pattern> 055 * </servlet-mapping></pre> 056 * 057 * <b>Example:</b> myDispatcher-servlet.xml, in turn forwarding "/myservlet" to your 058 * servlet (identified by servlet name). All such requests will go through the 059 * configured HandlerInterceptor chain (e.g. an OpenSessionInViewInterceptor). 060 * From the servlet point of view, everything will work as usual. 061 * 062 * <pre class="code"> 063 * <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> 064 * <property name="interceptors"> 065 * <list> 066 * <ref bean="openSessionInViewInterceptor"/> 067 * </list> 068 * </property> 069 * <property name="mappings"> 070 * <props> 071 * <prop key="/myservlet">myServletForwardingController</prop> 072 * </props> 073 * </property> 074 * </bean> 075 * 076 * <bean id="myServletForwardingController" class="org.springframework.web.servlet.mvc.ServletForwardingController"> 077 * <property name="servletName"><value>myServlet</value></property> 078 * </bean></pre> 079 * 080 * @author Juergen Hoeller 081 * @since 1.1.1 082 * @see ServletWrappingController 083 * @see org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor 084 * @see org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter 085 */ 086public class ServletForwardingController extends AbstractController implements BeanNameAware { 087 088 private String servletName; 089 090 private String beanName; 091 092 093 public ServletForwardingController() { 094 super(false); 095 } 096 097 098 /** 099 * Set the name of the servlet to forward to, 100 * i.e. the "servlet-name" of the target servlet in web.xml. 101 * <p>Default is the bean name of this controller. 102 */ 103 public void setServletName(String servletName) { 104 this.servletName = servletName; 105 } 106 107 @Override 108 public void setBeanName(String name) { 109 this.beanName = name; 110 if (this.servletName == null) { 111 this.servletName = name; 112 } 113 } 114 115 116 @Override 117 protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) 118 throws Exception { 119 120 RequestDispatcher rd = getServletContext().getNamedDispatcher(this.servletName); 121 if (rd == null) { 122 throw new ServletException("No servlet with name '" + this.servletName + "' defined in web.xml"); 123 } 124 // If already included, include again, else forward. 125 if (useInclude(request, response)) { 126 rd.include(request, response); 127 if (logger.isDebugEnabled()) { 128 logger.debug("Included servlet [" + this.servletName + 129 "] in ServletForwardingController '" + this.beanName + "'"); 130 } 131 } 132 else { 133 rd.forward(request, response); 134 if (logger.isDebugEnabled()) { 135 logger.debug("Forwarded to servlet [" + this.servletName + 136 "] in ServletForwardingController '" + this.beanName + "'"); 137 } 138 } 139 return null; 140 } 141 142 /** 143 * Determine whether to use RequestDispatcher's {@code include} or 144 * {@code forward} method. 145 * <p>Performs a check whether an include URI attribute is found in the request, 146 * indicating an include request, and whether the response has already been committed. 147 * In both cases, an include will be performed, as a forward is not possible anymore. 148 * @param request current HTTP request 149 * @param response current HTTP response 150 * @return {@code true} for include, {@code false} for forward 151 * @see javax.servlet.RequestDispatcher#forward 152 * @see javax.servlet.RequestDispatcher#include 153 * @see javax.servlet.ServletResponse#isCommitted 154 * @see org.springframework.web.util.WebUtils#isIncludeRequest 155 */ 156 protected boolean useInclude(HttpServletRequest request, HttpServletResponse response) { 157 return (WebUtils.isIncludeRequest(request) || response.isCommitted()); 158 } 159 160}