Seam Servlet 3.0.0.Alpha2 released, take control of the request!
Posted by dan.j.allen in Dan's Blog on Dec 8, 2010 1:44:23 AMRight on the tail of the first alpha, Seam Servlet 3.0.0.Alpha2 has been released. Why the quick turnaround? Because we wanted to put two features in your hands so you can take control of that request.
- The ServletRequestContext lifecycle object
- Initial Seam Catch integration
The first feature let's you control the beginning of the request more easily. The second allows you to handle the tail of the request if bad $#*! happens.
If filters were events
While examining the Servlet API to determine what enhancements to make, I realized that it offers only a hodgepodge of lifecycle events. You get an event when the request is initialized, but no matching event for the response. You have to resort to using a servlet filter to access the response at the beginning of the request. But then you have to worry about whether you are handling an HTTP request and about managing the filter chain.
In the first alpha, I added a synthetic event for when the response is initialized and destroyed using a built-in filter. You can listen for this event using a CDI observer:
void onResponseInitialized(@Observes @Initialized ServletResponse response) { response.addHeader("Cache-Control", "no-cache"); }
You can even inject the request object into the observer method so that you can access both the request and the response:
void setEncoding(@Observes @Initialized ServletResponse res, ServletRequest req) throws Exception { req.setCharacterEncoding("UTF-8"); res.setCharacterEncoding("UTF-8"); }
While this is a start, I thought of a few enhancements that would really make this ring:
- Group the request and response in a type together
- Short-circuit the request if the response is committed by a listener
- Allow the listener to observe a specific servlet path
Wrapping the request and response together was a no-brainer. One parameter is better than two. So I created the ServletRequestContext to provide access to the following implicit objects:
- ServletRequest
- ServletResponse
- ServletContext
I also created its HTTP-counterpart, HttpServletRequestContext, which provides access to these objects:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- ServletContext
These two objects can also be injected into any managed bean during the request.
We can now simplify the character encoding listener from the listing above:
void setEncoding(@Observes @Initialized ServletRequestContext ctx) throws Exception { ctx.getRequest().setCharacterEncoding("UTF-8"); ctx.getResponse().setCharacterEncoding("UTF-8"); }
So far, we've only been considering listeners that contribute to the response. But what if you want to commit the response, such as send an HTTP error response or redirect to another page? With Seam Servlet, you can!
Here's a basic example showing a welcome page filter that redirects to a start page if the root URL is requested.
void redirectToStartPage(@Observes @Initialized HttpServletRequestContext ctx) throws Exception { String servletPath = ctx.getRequest().getServletPath(); if (servletPath == null || servletPath.length() == 0 || servletPath.equals("/")) { String startPage = ctx.getResponse().encodeRedirectURL( ctx.getContextPath() + "/start.jsf"); ctx.getResponse().sendRedirect(startPage); } }
You also now have the option of associating the listener with a specific servlet path, in this case the context root, using the @Path qualifier:
void redirectToStartPage(@Observes @Path("") @Initialized HttpServletRequestContext ctx) throws Exception { String startPage = ctx.getResponse().encodeRedirectURL(ctx.getContextPath() + "/start.jsf"); ctx.getResponse().sendRedirect(startPage); }
You never have to write a Servlet listener, Servlet or Filter again!
When bad $#*! happens
Exceptions are a fact of life. As developers, we need to be prepared to deal with them in the most graceful manner possible. The Servlet API only gives you limited options for doing so:
- send an HTTP status code
- forward to an error page (servlet path)
To squeeze you into a tighter corner, you have to configure these exception mappings in web.xml. It just doesn't make this task easy. We have something that does.
Seam Catch provides a simple, yet robust foundation for modules and/or applications to establish a customized exception handling process. By employing a delegation model, Catch allow exceptions to be addressed in a centralized, extensible and uniform manner.
That sounds good, so how do you make use of it? That's where the Catch integration in Seam Servlet comes in.
When the request goes south (i.e., an unhandled exception occurs), and both Seam Servlet and Seam Catch are on the classpath, the exception will be caught and forwarded to your exception handler methods. How you deal with the exception from there is up to you.
Here's how you send an HTTP error response with a friendly message for any unhandled exception that occurs:
@HandlesExceptions public class ExceptionHandlers { void handleAll(@Handles @WebRequest CaughtException<Throwable> caught, HttpServletResponse response) { response.sendError(500, "Sorry things didn't work out the way you expected."); } }
The @WebRequest qualifier distinguishes this exception handler from one that might apply to a JAX-RS request. (It will be optional in a future release)
Let's add another handler that sends a 404 response when the application-specific AccountNotFoundException is thrown.
void handleAccountNotFound(@Handles @WebRequest CaughtException<AccountNotFoundException> caught, HttpServletResponse response) { response.sendError(404, "Account not found: " + caught.getException().getAccountId()); }
Since you have access to the response object, you can send any sort of response you want to the browser. The next release of Seam Servlet will provide handler annotations so you can configure these two responses above declaratively. Stay tuned!
Get started!
As with all Seam 3 modules, the Seam Servlet module is published to the JBoss Community repository. Here's the dependency declaration you need to add to your POM to include this module in your project:
<dependency> <groupId>org.jboss.seam.servlet</groupId> <artifactId>seam-servlet-impl</artifactId> <version>3.0.0.Alpha2</version> </dependency>
If you're not deploying to JBoss AS, you also need JBoss Logging (a portable logging abstraction):
<dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.0.0.Beta4</version> <scope>provided</scope> </dependency>
There's plenty more features to explore in Seam Servlet, and more to come, so check it out!
[ Module Overview ] | [ Download Distribution ] | [ JIRA ] | [ Reference Guide ]
Comments