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 * &lt;bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&gt;
056 *   &lt;property name="interceptors"&gt;
057 *     &lt;list&gt;
058 *       &lt;ref bean="openSessionInViewInterceptor"/&gt;
059 *     &lt;/list&gt;
060 *   &lt;/property&gt;
061 *   &lt;property name="mappings"&gt;
062 *     &lt;props&gt;
063 *       &lt;prop key="*.do"&gt;strutsWrappingController&lt;/prop&gt;
064 *     &lt;/props&gt;
065 *   &lt;/property&gt;
066 * &lt;/bean&gt;
067 *
068 * &lt;bean id="strutsWrappingController" class="org.springframework.web.servlet.mvc.ServletWrappingController"&gt;
069 *   &lt;property name="servletClass"&gt;
070 *     &lt;value&gt;org.apache.struts.action.ActionServlet&lt;/value&gt;
071 *   &lt;/property&gt;
072 *   &lt;property name="servletName"&gt;
073 *     &lt;value&gt;action&lt;/value&gt;
074 *   &lt;/property&gt;
075 *   &lt;property name="initParameters"&gt;
076 *     &lt;props&gt;
077 *       &lt;prop key="config"&gt;/WEB-INF/struts-config.xml&lt;/prop&gt;
078 *     &lt;/props&gt;
079 *   &lt;/property&gt;
080 * &lt;/bean&gt;</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}