Home > database >  How to hook begin and end of HTTP requests in JavaEE?
How to hook begin and end of HTTP requests in JavaEE?

Time:08-03

tl;dr:

How do i get the ServletResponse during ServletRequestListener.requestDestroyed?

Short Version

In JavaEE, i want to know when:

  • when a request starts
  • and when a request ends

and be able to inspect the request and response objects.

Long Version

In the ASP.net world, if you want to know when a request starts and ends, you write an IHttpModule:

public class ExampleModuleForThisQuestion : IHttpModule
{
}

And then register your "module" in the web xml configuration file:

web.config:

    <system.webServer>
        <modules>
            <add name="DoesntMatter" type="ExampleModuleForThisQuestion "/>
        </modules>
    </system.webServer> 

Inside your module you can register callback handlers for:

  • BeginRequest event
  • EndRequest event

The web-server infrastructure then calls you Init method. That is your opportunity to register that you want to receive notifications when a request starts, and when a request ends:

public class ExampleModuleForThisQuestion : IHttpModule
{
   public void Init(HttpApplication application)
   {
      application.BeginRequest  = new EventHandler(beginRequest); //register the "BeginRequet" event
      application.EndRequest  = new EventHandler(endRequest); //register the "EndRequest" event
   }
}

And now we have our callbacks when a request starts:

private void beginRequest(object sender, EventArgs e)
{
   HttpApplication application = (HttpApplication)sender;

   //Record the time the request started
   application.Context.Items["RequestStartTime"] = DateTime.Now;

   //We can even access the Request and Response objects
   application.ContenxtLog(application.Context.Request.Headers["User-Agent"]);
}

and we have our callback when a request ends:

private void endRequest(object sender, EventArgs e)
{
   HttpApplication application = (HttpApplication)sender;

   //We can even access the Request and Response objects

   //Get the response status code (e.g. 418 I'm a teapot)
   int statusCode = application.Context.Response.StatusCode;

   //Get the request method (e.g. GET, POST, BREW)
   String method = application.context.Request.RequestType;

   //Get the path from the request (e.g. /ViewCustomer)
   String path = application.context.Request.AppRelativeCurrentExecutionFilePath'
   
   //Get when the request started - that we recorded during "Begin Request"
   DateTime requestStartTime = (DateTime)application.Context.Items["RequestStartTime"];

   //And we can modify the response
   if ((DateTime.Now - requestStartTime).TotalSeconds = 17)
       application.Context.Response.StatusCode = 451;
}

Java Almost-Equivalent is ServletRequestListener

In Java, apparently the corresponding technique is to create and object that implements the ServletRequestListener interface:

@WebListener
public class ExampleListenerForThisQuestion 
      implements javax.servlet.ServletRequestListener {

}

and register our listener with the application server by including it in our web xml configuration file:

web.xml

<listener>
    <listener-class>ExampleListenerForThisQuestion</listener-class>
</listener>

Now we can implement the requestInitialized and requestDestroyed methods to get when a request starts and ends:

public class ExampleListenerForThisQuestion 
      implements javax.servlet.ServletRequestListener {

   @Override
   public void requestInitialized(ServletRequestEvent sre) {
      ServletRequest sr = sre.getServletRequest();
      sr.setAttribute("requestStartTicks", getCurrentTickCount());

      HttpServletRequest request = (HttpServletRequest) sr;

      // e.g. "PUT /Customers/1234"
      System.out.printf("%s %s\r\n", request.getMethod());
   }

   @Override
   public void requestDestroyed(ServletRequestEvent sre) {
      ServletRequest sr = sre.getServletRequest();
      long requestStartTicks = (long)sr.getAttribute("requestStartTicks");

      HttpServletResponse response = (HttpServletRequest)...nothing, because i don't know how...

      // e.g. "226 IM Used"
      System.out.printf("%d %s\r\n", response.getStatus(), response.getStatusDescription());
   }
}

But how to get the response?

Now that i'm notified when the response ends, i need the result of that request:

  • i need the http status code (e.g. 424)
  • i need the http status description (e.g. Failed Dependency)
  • i need to inspect response headers
  • i need to modify response headers

You notice the line in my code above:

HttpServletResponse response = (HttpServletRequest)...nothing, because i don't know how...

i don't know how to get ahold of the response; which is why i'm asking Stackoverflow.

CodePudding user response:

You can create a Filter instead of a listener. Filters allow you to create wrappers around request processing. See the documentation on that topic.

For HTTP, you can use HTTPFilter. This could look like the following:

@WebFilter("/*")//or via deployment descriptor
public class YourFilter extends HttpFilter{ //or just Filter for general (non-HTTP) processing
    @Override
    public void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {//for generic filters, use ServletRequest/ServletResponse instead
        //before request processing 
        chain.doFilter(req, res);//calls other filters and processes request
        //after request processing
        //you can use res here
    }
}

If you do not call chain.doFilter, other filters and the servlet will not be executed.

If you prefer declaring the filter in your deployment descriptor (web.xml), you can do that as well:

<filter>
  <filter-name>yourFilter</filter-name>
  <filter-class>your.FilterClass</filter-class>
</filter>
<filter-mapping>
  <filter-name>yourFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
  • Related