9. More Controller facilities




9.1 Understand the servlet lifecycle

Summary is found under 4.3 Understand fundamentals of the HttpServlet and related APIs


9.2 Describe and use more advanced elements of the servlet APIs

'Advanced elements' is rather vague. Less advanced elements found under 4.3 Understand fundamentals of the HttpServlet and related APIs

Be careful to associate the right method names to PrintWriter and OutputStream. For example:

  • ServletOutputStream uses getOutputStream and write()
  • PrintWriter uses getWriter() and println() [not getPrintWriter nor write()]

PrintWriter

PrintWriter writer = response.getWriter();
writer.println(some text and HTML);

OutputStream

ServletOutputStream out = response.getOutputStream();
out.write(aByteArray);

addHeader and setHeader

setHeader() overwrites the existing value, or works like addHeader() if the value does not exist
addHeader() adds an additional value

sendRedirect

Important: you cannot call sendRedirect() after writing to the response!
The browser URL is updated.

response.sendRedirect("http://catvideos.com"); // full URL, takes a String, cannot take a URL object
response.sendRedirect("foo/stuff"); // relative

getRequestDispatcher

The browser URL does not get updated. It can work with absolute (/) and relative paths, which are relative to the original request path. If obtaining the the RequestDispatcher from ServletContext, you cannot use a relative path.

Another trap is that you cannot use forward() after a response is already written to the client, e.g. by outputStream.write() or outputStream.flush(). However, methods such as getWriter().println() are okay, assuming the response is not committed.

The RequestDispatcher interface has two methods:

  • forward(ServletRequest, ServletResponse): used by controllers
  • include(ServletRequest, ServletResponse): rarely used, except for jsp:include tag
RequestDispatcher view = request.getRequestDispatcher("result.jsp");
view.forward(request,response);
// or use ServletContext
RequestDispatcher view = getServletContext().getRequestDispatcher("/result.jsp");
view.forward(request,response);

Where X is either include or forward, The following request attributes must be set:

javax.servlet.X.request_uri
javax.servlet.X.context_path
javax.servlet.X.servlet_path
javax.servlet.X.path_info
javax.servlet.X.query_string

The servlet to which a request is forwarded may access the original query string by calling getAttribute("javax.servlet.forward.query_string") on the ServletRequest. All of the above request attributes are always holding the **original request information even if multiple forwarding operations were made.

The execution control returns to the resource that has called the forward() method after the callee resource finishes processing.

SingleThreadModel (deprecated)

Note that SingleThreadModel is deprecated and is not required for the exam.

The SingleThreadModel interface ensures that no two threads will execute concurrently in the servlet’s service method. STM attempts to protect instance variables of the servlet. A container may pool servlet instances or use a queuing strategy.

In reality, only request attributes and local variables are thread-safe, so SingleThreadModel is useless, and therefore deprecated.


9.3 Create filters and use them in web applications

A filter sits between the container and a servlet. It can process the request or response, or both. Filters are independent and can run after each other. They have a lifecycle similar to servlets:

  1. init(FilterConfig config)
  2. doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
  3. destroy()

Request filters can:

  • perform security checks
  • reformat request headers or bodies
  • audit or log requests

Response filters can:

  • compress the response stream
  • append or alter the response stream
  • create a different response altogether

Ordering filters

FilterChain can call either a filter or servlet.

  1. ALL filters with matching URL patterns are located in the order in which they appear
  2. ALL filters with matching <servlet-name> are located in the order in which they appear
  • If there are two filters with the same filter class, with different names, the container is enforced to create two instances of the filter. e.g. web.xml-defined filter and annotated-defined filter using default name (fully-qualified classname)
  • There can't be a duplication of filter name in the Deployment Descriptor. There is no difference if the filter class is different or not.
  • web.xml will override annotated filter with the same name, only one instance will be created
  • When using annotations to define the listeners, servlets and filters, the order in which they are invoked is unspecified.

Example filter

<filter>
    <filter-name>BeerRequest</filter-name>
    <filter-class>com.example.web.BeerRequestFilter</filter-class>
    <init-param>
      <param-name>LogFileName</param-name>
      <param-value>UserLog.txt</param-value>
    </init-param>
</filter>
 
<!-- Declaring a filter mapping to a URL pattern. Can have more than one mapping. -->
<filter-mapping>
    <filter-name>BeerRequest</filter-name>
    <url-pattern>*.do</url-pattern>
</filter-mapping>
 
<!-- Declaring a flter mapping to a servlet name. Can have more than one mapping. -->
<filter-mapping>
    <filter-name>BeerRequest</filter-name>
    <servlet-name>AdviceServlet</servlet-name>
</filter-mapping>
public class BeerRequestFilter implements Filter {
    private FilterConfig fc;
    public void init(FilterConfig config) throws ServletException {
        this.fc = config;
    }
    public void doFilter(ServletRequest req,
                         ServletResponse resp,
                         FilterChain chain)
            throws ServletException, IOException {
 
        HttpServletRequest httpReq = (HttpServletRequest) req; // cast
 
        String name = httpReq.getRemoteUser();
        if (name != null) {
            fc.getServletContext().log("User " + name + " is updating");
        }
        chain.doFilter(req, resp); // call next filter or servlet
    }
    public void destroy() {
        // do cleanup stuff
    }
}

