Friday, March 27, 2009

Digging into Jersey JAX-RS: 1. setting up

So I started with a maven web application in Eclipse to do my test drive of jersey.

I added the repositories, and the jersey-server dependency (https://jersey.dev.java.net/source/browse/*checkout*/jersey/tags/jersey-1.0.2/jersey/dependencies.html)

I started out with a filter, instead of the servlet. I always prefer filters. I also added the following initialization parameters:

com.sun.jersey.config.property.packages: me.kentlai.jaxrs
com.sun.jersey.config.feature.Redirect: true
com.sun.jersey.config.feature.ImplicitViewables: true

com.sun.jersey.config.property.packages

I'm probably dumb, because I had a hard time finding out documentations for what these fields actually do. I do know that com.sun.jersey.config.property.packages tells jersey the package to find the resource classes, but all examples I saw specified only a single package.

Does it work for multiple packages? It did say packages..

The documentation did mention that the search is for the declared package and all sub-packages, so that would be fine. But how about two different packages?

I check in the source file. Multiple packages is detected by having the com.sun.jersey.config.property.packages. So how can I go about inserting multiple values?

So apparently, the documentation can be found in com.sun.jersey.spi.container.servlet.ServletContainer. Use ; as seperators.

I noticed two other properties mentioned:

com.sun.jersey.config.property.resourceConfigClass
javax.ws.rs.Application

I will probably look at them in a later date.

In conclusion: The value for com.sun.jersey.config.property.packages can be a ; seperated string value, as a list of packages to scan in. The scan works for sub-packages too.

com.sun.jersey.config.feature.Redirect

What is this? After doing a grep through the source, I found the documentation in com.sun.jersey.api.core.ResourceConfig. So apparently, what it does is this (when set to true)

request uri -> /test/abc
path declared -> /test/abc/

And the client will be redirected to /test/abc/. Hmm, seems no particular strong reason for me to turn that on. I'll keep it to false (default value) then.

I saw two other properties of interest in com.sun.jersey.api.core.ResourceConfig

com.sun.jersey.config.feature.NormalizeURI & com.sun.jersey.config.feature.CanonicalizeURIPath

They have a default value of false, and they seem to be about normalizing uri. For example, com.sun.jersey.config.feature.NormalizeURI would turn the request uri of a/b/../c into a/c. I'm not sure if it affects the routing, but I would not mind it turned on. Maybe some people like to check for intrusion attacks?

I'm not too sure what the second one does, but from what I understand after digging into the code, it seems to turn the request uri of ///a/b/c into /a/b/c. Guess it can be turned on as well.

I definitely need enlightenment on why some people might want this to be false.

It is also interesting to note that for com.sun.jersey.config.feature.CanonicalizeURIPath to take place, com.sun.jersey.config.feature.NormalizeURI must be true too.

The code to handle the normalization is found in com.sun.jersey.server.impl.container.filter.NormalizeFilter.

A filter?

Filters

Jersey supports filters! They are implemented in the two interfaces com.sun.jersey.spi.container.ContainerRequestFilter, com.sun.jersey.spi.container.ContainerResponseFilter, such that if your custom filter can care only before processing, or after processing, or if inclined, both. There are quite a few custom filters defined.

So where/how are they registered?

They seemed to be located by com.sun.jersey.server.impl.container.filter.FilterFactory (and it turns out that there are other filters like resource filter too), which the com.sun.jersey.server.impl.application.WebApplicationImpl will query and execute in order of declaration. Additional filters can be defined in the following init-params, which are executed before the apparently in-built filters:

com.sun.jersey.spi.container.ContainerRequestFilters
com.sun.jersey.spi.container.ContainerResponseFilters
com.sun.jersey.spi.container.ResourceFilters

The documentation of these can once again be found in com.sun.jersey.api.core.ResourceConfig. But in short, they are either a single string value, or a list of string values, which are fully qualitifed class names. It is interesting to note that the documentation and code will create these as singletons. That is, if you have a single class specified in all three filters, they are created as a single instance.

But where are the in-built filters defined? I had to dig deep, and found a com.sun.jersey.spi.service.ServiceFinder class. It is a way to locate services 'exported' by jars, as long as they have a folder META-INF/services in the jar, with the class files. So, opening the jersey-server jar, I found a single file with this value:

file: com.sun.jersey.spi.container.ContainerRequestFilters
values: com.sun.jersey.server.impl.container.filter.NormalizeFilter

There are more files in there, but for my current purpose, knowing this is sufficient.

There are more filters available too, like com.sun.jersey.api.container.filter.GZIPContentEncodingFilter, com.sun.jersey.api.container.filter.LoggingFilter, etc.

com.sun.jersey.config.feature.ImplicitViewables

This has to be on for jersey to resolve your jsp view implicitly. I'm curious on a few things.

1. The example had the jsp files in the root of the webapp. Convention had them inside WEB-INF. There should be a way to change that.

2. Is jsp the only option? Does it actually expose the ability to do other views like freemarker/velocity? There is mention of a com.sun.jersey.spi.template.TemplateProcessor, but how is it hooked up?

The magic seems to be in two places: com.sun.jersey.server.impl.model.ResourceClass and com.sun.jersey.server.impl.template.ViewableMessageBodyWriter.

The com.sun.jersey.server.impl.template.ViewableRule seems to be responsible for matching additional subpaths to the original resource. There is an additional match of empty/null subpath, and a match of an additional segment.

Eg, resource A with path /a/b. A request comes in as /a/b. The implicit view will be triggered. A request /a/b/c will be processed. But a request /a/b/c/d will not be processed by the resource A.

It iterates through the template processors from com.sun.jersey.spi.template.TemplateContext (it is injected, but what are the values?). And it only accepts HTTP GET method. What is the implications? I guess it would mean that if a request came in without a properly matched resource/path, it should not be redispatched to an implicit view. I am not too sure what it might really mean until I made experiments with HTTP POST.

A subclass com.sun.jersey.server.impl.template.TemplateFactory is created in com.sun.jersey.server.impl.application.WebApplicationImpl. This will then be injected into the com.sun.jersey.server.impl.template.ViewableRule. So there are two ways to have the com.sun.jersey.spi.template.TemplateProcessor hooked up. Services (as mentioned above), and providers. Providers seems to be classes marked with the annotation javax.ws.rs.ext.Provider.

Except that com.sun.jersey.server.impl.container.servlet.JSPTemplateProcessor is not marked with it! Mysterious..

But it turns out I missed out another way template processors can be injected. Via singleton instances registered with com.sun.jersey.api.core.ResourceConfig. And the creation and registration is done in com.sun.jersey.spi.container.servlet.WebComponent, which is the parent class of the jersey filter/servlet we usually registers.

Now on to com.sun.jersey.server.impl.container.servlet.JSPTemplateProcessor. It actually mentions, in the class, a configuration property used to lookup JSP files! com.sun.jersey.config.property.JSPTemplatesBasePath. Woo hoo!

Now back to com.sun.jersey.server.impl.template.ViewableMessageBodyWriter. Now that I am used to how things work, I check to see if there is a file javax.ws.rs.ext.MessageBodyWriter in the META-INF/services of the jar. Sure enough, there is one, with the entry com.sun.jersey.server.impl.template.ViewableMessageBodyWriter. And this is how it is injected into the application.

Looking at the writeTo code of com.sun.jersey.server.impl.template.ViewableMessageBodyWriter. It iterates through the template processors from com.sun.jersey.spi.template.TemplateContext again. It asks each template processor to resolve a path (which is the fully qualified class name, with . replaced with /, and append with 'index' if the path had been empty.

But wait.

The iteration does not stop with the first resolved template! All resolved template are written to! What could this mean? Potentially we could register two com.sun.jersey.spi.template.TemplateProcessor, and if both matches, could write its output out to the output stream? It does seem to indicate that..

To clarify more on that, we have to find out more about this com.sun.jersey.server.impl.template.ResolvedViewable mentioned in the class. It is the only exception to iterating over the available template processors. Who would set this?

Turns out we have to go back to look at com.sun.jersey.server.impl.template.ViewableRule. In its accept request check, it actually stops at the first resolved template processor, and set the response.

Things might be getting more confusing than required.

I will just dig into com.sun.jersey.server.impl.container.servlet.JSPTemplateProcessor first.

When asked to resolve a template, it actually attempts to locate the resource via the servlet context. If there is a perfect match, the template string is returned immediately. Otherwise it appends '.jsp' and try again. If there is still no match, a null string is returned. Pretty straightforward.

And writeTo basically commits the status/headers out to the response first, and then does a forward with the dispatcher to the new jsp.

While I'm here, I might as well find out what variables are exposed to the dispatched jsp. These are what I found and suspect what they mean (I gotta test it to find out)

1. _basePath: Original path to the resource

2. resource: Resource that handled the request

3. it: The resulting data

4. _request: Original request instance

5. _response: Original response instance

End of this post

This has been a long post (for my standard). I had approached this as a development and exploration diary, to pen down my discovery. All I had done so far has only been to figure out initialization parameters, and how the implicit view might flow.

I should stress that this is not meant to be an introduction on how to use Jersey. This is a post on understanding how Jersey works (even internally), in the hope to be able to use it more effectively. This is an exploration process, and as such, some of what was mentioned might be even wrong, different, or changed in future versions. And some of the exploration might be incomplete, but they are sufficient for the understanding I desired.

Coming up in the next post, I will follow up with a simple, barebone jersey web application with implicit views.

blog comments powered by Disqus