Using Servlet Filters to Profile Web Application Performance in Java EE 6

Overview

    Purpose

    This tutorial will show you how to use Filters in a Java EE 6 Application to measure Request processing time.

    Time to Complete

    Approximately 1 hour

    Introduction

    Servlet Filters is a feature in the Java EE Servlet Specification that is used to intercept requests made to Java EE Applications. Using Filters you can validate requests before they are processed, add information to responses and measure execution time of the called resource.

    In this tutorial you will create a simple web application and measure its response time using Servlet Filters, you will store the data in a Session Bean and then display it using JavaServer Faces.

    Scenario

    Web application performance monitoring is sometimes supported by Web Servers or by profiling tools used inside of IDEs. A quick method to start diagnosis of performance issues while accessing resources is to implement a monitoring filter to record processing times.

    The filter itself is very simple and will add very little overhead to the application, making it a good alternative to find performance problems in the application even in production environments.

    Response times are key to web applications performance. The filter logs response time by measuring how long it takes the application to complete a request and deliver a response to the user.

    Software Requirements

    The following is a list of hardware and software requirements:

    Prerequisites

    Before starting this tutorial, you should:

    • Have knowledge of the Java 7 Programming Language.
    • Have basic knowledge of the Java EE 6 Platform, specifically Servlets, JSP and JavaServer Faces.
    • This tutorial uses Java 7 syntax in the code examples. To read more about the changes in the Java 7 platform read the JDK 7 Release Notes.

Creating a simple Java EE6 Web Application

    To profile request processing times, you need a Web Application to test. For this tutorial you will create a simple Web Application first.

    If you are using your own Web Application instead of a new one go to the next topic: Verify web application project configuration

    Open NetBeans.

    Go to: File > New Project

    Select Java Web > Web Application and click Next.

    Type a name for your Web Application and click Next.

    From the server list select Oracle WebLogic Server.

    Select Java EE 6 Web from the Java EE Version dropdown list.

    Check Enable Context and Dependency Injection.

    Click Next.

    In the Frameworks pane, choose JavaServer Faces and verify that Server Library is selected.

    Click Finish and your application project will be created and configured.

    Right-Click on the project and select Run to test your application.

    A browser window opens and displays the home page of your application.

    You have created a simple web application from NetBeans using Java EE 6.

Verify the Web Application project configuration

    Existing applications start from here

    To ensure you have the proper settings, verify the project configuration.

    Click File > Project Properties (Your project name).

    In the Sources pane select JDK 7 for the Source/Binary Format.

    Code samples in this tutorial uses Java 7 syntax

    In the Run pane verify the Java EE Version is Java EE 6 Web. The server dictates the Java EE Version setting. In the screenshot, selecting Oracle WebLogic Server sets the Java EE Version to Java EE 6 Web.

    If you are running against other Server make sure the Java EE Version is 6.

    Make sure the Deploy on Save is checked, this will ensure that every change you make in the source code is deployed to the web server, and you can see your changes in real time.

Create a Servlet Filter

    To create a Servlet Filter, you need to create a class that implements the javax.servlet.Filter interface.

    Open the project and right-click Source Packages

    Select New > Java Class

    Name the class PerformanceFilter and type com.example.filters in the Package field.

    You can use any Class Name and Package you want in your Java EE 6 project.

    Click Finish.

    To implement the PerformanceFilter class add the highlighted code:

    Code: PerformanceFilter.java basic implementation.

      package com.example.filters;
      
      import java.io.IOException;
      import javax.servlet.*;
      
      @javax.servlet.annotation.WebFilter(filterName = "performanceFilter", urlPatterns = {"/*"})
      public class PerformanceFilter implements javax.servlet.Filter {
      
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
      
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
          chain.doFilter(request, response);
        }
      
        @Override
        public void destroy() {
        }
      }
      

    This is the basic implementation of a Filter. A Filter works as an interceptor and continues the regular invocation of the following servlet (or even another filter) using  chain.doFilter(request, response); method call.

    You mapped the Filter to specific resources or URLs using the @WebFilter annotation. Filters can be applied to specific resources modifying the urlPatterns parameter of the WebFilter annotation.

    Now add the highlighted code in the PerformanceFilter class to measure the processing time of the request.

    Code: PerformanceFilter.java logging to the standard output.

      package com.example.filters;
      
      import java.io.IOException;
      import javax.servlet.*;
      
      @javax.servlet.annotation.WebFilter(filterName = "performanceFilter", urlPatterns = {"/*"})
      public class PerformanceFilter implements javax.servlet.Filter {
      
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
      
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
          long startTime = System.currentTimeMillis();
          chain.doFilter(request, response);
          long endTime = System.currentTimeMillis();
          System.out.println("Time: " + (endTime - startTime) + "milliseconds");
        }
      
        @Override
        public void destroy() {
        }
      }
      

    Add the highlighted code to get the name of the resource:

    Code: PerformanceFilter.java logging resource name and time.

      package com.example.filters;
      
      import java.io.IOException;
      import javax.servlet.*;
      import javax.servlet.http.HttpServletRequest;
      
      @javax.servlet.annotation.WebFilter(filterName = "performanceFilter", urlPatterns = {"/*"})
      public class PerformanceFilter implements javax.servlet.Filter {
      
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
      
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
          long startTime = System.currentTimeMillis();
          chain.doFilter(request, response);
          long endTime = System.currentTimeMillis();
          if (request instanceof HttpServletRequest) {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            String resourceName = httpRequest.getPathInfo();
            long time = endTime - startTime;
            System.out.println(resourceName + " took: " + time + "milliseconds");
          }
        }
      
        @Override
        public void destroy() {
        }
      }
      

    For more information on why the cast to HttpServletRequest is needed, read the Questions and Answers section at the end of this document.

    Run the application.

    Refresh your Web Browser a couple of times and watch the Server Output window. The output should be similar to what is displayed on the screenshot.

