Friday, April 3, 2009

Digging into Jersey JAX-RS: 3. Request flow

Logging

Now it is time for a more complete feel of how a request flow through the system. And to do that, we will enable logging for Jersey.

Jersey uses java.util.logging as the logging framework, so enabling logging for it is easy. Just throw in a logging.properties into the WEB-INF/classes with a log level of ALL for com.sun.jersey.

(However, I did not manage to view my log output in tomcat launched via Eclipse. Somehow the console displays only levels up to FINE, and I needed levels up to FINEST... so I launched it via the normal tomcat outside of Eclipse.)

So I made a request for the xml content, and then view the log file.

... and there was nothing logged about the request.

Guess it's back to the code for manual mental tracing :(

Request Flow

The entry point of jersey is of course, the filter we set up, com.sun.jersey.spi.container.servlet.ServletContainer. 

I first looked at the code of com.sun.jersey.spi.container.servlet.ServletContainer#doFilter, and I go... ???

It is part of a filter chain, but however, it does not involve the next filter in the chain! It simply.. consumes the current request and is absolutely sure that it will handle the request. That does not seem to play well with static content though, unless my filter does not catch /*.

But let me put aside that for now. So it flows through to com.sun.jersey.spi.container.servlet.WebComponent#service, where the application context, user principal, form input are set up, and request/response are stored in the local thread storage context (though why I am unsure).

Next would be com.sun.jersey.server.impl.application.WebApplicationImpl#handleRequest, which applies the request filters. 

com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule#accept is triggered, which tries to match the input path and process it. This is where the com.sun.jersey.server.impl.template.ViewableRule mentioned in the previous post comes into play. But there are others as well. 

It is highly likely, in our example, that com.sun.jersey.server.impl.uri.rules.ResourceObjectRule/ResourceClassRule are matched. They are created in com.sun.jersey.server.impl.application.WebApplicationImpl#processRootResources, where all located resources/singletons (that was scanned by jersey runtime in the web.xml configuration) annotated by Path are attached to the root rule.

Eventually, the com.sun.jersey.server.impl.uri.rules.HttpMethodRule comes into play, which dispatch the request to the java method of the resource.

And after the com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule#accept is completed, the response filters are applied to the response, and the output is written out.

javax.ws.rs.ext.MessageBodyWriter & javax.ws.rs.ext.MessageBodyReader

So where do these message body reader/writers come into play?

In com.sun.jersey.spi.container.ContainerResponse#write, the right message body writer is located via best media type match. 

And in com.sun.jersey.spi.container.ContainerRequest#getEntity, the right message body reader is located as well via best media type match.

It is interesting to note however, that the com.sun.jersey.spi.container.ContainerRequest#getEntity result is not cached internally. Which means that multiple calls of it might result in an error. I would assume that it is currently carefully handled.

Summary

It was an interesting exploration, and there are a lot of magic involved (magic to me. I just did a touch and go on purposes of most classes). I was however, rather disappointing in the servlet/filter implementation, in that it does not give the next in chain a chance to handle the request if it was not handled by jersey itself.

Now I am rather curious as to how the other implementations are. I ought to take a look too. And what will I do next after the look? Probably try to code up a website on pure jax-rs if possible, reverting to jersey's helper if unable to.