Monday Nov 30, 2009

Jersey apps can now be deployed to Google App Engine

OK, it's not very exciting but you can go to the URL and should be able to receive the same response, including the trace headers i previously talked about.

I wanted to upload the Sparklines sample but the AWT graphics stuff is currently blacklisted (like java.awt.image.BufferedImage).

When you deploy a Jersey application to GAE you will currently get some errors in the logs in the logs such as:

com.sun.jersey.core.spi.component.ProviderFactory __getComponentProvider: 
The provider class, class com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App, 
could not be instantiated. Processing will continue but the class will not be utilized

This is because Jersey cannot load the JAXB-related components as JAXB related classes are currently black listed. As i understand JAXB will be supported in Google App Engine version 1.2.8. So these deployment errors will go away.

The code is in the trunk and should take a couple of hours for artifacts to be pushed to the repo.

Friday Nov 27, 2009

Tracing in Jersey

A common complaint we have been hearing is that JAX-RS/Jersey can be hard to debug when the application is not behaving as expected, for example "why the \*!@& is my resource class not matching and instead the client is receiving a 404 response?"

It is difficult for the devloper to know what is going on because there is no visibility to how the request is matched and dispatched to the application.

A number of JAX-RS and Jersey features were inspired by Stapler the underlying Web/HTTP framework for Hudson. And tracing is another idea. With Hudson it is possible set a system property and trace messages in the form of response headers will be returned to the client, as described here.

Jersey now supports the same feature implemented in 1.1.5-ea-SNAPSHOT. Adding the following servlet/filter initialization parameter enables tracing:


If we modify the bookstore sample to include the above initialization parameter in the web.xml and go to the URL http://localhost:8080/bookstore/items/3/tracks/2/ then analyze the response headers using firebug we can see:

Trace headers have a number associated with. Not all clients or servers may preserve the order in which headers are written (the Jersey server and client does) so the numbering is useful to re-construct the order of tracing. It also helps when referring to a particular trace statement.

Trace 001:

X-Jersey-Trace-001  match path "/items/3/tracks/2/" -> "/application\\.wadl(/.\*)?", "/happy(/.\*)?", "(/.\*)?"

presents the request path and the initial set of regular expressions that will be matched, in order from left to right.

Trace 002:

X-Jersey-Trace-002  accept right hand path 
                        java.util.regex.Matcher[pattern=(/.\*)? region=0,18 lastmatch=/items/3/tracks/2/]: 
                        "/items/3/tracks/2/" -> "" : "/items/3/tracks/2/" 

presents what regular expression was matched in trace 001, "(/.\*)?". It is called "accept right hand path" because some left hand path of the request URI has been matched by an @Path declaration, "", leaving some right hand path of the request URI, "/items/3/tracks/2/", to be further matched (the URI matching process will terminate when there is no right hand path left).

Trace 003:

X-Jersey-Trace-003  accept resource: "" -> @Path("/") com.sun.jersey.samples.bookstore.resources.Bookstore@9f9655 

presents the resource that matched the left hand path in trace 002. In this case it is the Bookstore resource that is annotated with @Path("/").

Trace 004:

X-Jersey-Trace-004  match path "/items/3/tracks/2/" -> "/items/([\^/]+?)(/.\*)?", ""

presents a further match on the right hand path in trace 002.

Trace 005:

X-Jersey-Trace-005  accept right hand path 
                        java.util.regex.Matcher[pattern=/items/([\^/]+?)(/.\*)? region=0,18 lastmatch=/items/3/tracks/2/]: 
                        "/items/3/tracks/2/" -> "/items/3" : "/tracks/2/"

presents the left hand path, "/items/3", and the right hand path, "/tracks/2", that matched the regular expression, "/items/([\^/]+?)(/.\*)?", from the ordered set in trace 004.

Trace 006:

X-Jersey-Trace-006  accept sub-resource locator: "" : "items/3/" -> @Path("/items/{itemid}/") 
                        com.sun.jersey.samples.bookstore.resources.Bookstore@9f9655.getItem(java.lang.String) = 

presents the sub-resource locator that was invoked on the Bookstore resource. In this case it is the Java method getItem annotated with @Path("/items/{itemsid}"). This sub-resource locator returns an instance of the CD resource to match more of the right hand path, "/tracks/2".

