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