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;
}
Now how to do it in JavaEE?
The question is: what is the moral equivalent of IHttpModule in the Java web-server world?
Some people say that it is an ServletRequestListener:
Interface for receiving notification events about requests coming into and going out of scope of a web application.
A ServletRequest is defined as coming into scope of a web application when it is about to enter the first servlet or filter of the web application, and as going out of scope as it exits the last servlet or the first filter in the chain.
Others insist that you want a "filter" and a "filter chain".
Some say that a "filter" gives you everything that ServletRequestListener does, but that a filter can also be configured to run for only certain URL.
This random page on docs.oracle.com says nothing about "filters", and instead i need a ServletContextListener, because that's the only way to receive a notification for each request. But then they have a table that seems to contradict that, and that i want a ServletRequestEvent:
| Object | Event | Listener Interface and Event Class |
|---|---|---|
| Web context | Initialization and destruction | javax.servlet.ServletContextListener and ServletContextEvent |
| Web context | Attribute added, removed, or replaced | javax.servlet.ServletContextAttributeListener and ServletContextAttributeEvent |
| Session | Creation, invalidation, activation, passivation, and timeout | javax.servlet.http.HttpSessionListener, javax.servlet.http.HttpSessionActivationListener, and HttpSessionEvent |
| Session | Attribute added, removed, or replaced | javax.servlet.http.HttpSessionAttributeListener and HttpSessionBindingEvent |
| Request | A servlet request has started being processed by web components | javax.servlet.ServletRequestListener and ServletRequestEvent |
| Request | Attribute added, removed, or replaced | javax.servlet.ServletRequestAttributeListener and ServletRequestAttributeEvent |
I don't know what any of these things are. I just want to be notified during a web-request:
- when the request processing begins
- when the request processing ends
Writing Java code blind
I can try to transcribe the ASP.net code into Java-esque code on the fly, and hopefully someone can just fix it.
First we'll create a class that implements...ServletRequestListener?:
public class ExampleListenerForThisQuestion
implements javax.servlet.ServletRequestListener {
}
And then we register our "module" in our web xml configuration file:
web.xml
<listener>
<listener-class>ExampleListenerForThisQuestion</listener-class>
</listener>
Now we will implement the requestInitialized method to create our event listeners:
public class ExampleListenerForThisQuestion
implements javax.servlet.ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
//...now i'm stuck
}
}
That didn't get very var.
The closest question on stackoverflow to what i want is:
Event like .net's "Application_Start" and "Begin_Request" for java/tomcat/JSP?
The only problem is that the accepted answer doesn't answer how to do it. That makes sense: the question wasn't asking how to do it. The question was only asking if Java has an equivalent. Apparently it does - but we won't tell you how.
Research Effort
- How to get request object from HttpSessionAttributeListener
- Event like .net's "Application_Start" and "Begin_Request" for java/tomcat/JSP?
- ServletRequestListener Example Use Case
- ServletRequestEvent and ServletRequestListener in Servlet
- The Java EE 5 Tutorial - Handling Servlet Life-Cycle Events
- Java Web series: Java Web basics and Java Web Basics
I know that trying to figure out how to do it in JavaEE will take me at least 4 days. I'm hoping to cut that down to 6 or 7 hours by asking Stackoverflow.
This question took me an hour-and-a-half to write up. I wrote the ASP.net code, from memory, in 25 seconds. It took me longer to ask the question about how to do it in Java than it would have just taken me to do it in ASP.net - which shows just how crushed and defeated i am right now.
Edit: I happen to be using Java 8. I assume that corresponds to JavaEE 8.
ServletRequestListenerUse a
ServletRequestListeneras suggested in your Question. This is a long-standing feature in the Servlet API, since Servlet spec version 2.4.requestInitializedmethod is your hook to know when handling of a request begins.requestDestroyedmethod is your hook for when handling of a request ends.Example code
Here is a little demo I wrote.
@WebListenerNotice the annotation
@WebListener. With this annotation, you can skip editing the web XML configuration file. The@WebListeneracts as a flag at runtime, so the Servlet container will automatically detect, instantiate, and execute this class at the appropriate point in the lifecycle of your web app. This modern convenience was added in Servlet spec 3.0.jakarta.*package namingAlso notice the
jakarta.*package naming in theimportstatements. Java EE is now known as Jakarta EE after Oracle Corp handed responsibility to the Eclipse Foundation a few years ago. Jakarta Servlet specifications versions 4 and 5 (used here) are equivalent except for a change in package names from javax.* to jakarta.*.By the way, Jakarta Servlet spec version 6 was released this year as part of Jakarta EE 10, with the first major changes in many years. See article, Top 5 things to know about the Jakarta Servlet 6.0 API release.
Run example
When run in the Microsoft Edge browser Version 103.0.1264.62 (Official build) (arm64) for macOS Monterey 12.4 on Apache Tomcat 2022.2 Beta with Adoptium Temurin for Java 18.0.1+10 on a MacBook Pro (16-inch, 2021) Apple M1 Pro, for URL
http://localhost:8080/demo_war_exploded/hello-servletresults in the following output.Why do we get a four lines instead of two for a single request? Well, looking at the access logs shows that this browser sent a request on both IPv4 and IPv6. And the browser sent a request to just
/, but I have no idea why.Filters
When to use
ServletRequestListenerand when to use filters? Filters are used in a chain to process the request and response. The listener fires once per request, at the very beginning and the very end. In contrast, multiple filters may fire for a single request. To quote the Javadoc:So we have a progression of beginning, middle, and end where the listener fires for beginning and end while zero, one , or more filters may fire in the middle.
Interestingly, I noticed the Filter API was added in Servlet spec version 2.3, and the ServletRequestListener API was added in 2.4. So I would guess that soon after rolling out the filter feature, developers found a need for hooks that act as temporal bookends, bracketing outside a series of filters. Since any number of filters may fire, without one filter knowing about the others, you might want a hook for the very beginning and the very end.
Servlet example code
If curious, the Servlet I wrote for the above example:
You said:
If you mean a correspondence between version numbers 8 and 8, no.
The name Java EE (now Jakarta EE), meaning “Enterprise Edition”, is a bit of a misnomer. Jakarta EE is just a bunch of specifications for a bunch of software that can be called from Java code running in a Java Virtual Machine. So "Java EE" was never really "Java", it was a framework of specs and libraries for helping developers build enterprise-oriented applications.
Java EE 8, re-released as Jakarta EE 8, requires Java 8. (Major changes came to Java 9 through 11 that may cause problems when migrating.)
Jakarta EE 9 is essentially the same as Jakarta EE 8, but for the change in package naming from
javax.*tojakarta.*. So it too requires Java 8.Jakarta EE 9.1 is a revamp and cleanup of Jakarta EE 8 & 9, dropping some old deprecations, adding more deprecations, and doing other work to lay the foundation for future innovations. Among the changes is requiring implementations to support Java 11 in addition to Java 8 (both LTS versions). And while not an explicit requirement, all implementations are expected to be able to run in Java 17 (the current LTS version) as well.
Jakarta EE 10 brings the first wave of those long-awaited innovations.