The Web App

Many components work together to accomplish a goal, you have Models, Controllers and Views, you also have helper classes. But what ties them all together, how do you hide information and make it all thread safe.

Many developers hard code information into the java code, but what if you want to be able to change something without altering the Java code, in other words obtain the information from a deployment descriptor (web.xml) file, the servlet reads this descriptor file and obtains the information.

This is were you can use initialization parameters in servlets

servlet initialization parameters

# In the deployment descriptor file (web.xml)
<servlet>
  <servlet-name>ParamTest</servlet-name>
  <servlet-class>TestInitParams</servlet-class>

  <init-param>
    <param-name>adminEmail</param-name>
    <param-value>problems@datadisk.co.uk</param-value>
  </init-param>
</servlet>

# In the servlet code

out.println(getServletConfig().getInitParameter("adminEmail"));

Note: every servlet inherits a getServletConfig() method

The servlet has to be initialized before you can the initialize parameters, the servlets's init() must have been called first, thus (which is after the constructor has been called) getting its full servletness. Servlet parameters are only read once when the Container initializes the servlet. When the servlet is created it reads the DD and creates the name/value pairs for the servletConfig, the container never reads the init parameters again, unless you redeploy the servlet again. Tomcat can hot redeploy web applications, so you can make a change (web.xml) then hot redeploy you web application to make the changes affective.

Here is a complete example

Complete example

# In the deployment descriptor file (web.xml)
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         Version="2.4">

  <!-- Notice its inside the servlet element -->
  <servlet>
    <servlet-name>CoffeParamTest</servlet-name>
    <servlet-class>com.example.TestInitParams</servlet-class>

    <init-param>
      <param-name>adminEmail</param-name>
      <param-value>problems@datadisk.co.uk</param-value>
    </init-param>

    <init-param>
      <param-name>mainEmail</param-name>
      <param-value>sales@datadisk.co.uk</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>CoffeeParamTest</servlet-name>
    <url-pattern>/Tester.do</url-pattern>
  </servlet-mapping>
</web-app>

# In the servlet class (TestInitParams.java)
package com.example;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class TestInitParams extends HttpServlet {

  public void doGet(HttpServletRequest request, HttpServletResponse response)
                       throws IOException, ServletException {

    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("Test Init Parameters<br>");

    java.util.Enumeration e = getServletConfig().getInitParameterNames();

    while (e.hasMoreElements()) {
      out.println("<br>para name = " + e.nextlement() + "<br>");
    }

    out.println("Main Email is " + getServletConfig().getInitParameter("mainEmail"));
    out.println("<br>");
    out.println("Admin Email is " + getServletConfig().getInitParameter("adminEmail"));
  }
}

Note: the javax.servlet.* imports ServletConfig (servlet only) and ServletContext (all servlets) as seen in the picture below

To allow all parts of the web application to access the initialization parameters we use context init parameters, they work just like servlet init parameters, except that they are available to the entire webapp, not just a single servlet. That means any servlet and JSP in the app automatically has access to the context init parameters, so we don't have to worry about configuring the DD for every servlet, and when the value changes, you only have to change it in one place.

Context init parameter

# In the DD (web.xml) file
<web-app ...>
  <servlet>
    <servlet-name>CoffeParamTest</servlet-name>
    <servlet-class>com.example.TestInitParams</servlet-class>
   </servlet>
 
   <!-- Notice its not inside the servlet element but available to the whole app-->
   <context-param>
     <param-name>adminEmail</param-name>
     <param-value>problems@datadisk.co.uk</param-value>
   </context-param>
</web-app

# In the servlet code (every servlet inherits getServletContext() method)

out.println(getServletContext().getInitParameter("adminEmail"));

or