Create a backing bean to store performance information.

    You have to create a Java Bean to store the statistics and then use CDI to inject it into your Filter.

    First create a Statistic Bean to store the data you want to log per resource.

    Right-click on the com.example.filters package.

    Select New > Java Class

    Name the class name: PerformanceStatistic.

    Verify the package is com.example.filters.

    Click Finish.

    Add the highlighted code to the PerformanceStatistic class.

    Code: PerformanceStatistic.java simple data bean.

      package com.example.filters;
      
      public class PerformanceStatistic {
      
        private String resourceName;
        private long totalTime;
        private long count;
      
        public void setResourceName(String resourceName) {
          this.resourceName = resourceName;
        }
      
        public String getResourceName() {
          return resourceName;
        }
      
        public long getTotalTime() {
          return totalTime;
        }
      
        public long getCount() {
          return count;
        }
      
        public long getAverage() {
          return totalTime / count;
        }
      
        public void addTime(long time) {
          totalTime += time;
          count++;
        }
      }
      

    Right-Click on the package and select New > Java Class.

    Name the class PerformanceStatisticsBean and verify the package is com.example.filters.

    Click Finish.

    Add the highlighted code to the PerformanceStatisticsBean Class.

    Code: PerformanceStatisticsBean.java injactable bean.

      package com.example.filters;
      
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      @javax.inject.Named("performanceStatistics")
      @javax.enterprise.context.ApplicationScoped
      public class PerformanceStatisticsBean {
      
        private Map<String, PerformanceStatistic> statistics = new HashMap<>();
      
        public void logTime(String resourceName, long time) {
          PerformanceStatistic statistic = statistics.get(resourceName);
          if (statistic == null) {
            statistic = new PerformanceStatistic();
            statistic.setResourceName(resourceName);
            statistics.put(resourceName, statistic);
          }
          statistic.addTime(time);
        }
      
        public List<PerformanceStatistic> getStatistics() {
          return new ArrayList<>(statistics.values());
        }
      }
      

    You need two annotations on this class to enable this class to record statistics. The first one is @Named, this annotation makes this bean usable in your JSF pages. The second annotation is @ApplicationScoped, this annotation defines the scope of the bean in your Web Application. ApplicationScoped means one bean will be created per application and will be shared in all sessions and requests.

    The rest of the code is a simple Map that will store a statistic per resource accessed.

    Add the dependency to the PerformanceFilter class to let CDI inject it.

    Open the PerformanceFilter class and add the highlighted code.

    Code: PerformanceFilter.java with PerformanceStatisticsBean dependency.

      package com.example.filters;
      
      import java.io.IOException;
      import javax.servlet.*;
      import javax.servlet.http.HttpServletRequest;
      
      @javax.servlet.annotation.WebFilter(filterName = "performanceFilter", urlPatterns = {"/*"})
      public class PerformanceFilter implements javax.servlet.Filter {
      
        @javax.inject.Inject
        private PerformanceStatisticsBean performanceBean;
      
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
      
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
          long startTime = System.currentTimeMillis();
          chain.doFilter(request, response);
          long endTime = System.currentTimeMillis();
          if (request instanceof HttpServletRequest) {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            String resourceName = httpRequest.getPathInfo();
            long time = endTime - startTime;
            performanceBean.logTime(resourceName, time);
          }
        }
      
        @Override
        public void destroy() {
        }
      }
      

    To make injection work you need to add the @Inject annotation on the injected field.

Add the JSF view to display the performance information.

    Using the @named annotation on PerformanceStatisticsBean you can now inject the bean on a JSF page to display the information.

    If you do not use JSF in your application you can display the data in your application injecting the PerformanceStatisticsBean in any Servlet.

    Open the index.xhtml file.

    Add the highlighted code:

    Code: index.xhtml jsf view.

      <?xml version='1.0' encoding='UTF-8' ?>
      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
        <h:head>
          <title>Performance Report</title>
        </h:head>
        <h:body>
          Performance Report
          <h:dataTable value="#{performanceStatistics.statistics}" var="statistic">
            <h:column>
              <f:facet name="header">
                <h:outputText value="Resource Name"/>
              </f:facet>
              <h:outputText value="#{statistic.resourceName}"/>
            </h:column>
            <h:column>
              <f:facet name="header">
                <h:outputText value="Count"/>
              </f:facet>
              <h:outputText value="#{statistic.count}"/>
            </h:column>
            <h:column>
              <f:facet name="header">
                <h:outputText value="Average Time"/>
              </f:facet>
              <h:outputText value="#{statistic.average}"/>
            </h:column>
          </h:dataTable>
        </h:body>
      </html>
      
      

    Run the Application and open your Web Browser.

    Refresh the page a couple of times and see the values change.

