views:

295

answers:

2

I have a 3rd-party servlet that I cannot modify. It accepts an init-param that I would like to externalize (from web.xml).

I can externalize a servlet filter init-param using DelegatingFilterProxy. This effectively moves the servlet filter definition into Spring where there are much more powerful externalization tools (eg: PropertyPlaceholderConfigurer, environment variables, etc.)

How can I do this for a servlet?

+1  A: 

Sounds like ServletWrapperController is what you need.

Spring Controller implementation that wraps a servlet instance which it manages internally. Such a wrapped servlet is not known outside of this controller; its entire lifecycle is covered here

 <bean id="strutsWrappingController" class="org.springframework.web.servlet.mvc.ServletWrappingController">
   <property name="servletClass" value="org.apache.struts.action.ActionServlet"/>
   <property name="servletName" value="action"/>
   <property name="initParameters">
     <props>
       <prop key="config">/WEB-INF/struts-config.xml</prop>
     </props>
   </property>
 </bean>

This will let you treat the legacy servlet like other Spring controller, so you use normal Spring MVC handler mappings to route to it.

skaffman
how do you use this as a replacment? It doesn't seem to implement the Servlet interface.
mdma
@mdma: It doesn't, it's a Spring controller. The servlet part is handled by the standard Spring `DispatcherServlet`, which forwards the request to the controller, which then invokes your Spring-managed servlet.
skaffman
+1  A: 

Looks like you need a DelegatingServletProxy class, although this doesn't exist in Spring However, I imagine it is pretty straightforward to code, using DelegatingFilterProxy as a starting-point.

A servlet only implements a handful of concrete methods, so delegating should be straightforward.

Ok, I put my money where my mouth is! EDIT: Below a basic implementation of DelegatingServletProxy.

You use it like this:

  1. Setup the usual spring context configuration/ContextListener in web.xml to set the application-wide spring context for your webapp.
  2. Add a servlet to your web.xml whose implementing class is this DelegatingServletProxy. This replaces your existing serlvet that you want to change the init-params for. You set the init params for this new servlet: proxyServletClass is the name of the class of your servlet. proxyServletParams is the name of a Properties bean in your spring config. This properties bean is used to set the init params for the proxied servlet.
  3. In your spring config, add a new Properites bean that defines the init-params

Some examples, in the spring application context

<bean name="myInitParams" class="java.util.Properties">
   <constructor-arg>
   <props>
        <prop key="initParamName">initParamValue</prop>
   </props>
   </constructor-arg>
</bean>

Example web.xml snippet

<servlet>
   <servlet-name>...</servlet-name>
    <servlet-class>
        acme.DelegatingServletProxy
    </servlet-class>
    <init-param>
         <param-name>proxyServletClass</param-name>
         <param-value>your.original.servlet.ServletClass</param-value>
    </init-param>
    <init-param>
        <param-name>proxyServletParams</param-name>
        <param-value>myServletParams</param-value>
        <!-- name of bean in spring context -->
    </init-param>
</servlet>

Here's the servlet code, it's quite long, but most of it is delegating ServletContext - the interesting stuff happens at the top. It's untested - should be considered a starting point.

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Set;

public class DelegatingServletProxy extends HttpServlet implements WebApplicationContextAware
{
    private HttpServlet delegate;

    private Properties initParams;
    private String delegateName;

    public void setDelegateName(String delegateName)
    {
        this.delegateName = delegateName;
    }

    public void init(ServletConfig config) throws ServletException
    {
        super.init(config);
        WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
        delegate = wac.getBean(delegateName, HttpServlet.class);
        delegate.init(new WrapServletConfig(config));
    }

    @Override
    public void destroy()
    {
        delegate.destroy();
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
    {
        delegate.service(req, res);
    }

    public void setInitParams(Properties initParams)
    {
        this.initParams = initParams;
    }

    private class WrapServletConfig implements ServletConfig, ServletContext
    {
        // we override ServletContext also because it exposes getInitParameterNames()/getInitParemter()
        private ServletConfig delegate;
        private ServletContext delegateContext;
        public WrapServletConfig(ServletConfig config)
        {
            this.delegate = config;
            this.delegateContext = config.getServletContext();
        }

        @Override
        public String getServletName()
        {
            return delegate.getServletName();
        }

        @Override
        public ServletContext getServletContext()
        {
            return delegate.getServletContext();
        }

        @Override
        public String getInitParameter(String s)
        {
            return initParams.getProperty(s);
        }

        @Override
        public Enumeration getInitParameterNames()
        {
            return initParams.propertyNames();
        }

        @Override
        public Object getAttribute(String s)
        {
            return delegateContext.getAttribute(s);
        }

        @Override
        public Enumeration getAttributeNames()
        {
            return delegateContext.getAttributeNames();
        }

        @Override
        public void setAttribute(String s, Object o)
        {
            delegateContext.setAttribute(s, o);
        }

        @Override
        public void removeAttribute(String s)
        {
            delegateContext.removeAttribute(s);
        }

        @Override
        public String getServletContextName()
        {
            return delegateContext.getServletContextName();
        }

        // the remainer is just straight delegation to ServletContext

        @Override
        public ServletContext getContext(String s)
        {
            return delegateContext.getContext(s);
        }

        @Override
        public int getMajorVersion()
        {
            return delegateContext.getMajorVersion();
        }

        @Override
        public int getMinorVersion()
        {
            return delegateContext.getMinorVersion();
        }

        @Override
        public String getMimeType(String s)
        {
            return delegateContext.getMimeType(s);
        }

        @Override
        public Set getResourcePaths(String s)
        {
            return delegateContext.getResourcePaths(s);
        }

        @Override
        public URL getResource(String s)
                throws MalformedURLException
        {
            return delegateContext.getResource(s);
        }

        @Override
        public InputStream getResourceAsStream(String s)
        {
            return delegateContext.getResourceAsStream(s);
        }

        @Override
        public RequestDispatcher getRequestDispatcher(String s)
        {
            return delegateContext.getRequestDispatcher(s);
        }

        @Override
        public RequestDispatcher getNamedDispatcher(String s)
        {
            return delegateContext.getNamedDispatcher(s);
        }

        @Override
        public Servlet getServlet(String s)
                throws ServletException
        {
            return delegateContext.getServlet(s);
        }

        @Override
        public Enumeration getServlets()
        {
            return delegateContext.getServlets();
        }

        @Override
        public Enumeration getServletNames()
        {
            return delegateContext.getServletNames();
        }

        @Override
        public void log(String s)
        {
            delegateContext.log(s);
        }

        @Override
        public void log(Exception e, String s)
        {
            delegateContext.log(e, s);
        }

        @Override
        public void log(String s, Throwable throwable)
        {
            delegateContext.log(s, throwable);
        }

        @Override
        public String getRealPath(String s)
        {
            return delegateContext.getRealPath(s);
        }

        @Override
        public String getServerInfo()
        {
            return delegateContext.getServerInfo();
        }
    }
}
mdma