ServletContext context = getServletContext();
out.println("context.getInitParameter("adminEmail"));

So to sum up there is only one ServletContext for an entire app and all the parts of the web app share it. But each servlet in the app has its own ServletConfig. The Container makes a ServletContext when a web app is deployed and makes the context available to each Servlet and JSP (which becomes a servlet) in the web app. You maybe asking yourself what not just use the <context-param>, this is up to you but you may want to hide certain information from certain servlets, in other words only restrict a particular servlet seeing a particular value. If you use the same name in a context init parameter and a servlet init parameter, nothing will happen because there's no name space conflict since you get the parameters through two different objects (ServletContext or ServletConfig).

Remember if you change a value you must redeploy the web app in order to get the new value because a servlet is only initialized once at the beginning of its life (basically there is only a getter method in both ServletContext and ServletConfig no setter methods, so there is no way to change the values).

ServletContextListener

Context parameters cannot be anything but Strings, but what if you really want all parts of the web app to have access to a database connection?. You would normally put the DataSource lookup name in a context init parameter but what does the work of turning the String parameter into a actual DataSource reference, what you need is something that listens for a context initialization event, so that you get the context init parameters and run some code before the rest of the app can service a client. This is the job of ServletContextListener, it listens for two key events of a ServletContext life - initialization (creation) and destruction. The javax.servlet.ServerContextListener class will perform this, it will

Code example

import javax.servlet.*;

public class MyServletContextListener implements ServletContextListener {

  public void contextInitialized(ServerContextEvent event) {
    ...
    ...
  }

  public void contextDestroyed(ServerContextEevent event) {
    ...
    ...
  }
}

Note: the ServletontextListener is in the javax.servlet package

For the web app to use your listener you need to specify it in the web.xml file, the <listener> element only needs the classname and the Container will figure that this is a listener by inspecting the class and noticing the listener interface.

listener element

# web.xml

<web-app>

  <servlet>
    ...
  </servlet>

  <context-param>
    ...
  </context-param>

  <!-- Notice its not inside the servlet element but available to the whole app-->
  <!-- the listener element only need the classname -->
  <listener>
    <listener-class>com.example.MyServletContextListener</listener-class>
  </listener>
</web-app

Complete ServletContextListener Example

Now for an complete example to try an explain some of the information I have been talking about, in this example we will turn a String init parameter into an actual object a dog object (in the real world this would be a database object) . The listeners job is to get the context init parameter for the dog's breed then use that String to construct a dog object. The listener then sticks the dog object into a ServletContext attribute, so that the servlet can retrieve it. At this point the servlet has access to a shared application object (in this case a dog object) and doesn't have to read the context parameters. so the following will happen

We need three classes and one DD, all the classes will be in the com.example package

ServletContextListener MyServletContextListener.java This class implements ServletContextListener, gets the context init parameters, creates the Dog, and set the Dog as a context attribute
attribute class Dog.java The Dog class is just a plain old java class. Its job is to be the attribute value that the ServletContextListener instantiates and sets in the ServletContext, for the servlet to retrieve.
servlet ListenerTester.java This class extends HttpServlet. Its job is to verify that the listener worked by getting the Dog attribute from the context, invoking getBreed() on the Dog, printing the result to the response (in the browser)
listener class

package com.example;

import javax.servlet.*;

public class MyServletContextListener implements ServletContextListener {

  public void contextInitialized(ServletContextEvent event) {

    ServletContext sc = event.getServletContext();

    String dogBreed = sc.getInitParameter("breed");
    Dog d = new Dog(dogBreed);
    sc.setAttribute("dog", d);
  }

  public void contextDestroyed(ServletContextEvent event) {

    // nothing to do here
  }
}

attribute class package com.example;

public class Dog {
  private String breed;

  public Dog(String breed) {
    this.breed = breed;
  }

  public String getBreed() {
    return breed;
  }
}
servlet class

package com.example;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class ListenerTester extends HttpServlet {

  public void doGet(HttpServletRequest request, HttpServletResponse response)
                       throws IOException, ServletException {

    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    
    out.println("Test context attributes set by listener<br>");
    out.println("<br>");

    Dog dog = (Dog) getServletContext().getAttribute("dog");

    out.println("Dog's breed is: " + dog.getBreed());
  }
}

Deployment descriptor

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
         Version="2.4">

  <servlet>
    <servlet-name>ListenerTester</servlet-name>
    <servlet-class>com.example.ListenerTester</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ListenerTester</servlet-name>
    <url-pattern>/ListenTest.do</url-pattern>
  </servlet-mapping>

   <context-param>
     <param-name>breed</param-name>
     <param-value>Great Dane</param-value>
   </context-param>

   <listener>
    <listener-class>com.example.MyServletContextListener</listener-class>
   </listener> 
 
</web-app>

Compile the three classes and create the directory structure below and drop it into Tomcat, restart Tomcat and test http://localhost:8080/listenerTest/listenTest.do

Other Listeners

Where there's a lifecycle moment, there is usually a listener to hear about it. Besides context events, you can listen for events related to context attributes, servlet requests and attributes and HTTP sessions and session attributes.

ServletContextAttributeListener want to know if an attribute in a web app context has been added, removed or replaced
HttpSessionListener you want to know how many active sessions there are
ServletRequestListener you want to know when each time a request comes in so that you can log it
ServletRequestAttributeListener You want to know when a request attribute has been added, removed or replaced
HttpSessionBindingListener you have an attribute class and you want objects of this type to be notified when they are bound to or removed from a session
HttpSessionAttributeListener

You want to know when a session attribute has been added, removed or replaced

The difference between the HttpSessionAttributeListener and the HttpSessionBindingListener, is that the plain old HttpSessionAttributeListener is just a class that wants to know when ay type of attribute has been added, removed ore replaced in a session, but the HttpSessionBindingListener exists so that the attribute itself can find out when it has been added to or removed from a session.

ServletContextListener You want to know if a context has been created or destroyed
HttpSessionActivationListener You have an attribute class and you want objects of this type to be notified when the session to which they're bound is migrating to and from another JVM

So why are we talking all about this, well if you know anything about Entity beans, this is kind of a poor mans version.

Attributes

An attribute is an object set into one of the three other servlet API objects (ServletContext, HttpServletRequest or HttpSession). You can think of it as a name/value pair (where the name is a String and the value is an Object) in a map instance variable. The difference between an attribute and parameters are

Attributes
Parameters
Types
Application/context
Request
Session
Application/context init parameters
Request parameters
Servlet init parameters
Method to set
setAttribute(String name, Object value)
You cannot set Application and servlet init parameters - they're set in the DD
Return type
Object
String
Method to get
ServletContext.getAttribute(String name)
HttpSession.getAttribute(String name)
ServletRequest.getAttribute(String name)
ServletContext.getInitParameter(String name)
ServletConfig.getInitParameter(String name)
ServletRequest.getParameter(String name)

There are three scopes

  Accessibility Scope What's good for
Context
(not thread-safe)
Any part of the web app including servlets, JSPs, ServletContextListener, ServletContextAttribute-Listeners Lifetime of the ServletContext, which means life of the deployed app Resources you want the entire app to share including database connections, JNDI lookups, email addresses, etc
HttpSession
(not thread-safe)
Any servlet or JSP with access to this particular session. The life of the session Data and resources related to this clients session, not just a single request. i.e a shopping cart
Request
(thread-safe)
Any part of the app that has direct access to the request object. That mostly means only the servlet and JSPs to which the request is forwarded using a RequestDispatcher. also Request-related listeners The life of the request, which means until the Servlets service() method completes, in other words the life of the thread. Passing model info from the controller to the view or any other data specific to a single client request.

To try and make the context attribute thread-safe you might be thinking in synchronizing the doGet method which is a bad idea, this means that only one thread in a servlet can be running at a time (less concurrency) but it does not stop other servlets or JSPs from accessing the attribute. What you need to do is place a lock on the context, if everyone accessing the context has to first get the lock on the context object, then you are guaranteed that only one thread at a time can be getting and setting the context attribute. It only works if all of the other code that manipulates the same context attributes also synchronizes on the ServletContext, if it does not ask for the lock then the code is free to change the context attribute.

Lock the ServletContext to make context attributes thread safe

public void doGet(HttpServletRequest request, HttpServletResponse response)
                       throws IOException, ServletException {

    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    
    out.println("Test context attributes<br>");

    synchronized(getServletContext()) {

      ...
    }
}

Note:  there is only one getServletContext method which the whole app uses, remember there is only one ServletContext per web app

So to sum up we know that context attributes are not inherently thread-safe, as all pieces of the app can access context attributes, from any request, this also means session attributes as a client could open multiple browser windows thus using the same session ID even though it's coming from a different instance of the browser, to protect session attributes you must synchronize on HttpSession

Lock the HttpSession to make session attributes thread safe public void doGet(HttpServletRequest request, HttpServletResponse response)
                       throws IOException, ServletException {

    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    
    out.println("Test context attributes<br>");
    HttpSession session = request.getSession();

    synchronized(session) {

      ...
    }
}

Note: we synchronize the HttpSession this time to protect the session attributes.

When synchronizing code don't forget to follow the standard synchronized rule keep the lock for the shortest amount of time (keep the synchronized block of code as small as possible), get the lock, get in, get what you need and get out quick, so that other threads can run that code.

Only request and local variables are thread-safe, everything else is subject to manipulation by multiple threads, unless you do something to stop it.

Request attributes make sense when you want some other component of the app to take over all or part of the request. The MVC example that starts with a servlet controller but ends with a JSP view. The controller communicates with the model and gets back data that the view needs in order to build the response. There is no reason to put the data in a context or session attribute, since it applies only to this request, so we put it in the request scope and in order to take over the request we use a RequestDispatcher.

RequestDispatcher

// Code in doGet()
CoffeeExpert ce = new CoffeeExpert();
ArrayList result = be.getBrands(c);

// Put model data into Request scope
request.setAttributes("styles", result);

// Get a dispatcher fro the view JSP
RequestDispatcher view = request.getRequestDispatcher("result.jsp");

// Tell JSP to take over the request and supply it the request and response objects
view.forward(request, response);

RequestDispatchers have only two methods forward() and include() both of which take the request and response objects. You most likely use the forward() method but behind the scenes the include() method is being used by the JSP in the <jsp:include> standard action. The difference between forward and include is that forward means that you are saying that you are done doing anything else to process this request and response, but with include you are saying that something else may do some things with the request/response but when you are done i want to finish handling the request and response myself, which may well be another forward or include.

You can get a RequestDispatcher in two ways

from the ServletRequest RequestDispatcher view = request.getRequestDispatcher("result.jsp");
from a ServletContext RequestDispatcher view = getServletContext().getRequestDispatcher("/result.jsp");

Note: you must start with a forward slash as it does not start with the relative path of the source.
Calling forward() on the RequestDispatcher view.forward(request, response);

One word of advise, you cannot forward the request if you have already committed a response and by committed I mean sent the response to the client. Something like using flush() will cause the response to be sent to the client, once gone that's it.

SingleThreadModel

The SingleThreadModel ensures that servlets handle only one request at a time, the interface has no methods and guarantees that no two threads will execute concurrently in the servlets service method. The servlet can guarantee this by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances and dispatching each new request to a free servlet.

The web container has a choice it can maintain a single servlet but queue every request and process one request completely before allowing the next request to processed, or the container can create a pool of servlet instances and process each request concurrently one per servlet instance.

Although this API has been deprecated it still my well be out in the wild and is worth knowing about.