Thursday, 27 May 2010

Servlet 3.0 Annotations

The specification for JSR315: Servlet 3.0 was released in December last year with some interesting new ideas and features. This is still very new and the application servers haven't quite caught up with it yet. At the time of writing, the only server I know of that supports Servlet 3.0 is Glassfish v3. Tomcat 7 will provide support but is apparently not due for release until toward the end of this year.

In this post, I want to take a brief look at the new annotations in Servlet 3.0, and show how, although not ground-breaking, they will reduce the amount of configuration in web.xml.

To run any of these examples, you will need a copy of Glassfish 3.0 or, if you are feeling adventurous, you could try Tomcat 7.0 RC2.

Getting hold of the Servlet 3.0 API

Assuming you are using Maven, you'll need to find yourself a repository artifact that corresponds to the new specification. The central repository now has the required artifacts, so no need to mess around adding repositories:

pom.xml
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>6.0</version>
</dependency>

Note you may also find an artifact called javaee-web-api. As far as I can tell from a cursory glance, this contains the same classes as javaee-api.

Servlet with Annotations

Servlet 3.0 provides the @WebServlet annotation to define a servlet, obviating the need for configuration in web.xml.

SampleServlet.java
package blogger;

import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(value="/sampleServlet", loadOnStartup=1)
public class SampleServlet extends HttpServlet {

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

}

Simply create the class, deploy the project and invoke the servlet using the path specified in the annotation, e.g. http://localhost:8080/<contextPath>/sampleServlet. Note that things like load on startup can be specified for servlets using the annotation.

Listener with Annotations

Listeners can be defined in much the same way. Again, no configuration is required in web.xml.

SampleListener.java
package blogger;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@WebListener
public class SampleListener implements HttpSessionListener {

    private static final Logger log = LoggerFactory.getLogger(SampleListener.class);

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
        log.info("Session created");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        log.info("Session destroyed");
    }

}

I've used SLF4J for logging here, which is becoming more popular as a logging framework wrapper, used by Spring 3.0 for example. To make this work, you'll need to add the following to your pom.xml. Note there's no need to add an artifact for Log4J.

pom.xml
...

<properties>
    <slf4j.version>1.5.8</slf4j.version>
</properties>

...

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j.version}</version>
</dependency>

To configure the underlying Log4J, you'll need something like this copied out to your classpath:

log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="target" value="system.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{ABSOLUTE} %c{1} - %m%n"/>
        </layout>
    </appender>

    <root>
        <priority value="info"/>
        <appender-ref ref="console" />
    </root>

</log4j:configuration>

Filter with Annotations

Same idea again here. No configuration in web.xml.

SampleFilter
package blogger;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@WebFilter("/sampleServlet")
public class SampleFilter implements Filter {

    Logger log = LoggerFactory.getLogger(SampleFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
        chain.doFilter(request, response);
        if (response instanceof HttpServletResponse) {
            log.info("Applying cache control filter to response");
            HttpServletResponse httpServletResponse = (HttpServletResponse)response;
            httpServletResponse.setHeader("Cache-Control", "nocache");
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}

}

In this case, the parameter in the @WebFilter annotation specifies the paths to which the filter is applied. In my case, just to the sample servlet above.

One of the limitations of the annotations for filters is that there is no way of specifying the ordering. If multiple filters were in use, and the ordering was important, you'd have to revert to a schema-based configuration in web.xml.

Web Deployment Descriptor

None of the above examples need a web.xml file. By default, the welcome file list includes index.html and index.jsp. If you need to configure things like session timeouts, prelude/coda, etc., you will need a web.xml file. This is an example of a basic Servlet 3.0 version:

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

Settings in web.xml, by the way, can also be used to override the settings provided by the annotations. The web.xml takes precedence. You can also set metadata-complete to true in web.xml to tell the container only to consider configuration in web.xml and skip annotations altogether.

Conclusion

So, there you have it. Servlet 3.0 annotations. As I said in the introduction, they aren't ground-breaking in terms of setting up your application, but they do make it slightly easier to configure servlets, filters and listeners in your web applications. I suspect 99% of the time, we will still need a web.xml file, but it could be less cluttered.

5 comments:

maheshv said...

The Servlet 3.0 could have supported annotations for simple POJO. I am not sure why we still have to extend the HttpServlet?

class SampleListener implements HttpSessionListener

Richard Senior said...

I wrote the post based on the "final" Servlet 3.0 specification linked at the top of the post. This shows the @WebServlet annotation with classes extending HttpServlet, etc. and does not describe the @Servlet and @GET annotations that I have seen in other places that would allow use of a POJO.

rolojavadeveloper said...

I have been try to startup a webservlet with annotation and doesn´t work. I work with tomcat 6. I make a class with @WebServlet annotation with the same parameters, deploy and start the server, and put url with the servlet name and get the http 404 error.

Regards

Richard Senior said...

Check back at the first paragraph rolo: you need Tomcat 7 or Glassfish 3 to use anything from the Servlet 3.0 spec.
Tomcat 7 is now released but I've not tried it, only Glassfish 3.

Gopi Krishna said...

AngularJS is a toolset for building the framework most suited to your application development. It is fully extensible and works well with other libraries. Every feature can be modified or replaced to suit your unique development workflow and feature needs. Read on to find out how.

AngularJS Training in Chennai
AngularJS Training Institute in Chennai

AngularJS Certification Training in Chennai

Followers