Filter mapping for request-dispatched web resources

A filter is not invoked when the resource upon which the filter is applied gets the request through a request dispatcher, UNLESS an appropriate dispatcher value is specified in filter-mapping. Note that if you use a dispatcher element, then the filter will be invoked ONLY for those dispatcher elements that are listed.

<filter-mapping>
    <filter-name>MonitorFilter</filter-name>
    <url-pattern>*.do</url-pattern>
 
    <!-- zero or more of the below -->
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>ASYNC</dispatcher>
</filter-mapping>

Response filter

Code for response filters goes after:

chain.doFilter(request, response);

A response filter may substitute a different response object, if a servlet has already written to the response. A Wrapper object can be used to save implementing unnecessary methods; extend the Wrapper and implement only the methods you need. The four wrapper classes are:

  • ServletRequestWrapper
  • HttpServletRequestWrapper
  • ServletResponseWrapper
  • HttpServletResponseWrapper
class CompressionResponseWrapper extends HttpServletResponseWrapper {
    // override any methods you want to customize
}
 
class MyCompressionFilter implements Filter {
    public void init(FilterConfig cfg) { }
 
    public void doFilter( request, response, chain) {
        CompressionResponseWrapper wrappedResp = new CompressionResponseWrapper(response);
        chain.doFilter(request, wrappedResp);
        // do compression logic here
    }
    public void destroy() { }
    }
}

Annotations

  • * can be used to match servlet names, as well as URL patterns
/*
    DispatcherType:
    REQUEST: Only when the request comes directly from the client (default)
    ASYNC: Only when the asynchronous request comes from the client
    FORWARD: Only when the request has been forwarded to a component
    INCLUDE: Only when the request is being processed by a component that has been included
    ERROR: Only when the request is being processed with the error page mechanism
 */
@WebFilter(filterName = "FilterAnnotatedA",
        urlPatterns = {"/*"},
        servletNames = {"*"},
        initParams = {@WebInitParam(name = "name", value = "Bob")},
        dispatcherTypes = {DispatcherType.REQUEST},
        asyncSupported = true)

Other attributes:

java.lang.String[] value           The URL patterns to which the filter applies
java.lang.String description           The description of the filter
java.lang.String displayName           The display name of the filter
java.lang.String filterName           The name of the filter
java.lang.String largeIcon           The large-icon of the filter
java.lang.String smallIcon           The small-icon of the filter


File Upload / multipart

getParts and getPart methods to iterate over the various mime attachments:

Part getPart(java.lang.String name) // Gets the Part with the given name. 
 
java.util.Collection<javax.servlet.http.Part> getParts()  // Gets all the Part components of this request, provided that it is of type multipart/form-data.

Files uploaded can be access through tjese methods in ServletRequest (not HttpRequest):
* request.getInputStream(); for binary
* request.getReader(); for character data

location

An absolute path to a directory on the file system. The location attribute does not support a path relative to the application context. This location is used to store files temporarily while the parts are processed or when the size of the file exceeds the specified fileSizeThreshold setting. The default location is "".

fileSizeThreshold

The file size in bytes after which the file will be temporarily stored on disk. The default size is 0 bytes.

maxFileSize

The maximum size allowed for uploaded files, in bytes. If the size of any uploaded file is greater than this size, the web container will throw an exception (IllegalStateException). The default size is unlimited.

maxRequestSize

The maximum size allowed for a multipart/form-data request, in bytes. The web container will throw an exception if the overall size of all uploaded files exceeds this threshold. The default size is unlimited.

/*
    Config is equivalent to:
    <servlet>
    <multipart-config>
        <location>/temp</location>
        <max-file-size>20848820</max-file-size>
        <max-request-size>418018841</max-request-size>
        <file-size-threshold>1048576</file-size-threshold>
    </multipart-config>
    </servlet>
 */
@WebServlet(name = "FileUploadServlet", urlPatterns = {"/upload"})
@MultipartConfig(location="C:\\xampp\\tomcat\\temp", fileSizeThreshold=1024*1024,
        maxFileSize=1024*1024*5, maxRequestSize=1024*1024*5*5)
public class ServletAnnotatedMultipart extends HttpServlet {
 
    private final static Logger LOGGER =
            Logger.getLogger(ServletAnnotatedMultipart.class.getCanonicalName());
 
    @Override
    protected void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 
                                        throws ServletException, IOException {
        Collection<Part> parts = httpServletRequest.getParts();
        for(Part part: parts) {
            System.out.println(part.getName());
            System.out.println(part.getSize());
            System.out.println(part.getContentType());
        }
 
        Part part = httpServletRequest.getPart("file");
        part.write(httpServletRequest.getParameter("destination"));
    }
}

JSP file:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>File Upload</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <form method="POST" action="upload" enctype="multipart/form-data" >
            File:
            <input type="file" name="file" id="file" /> <br/>
            Destination:
            <input type="text" value="c:/" name="destination"/>
            </br>
            <input type="submit" value="Upload" name="upload" id="upload" />
        </form>
    </body>