Summary / next steps

    In this tutorial you created a new application, added a simple filter to profile performance in the application's resources and used JSF to display a Bean created and injected by CDI.

    There are a couple of things you can do to improve the functionality of the filter you've created.

    You can add the maximum and minimum times to add more data to the statistic.

    You can add even improve your view to display the top 10 slowest resources.

    Add Data Persistence using JPA to your statistics to store them for later analysis.

    Add Session Tracking using Servlet Listeners and add them to your Statistics.

    You can build on the code generated in this tutorial to have the statistics you need to start profiling your application's performance and find problematic resources easily.

    Resources

    Questions and answers:

      • What does NetBeans create with a Web Application?

        When you created your Java EE 6 application using NetBeans some files are created for you and some functionality becomes available. A Java EE 6 Web Profile Application contains a lot more than only Servlets and JSPs. With Java EE 6 web profile you have access to JSF as your presentation layer, JPA as your persistence layer, CDI as your dependency injection engine, EJB for business objects that might be distributed over several machines, JTA for transactions and of course Servlets and JSPs already in your server libraries.

        The entire Enterprise stack has all these libraries included and configured in your server. This means you don't need to add extra libraries to have all the features of the Enterprise APIs as long as you have an Enterprise compliant server. This also helps you leverage the responsibility of keeping such libraries working and up to date to the server provider. It also makes deployment easier and lighter since the only deployed code is the one from your application not including libraries or extra configurations. Your application deployment unit (WAR file) will be smaller. Also using the standard Java EE libraries and conventions will help you and your team keep application maintenance efficient and easy to adopt.

        The notable configuration files added by NetBeans are:

        • WEB-INF/web.xml. Enables JSF and sets URL mapping to FacesServlet.

        • WEB-INF/beans.xml. An empty XML file to enable CDI dependency injection mechanism.

        •  weblogic.xml (or other server specific xml) to add server specific configuration.

      • How does the javax.servlet.Filter interface work?

        The javax.servlet.Filter interface requires three methods to be implemented

        • init(FilterConfig filterConfig) Filters that require some initialization code uses this method to add some code that gets executed once when the filter is created. You can add Filter configuration parameters in the web.xml file for each filter.

        • destroy() This method will get executed only once when the application is undeployed or stopped, you can add cleanup of resources here.

        • doFilter(request, response, filterChain) This method is contains the actual filtering and calls filter.chain.doFilter(request, response) to continue to the next filter or to the called resource.

        The Web Server will look for filters annotated by the WebFilter annotation as well as filters configured in the web.xml. When a request matches the Filter mapping either in the web.xml or in the WebFilter annotation the filter will be invoked. When the filter calls the chain.doFilter method the next filter that matches the mapping will be called. This will repeat until all the matching filters have invoked the chain.doFilter method and will usually end in the target Servlet or resource. Think of this filter chain as a Stack that will put a new filter on it every time a Filter matches the url and the chain.doFilter method is called. At the end of the resource invocation the filters will continue execution.
        Usually you filter requests before the chain.doFilter call and filter responses after the chain.doFilter call. In this example you measure how long it takes from request to response so the code goes around the chain.doFilter call.

      • Why do you cast ServletRequest to HttpServletRequest? Can't a filter receive a HttpServletRequest instead?

        Filters act on Servlets regardless of its type, an HttpServlet is a specific type of Servlet but we have Portlets as well and the Servlet specification is open to even more types of specific Servlets and as you can guess the Filter interface is generic for all types of Servlets that might come. So its not safe to use a specific type of request or response since we can get almost anything that is a ServletRequest in this method.

      • My CDI Injection doesn't work I get NullPointerExceptions and the page displays nothing!

        Do you have the beans.xml file in WEB-INF? In NetBeans look under Web Pages > WEB-INF on your project if its not there add it with the following content:

        <?xml version="1.0" encoding="UTF-8"?>
        <beans 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/beans_1_0.xsd">
        </beans>

        For CDI to work beans.xml is a required configuration file even if its empty.

    Credits

    • Lead Curriculum Developer: Eduardo Moranchel

To help navigate this Oracle by Example, note the following:

Hiding Header Buttons:
Click the Title to hide the buttons in the header. To show the buttons again, simply click the Title again.
Topic List Button:
A list of all the topics. Click one of the topics to navigate to that section.
Expand/Collapse All Topics:
To show/hide all the detail for all the sections. By default, all topics are collapsed
Show/Hide All Images:
To show/hide all the screenshots. By default, all images are displayed.
Print:
To print the content. The content currently displayed or hidden will be printed.

To navigate to a particular section in this tutorial, select the topic from the list.