22

Can we write an argument constructor in a Servlet? If yes, how can you call?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Biju CD
  • 4,999
  • 11
  • 34
  • 55

4 Answers4

31

Can we write an argument constructor in a Servlet?

Yes, you can but it is useless since the servlet container won't invoke it.

The proper way to do it is to use the init() method:

@Override
public void init() throws ServletException {
    String foo = getInitParameter("foo");
    String bar = getServletContext().getInitParameter("bar");
    // ...
}

In this example, getInitParameter("foo") returns the value of the <init-param> of the specific <servlet> entry in web.xml, and getServletContext().getInitParameter("bar") returns the value of the independent <context-param> in web.xml.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
dfa
  • 114,442
  • 31
  • 189
  • 228
  • 3
    servlet + web.xml was one of the first dependency injection framework of the history – dfa Aug 14 '09 at 04:55
24

The problem can be state more generically:

"According to the servlets (2.3) specification, the servlets are instantiated by the servlet engine by invoking the no-arg constructor. How can I initialize a servlet properly given that correct initialization depends on the central/global/unique/application configuration?"

Actually, you can use serlvets with constructor and/or initialize them as you like. However, it requires a little bit of plumbing.

Assuming you have a servlet with a constructor having arguments:

package org.gawi.example.servlets;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class SampleServlet extends HttpServlet
{
    private final String mMessage;

    public SampleServlet(String message)
    {
        mMessage = message;
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        response.setContentType("text/plain");
        response.getWriter().write(mMessage);
    }
}

The first thing you need is a unique servlet whithin your application, let's call it InitializationServlet, to create all the required instances. Those instances must then be exported in the servlet context to be retrieve by another servlet (explained later). The InitializationServlet may look like this:

package org.gawi.example.servlets;

import javax.servlet.*;
import javax.servlet.http.*;

public class InitializationServlet extends HttpServlet
{
    public void init() throws ServletException
    {
        SampleServlet servletA = new SampleServlet("this is servlet A");
        SampleServlet servletB = new SampleServlet("this is servlet B");
        SampleServlet servletC = new SampleServlet("this is servlet C");

        getServletContext().setAttribute("servletA", servletA);
        getServletContext().setAttribute("servletB", servletB);
        getServletContext().setAttribute("servletC", servletC);
    }
}

You see that only the init() method has been provided. This servlet is not servicing any HTTP request. Its only purpose is to store the servlet in the ServletContext. Note that you could have also use this servlet to load your application configuration. So this can act as the web-application entry point, like the main(String[] args) method of a program. This might remind you of the ContextLoaderServlet of SpringSource.

The last piece is the DelegateServlet that will effectively be instantiated by the servlet container, only this servlet will forward all the pertinent method calls to the wrapped servlet instance:

package org.gawi.example.servlets;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class DelegateHttpServlet extends HttpServlet
{
    private static final String SERVLET_CONTEXT_KEY_INIT_PARAMETER = "servletContextKey";

    private HttpServlet mServlet;

    public void init(ServletConfig servletConfig) throws ServletException
    {
        super.init(servletConfig);
        locateServlet(servletConfig);
        mServlet.init(servletConfig);
    }

    private void locateServlet(ServletConfig servletConfig) throws ServletException
    {
        String servletContextAttributeName = servletConfig.getInitParameter(SERVLET_CONTEXT_KEY_INIT_PARAMETER);
        if (servletContextAttributeName == null)
        {
            throw new ServletException("Unable to find init parameter '" + SERVLET_CONTEXT_KEY_INIT_PARAMETER + "'");
        }

        Object object = servletConfig.getServletContext().getAttribute(servletContextAttributeName);

        if (object == null)
        {
            throw new ServletException("Unable to find " + servletContextAttributeName + " in servlet context.");
        }

        if (!(object instanceof HttpServlet))
        {
            throw new ServletException("Object is not an instance of"
                                       + HttpServlet.class.getName()
                                       + ".  Class is "
                                       + object.getClass().getName()
                                       + ".");
        }

        mServlet = (HttpServlet) object;
    }

    public void destroy()
    {
        mServlet.destroy();
    }

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

}

During its initialization, the DelegateServlet will look-up the target servlet in the servlet context using the servletContextKey servlet initial argument.

The web.xml for such an application might look like that:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <display-name>Example</display-name>
  <description>Example web showing handling of servlets w/ constructors.</description>


  <servlet>
    <servlet-name>Initialization</servlet-name>
    <servlet-class>org.gawi.example.servlets.InitializationServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>A</servlet-name>
    <servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>

    <init-param>
      <param-name>servletContextKey</param-name>
      <param-value>servletA</param-value>
    </init-param>

    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>B</servlet-name>
    <servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>

    <init-param>
      <param-name>servletContextKey</param-name>
      <param-value>servletB</param-value>
    </init-param>

    <load-on-startup>3</load-on-startup>
  </servlet>

  <servlet>
    <servlet-name>C</servlet-name>
    <servlet-class>org.gawi.example.servlets.DelegateHttpServlet</servlet-class>

    <init-param>
      <param-name>servletContextKey</param-name>
      <param-value>servletC</param-value>
    </init-param>

    <load-on-startup>4</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>A</servlet-name>
    <url-pattern>/servlet/a</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>B</servlet-name>
    <url-pattern>/servlet/b</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>C</servlet-name>
    <url-pattern>/servlet/c</url-pattern>
  </servlet-mapping>

  <session-config>
    <session-timeout>5</session-timeout>
  </session-config>
</web-app>

Be sure to load the InitializationServlet first, using a low <load-on-startup> value.

The benefit of this approach is that HttpServlet objects can be handled like any other regular Java object or bean. Hence, it provides a better control over initialization: no more tricky stuff to do in the init() method, nor messy servlet init-arg handling.

gawi
  • 13,940
  • 7
  • 42
  • 78
  • How can servlets be singleton if we have parameterized constructor or a public constructor?? Will servlet container have one instace of "SampleServlet" as in your example. can you please elaborate.? – Punith Raj Sep 16 '14 at 08:11
6

You can't. Servlet is instantiated reflectively by container. If servlet spec have allowed arguments in constructor, you would have to have some complicated deployment descriptor like,

<servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>MyServlet</servlet-class>
    <servlet-argument id="1" type="string">Test<servlet-argument>
    <servlet-argument id="2" type="int">10</servlet-argument>
    <load-on-startup>1</load-on-startup>
</servlet>

I guess no one wants that.

Grzegorz Rożniecki
  • 27,415
  • 11
  • 90
  • 112
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
5

Constructors are objects managed by the application server.

For initialization, see the init() method.

Update:

Can I use a constructor in my servlet?

A: A servlet is a normal Java class, so when there are no custom constructors, there is an implicit default constructor with no arguments. Servlet containers typically use the Class.newInstance() method to load servlets, so you must be careful to add an explicit default constructor if you add non-default constructors.

source: http://www.codestyle.org/java/servlets/FAQ.shtml

Grzegorz Rożniecki
  • 27,415
  • 11
  • 90
  • 112
Tom
  • 43,810
  • 29
  • 138
  • 169