The same pattern for traces 004 to 006 repeats itself for traces 007 to 009 with a sub-resource locator being matched on the CD resource that returns a Track resource.

Trace 010 indicates that URI matching has completed because there is no right hand path left and resource methods are ready to be matched.

Trace 011:

X-Jersey-Trace-011  accept resource methods: "items/3/tracks/2/", GET -> 

presents that resource methods will be matched on the Track resource for the GET request.

Trace 012

X-Jersey-Trace-012  accept implicit view: "" -> com.sun.jersey.samples.bookstore.resources.Track@6fb5ef, 

presents that an implicit view has been selected to process the GET request. The Track resource is the controller and the model, the JSP "/com/sun/jersey/samples/bookstore/resources/Track/index.jsp" is the view that processes the model.

Trace 013

X-Jersey-Trace-013  matched message body writer: com.sun.jersey.spi.template.ResolvedViewable@817f19, 
                        "text/html;qs=5" -> com.sun.jersey.server.impl.template.ViewableMessageBodyWriter@9f17b1 

presents that a message body writer was matched to process the response entity returned by the implicit view in trace 012. The ViewableMessageBodyWriter is selected to write a ResolvedViewable with the media type "text/html;qs=5".

The above traces present how Jersey is returning an HTML page to the browser. Curl can be utilized to obtain XML instead:

$ curl -i http://localhost:8080/bookstore/items/3/tracks/2/
HTTP/1.1 200 OK
X-Powered-By: Servlet/3.0
Server: GlassFish v3
X-Jersey-Trace-000: accept root resource classes: "/items/3/tracks/2/"
X-Jersey-Trace-001: match path "/items/3/tracks/2/" -> "/application\\.wadl(/.\*)?", "/happy(/.\*)?", "(/.\*)?"
X-Jersey-Trace-002: accept right hand path 
                        java.util.regex.Matcher[pattern=(/.\*)? region=0,18 lastmatch=/items/3/tracks/2/]: 
                        "/items/3/tracks/2/" -> "" : "/items/3/tracks/2/"
X-Jersey-Trace-003: accept resource: "" -> @Path("/") com.sun.jersey.samples.bookstore.resources.Bookstore@19d1c871
X-Jersey-Trace-004: match path "/items/3/tracks/2/" -> "/items/([\^/]+?)(/.\*)?", ""
X-Jersey-Trace-005: accept right hand path 
                        java.util.regex.Matcher[pattern=/items/([\^/]+?)(/.\*)? region=0,18 lastmatch=/items/3/tracks/2/]: 
                        "/items/3/tracks/2/" -> "/items/3" : "/tracks/2/"
X-Jersey-Trace-006: accept sub-resource locator: "" : "items/3/" -> @Path("/items/{itemid}/") 
                        com.sun.jersey.samples.bookstore.resources.Bookstore@19d1c871.getItem(java.lang.String) = 
X-Jersey-Trace-007: match path "/tracks/2/" -> "/tracks/([\^/]+?)(/.\*)?", ""
X-Jersey-Trace-008: accept right hand path 
                        java.util.regex.Matcher[pattern=/tracks/([\^/]+?)(/.\*)? region=0,10 lastmatch=/tracks/2/]: 
                        "/tracks/2/" -> "/tracks/2/" : ""
X-Jersey-Trace-009: accept sub-resource locator: "items/3/" : "tracks/2/" -> @Path("/tracks/{num}/") 
                        com.sun.jersey.samples.bookstore.resources.CD@4eb24759.getTrack(int) = 
X-Jersey-Trace-010: match path "" -> ""
X-Jersey-Trace-011: accept resource methods: "items/3/tracks/2/", GET -> 
X-Jersey-Trace-012: matched resource method: public com.sun.jersey.samples.bookstore.resources.Track 
X-Jersey-Trace-013: matched message body writer: com.sun.jersey.samples.bookstore.resources.Track@435792a0, 
                        "application/xml" -> com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App@27cce278
Content-Type: application/xml

The traces are the same as previously presented up to trace 012. Trace 012 presents the resource method getXml that is invoked on the Track resource. Trace 013 presents that XMLRootElementProvider has been selected to serialize out Track with the media type "application/xml".

Like Stapler/Hudson Jersey also supports per-request tracing. Adding the following servlet/filter initialization parameter enables per-request tracing:

The client enables tracing by sending a request header "X-Jersey-Trace-Accept" with any value.




« July 2016