001/* 002 * Copyright 2002-2019 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 java.util.Enumeration; 020import java.util.Properties; 021 022import javax.servlet.Servlet; 023import javax.servlet.ServletConfig; 024import javax.servlet.ServletContext; 025import javax.servlet.http.HttpServletRequest; 026import javax.servlet.http.HttpServletResponse; 027 028import org.springframework.beans.factory.BeanNameAware; 029import org.springframework.beans.factory.DisposableBean; 030import org.springframework.beans.factory.InitializingBean; 031import org.springframework.lang.Nullable; 032import org.springframework.util.Assert; 033import org.springframework.util.ReflectionUtils; 034import org.springframework.web.servlet.ModelAndView; 035 036/** 037 * Spring Controller implementation that wraps a servlet instance which it manages 038 * internally. Such a wrapped servlet is not known outside of this controller; 039 * its entire lifecycle is covered here (in contrast to {@link ServletForwardingController}). 040 * 041 * <p>Useful to invoke an existing servlet via Spring's dispatching infrastructure, 042 * for example to apply Spring HandlerInterceptors to its requests. 043 * 044 * <p>Note that Struts has a special requirement in that it parses {@code web.xml} 045 * to find its servlet mapping. Therefore, you need to specify the DispatcherServlet's 046 * servlet name as "servletName" on this controller, so that Struts finds the 047 * DispatcherServlet's mapping (thinking that it refers to the ActionServlet). 048 * 049 * <p><b>Example:</b> a DispatcherServlet XML context, forwarding "*.do" to the Struts 050 * ActionServlet wrapped by a ServletWrappingController. All such requests will go 051 * through the configured HandlerInterceptor chain (e.g. an OpenSessionInViewInterceptor). 052 * From the Struts point of view, everything will work as usual. 053 * 054 * <pre class="code"> 055 * <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> 056 * <property name="interceptors"> 057 * <list> 058 * <ref bean="openSessionInViewInterceptor"/> 059 * </list> 060 * </property> 061 * <property name="mappings"> 062 * <props> 063 * <prop key="*.do">strutsWrappingController</prop> 064 * </props> 065 * </property> 066 * </bean> 067 * 068 * <bean id="strutsWrappingController" class="org.springframework.web.servlet.mvc.ServletWrappingController"> 069 * <property name="servletClass"> 070 * <value>org.apache.struts.action.ActionServlet</value> 071 * </property> 072 * <property name="servletName"> 073 * <value>action</value> 074 * </property> 075 * <property name="initParameters"> 076 * <props> 077 * <prop key="config">/WEB-INF/struts-config.xml</prop> 078 * </props> 079 * </property> 080 * </bean></pre> 081 * 082 * @author Juergen Hoeller 083 * @since 1.1.1 084 * @see ServletForwardingController 085 */ 086public class ServletWrappingController extends AbstractController 087 implements BeanNameAware, InitializingBean, DisposableBean { 088 089 @Nullable 090 private Class<? extends Servlet> servletClass; 091 092 @Nullable 093 private String servletName; 094 095 private Properties initParameters = new Properties(); 096 097 @Nullable 098 private String beanName; 099 100 @Nullable 101 private Servlet servletInstance; 102 103 104 public ServletWrappingController() { 105 super(false); 106 } 107 108 109 /** 110 * Set the class of the servlet to wrap. 111 * Needs to implement {@code javax.servlet.Servlet}. 112 * @see javax.servlet.Servlet 113 */ 114 public void setServletClass(Class<? extends Servlet> servletClass) { 115 this.servletClass = servletClass; 116 } 117 118 /** 119 * Set the name of the servlet to wrap. 120 * Default is the bean name of this controller. 121 */ 122 public void setServletName(String servletName) { 123 this.servletName = servletName; 124 } 125 126 /** 127 * Specify init parameters for the servlet to wrap, 128 * as name-value pairs. 129 */ 130 public void setInitParameters(Properties initParameters) { 131 this.initParameters = initParameters; 132 } 133 134 @Override 135 public void setBeanName(String name) { 136 this.beanName = name; 137 } 138 139 140 /** 141 * Initialize the wrapped Servlet instance. 142 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) 143 */ 144 @Override 145 public void afterPropertiesSet() throws Exception { 146 if (this.servletClass == null) { 147 throw new IllegalArgumentException("'servletClass' is required"); 148 } 149 if (this.servletName == null) { 150 this.servletName = this.beanName; 151 } 152 this.servletInstance = ReflectionUtils.accessibleConstructor(this.servletClass).newInstance(); 153 this.servletInstance.init(new DelegatingServletConfig()); 154 } 155 156 157 /** 158 * Invoke the wrapped Servlet instance. 159 * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 160 */ 161 @Override 162 protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) 163 throws Exception { 164 165 Assert.state(this.servletInstance != null, "No Servlet instance"); 166 this.servletInstance.service(request, response); 167 return null; 168 } 169 170 171 /** 172 * Destroy the wrapped Servlet instance. 173 * @see javax.servlet.Servlet#destroy() 174 */ 175 @Override 176 public void destroy() { 177 if (this.servletInstance != null) { 178 this.servletInstance.destroy(); 179 } 180 } 181 182 183 /** 184 * Internal implementation of the ServletConfig interface, to be passed 185 * to the wrapped servlet. Delegates to ServletWrappingController fields 186 * and methods to provide init parameters and other environment info. 187 */ 188 private class DelegatingServletConfig implements ServletConfig { 189 190 @Override 191 @Nullable 192 public String getServletName() { 193 return servletName; 194 } 195 196 @Override 197 @Nullable 198 public ServletContext getServletContext() { 199 return ServletWrappingController.this.getServletContext(); 200 } 201 202 @Override 203 public String getInitParameter(String paramName) { 204 return initParameters.getProperty(paramName); 205 } 206 207 @Override 208 @SuppressWarnings({"rawtypes", "unchecked"}) 209 public Enumeration<String> getInitParameterNames() { 210 return (Enumeration) initParameters.keys(); 211 } 212 } 213 214}