</html>
<%=System.getProperty("java.io.tmpdir")%>



Listeners

This content should be put in 9.2, but due to its size it is a separate section.
See also: 5.3 Create and use context and init parameters for more details on ServletContextListener

Container initialization and listeners

As Per SRV 10.12 of Servlet Specification,

When a web application is deployed into a container, the following steps must be performed, in this order, before the web application begins processing client requests.

  1. Instantiate an instance of each event listener identified by a <listener> element in the deployment descriptor.
  2. For instantiated listener instances that implement ServletContextListener, call the contextInitialized() method.
  3. Instantiate an instance of each filter identified by a <filter> element in the deployment descriptor and call each filter instance’s init() method.
  4. Instantiate an instance of each servlet identified by a <servlet> element that includes a <load-on-startup> element in the order defined by the load-onstartup element values, and call each servlet instance’s init() method.

Listener ordering

Using web.xml, The order the container uses in building the chain of filters to be applied for a particular request URI is as follows:

  1. First, the <url-pattern> matching filter mappings in the same order that these elements appear in the deployment descriptor.
  2. Next, the <servlet-name> matching filter mappings in the same order that these elements appear in the deployment descriptor.
  • For multiple instances of ServletContextListener, the listeners are invoked in the order of their appearance in web.xml. The "destroyed" method is invoked in the reverse order.
  • The order of listeners when using @WebListener annotation is unspecified.
Object Event Listener Interface Event Class
Web context Initialization and destruction javax.servlet.ServletContextListener ServletContextEvent
Web context Attribute added, removed, or replaced javax.servlet.ServletContextAttributeListener ServletContextAttributeEvent
Session Creation, invalidation, activation, passivation, and timeout javax.servlet.http.HttpSessionListener, javax.servlet.http.HttpSessionActivationListener HttpSessionEvent
Session Attribute added, removed, or replaced javax.servlet.http.HttpSessionAttributeListener HttpSessionBindingEvent
Request A servlet request has started being processed by web components javax.servlet.ServletRequestListener ServletRequestEvent
Request Attribute added, removed, or replaced javax.servlet.ServletRequestAttributeListener ServletRequestAttributeEvent

Ways of classifying listeners

These categories may help you remember:

  • Attribute listeners

ServletRequestAttributeListener
ServletContextAttributeListener
HttpSessionAttributeListener

  • Other lifecycle listeners

ServletRequestListener
ServletContextListener
HttpSessionListener
HttpSessionBindingListener
HttpSessionActivationListener

  • Methods in all attribute listeners (except binding listener)

attributeAdded()
attributeRemoved()
attributeReplaced()

  • Lifecycle events related to sessions (excluding attribute-related events)

sessionCreated()
sessionDestroyed()

  • Lifecycle events related to requests (excluding attribute-related events)

requestInitialized()
requestDestroyed()

  • Lifecycle events related to servlet context (excluding attribute-related events)

contextInitialized()
contextDestroyed()

All listeners

HttpSessionActivationListener

HttpSessionEvent (NOT HttpSessionActivationEvent)

sessionDidActivate
sessionWillPassivate

You have an attribute class, and you want objects of this type to be notifed when the session to which they’re bound is migrating to and from another JVM. The class must implement Serializable, but the readObject and writeObject methods may not be called.

This listener is not configured in the Deployment Descriptor, but the interface is implemented by entity classes.

HttpSessionAttributeListener

HttpSessionBindingEvent (NOT HttpSessionAttributeEvent)

attributeAdded
attributeRemoved
attributeReplaced - ServletRequestAttributeEvent.getValue() returns the replaced value, not the new value

You want to know how many concurrent users there are. In other words, you want to track the active sessions.

HttpSessionBindingListener

HttpSessionBindingEvent

valueBound // called BEFORE the object becomes accessible through HttpSession.getAttribute()
valueUnbound // called AFTER the object is removed from the session.

You have an attribute class (a class for an object that will be put in an attribute) and you want objects of this type to be notifed when they are bound to or removed from a session.

This listener is not configured in the Deployment Descriptor, but the interface is implemented by entity classes.

HttpSessionListener

HttpSessionEvent

sessionCreated
sessionDestroyed

You want to know when a session attribute has been added, removed, or replaced. For more see 4.4 Write code that manages client sessions and cookies

ServletContextAttributeListener

ServletContextAttributeEvent

attributeAdded
attributeRemoved
attributeReplaced

You want to know if an attribute in a web app context has been added, removed, or replaced.

ServletContextListener

ServletContextEvent

contextInitialized
contextDestroyed

You want to know if a context has been created or destroyed

ServletRequestAttributeListener

ServletRequestAttributeEvent

attributeAdded
attributeRemoved
attributeReplaced

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

ServletRequestListener

ServletRequestEvent

requestInitialized
requestDestroyed

You want to know each time a request comes in, so that you can log it.





Comments

If you have questions, corrections, information or examples to add, please comment below.

Add a New Comment
or Sign in as Wikidot user
(will not be published)
- +
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License