Troubleshooting Jersey REST Server and Client

The logging in Jersey, the reference JAX-RS implementation, is little sub-optimal. For example if it cannot find a method producing the expected MIME type then it will return "Unsupported mime type" to the client but won't log anything (which mime type was requested, which mime types are actually available, ...).  Debugging it isn't exactly easy either, so what to do?

Well, I don't know the ultimate solution but want to share few tips.

Enable Tracing of Request Matching

Jersey since version 1.1.5 supports request matching tracing, provided somehow detailed information about the matching process in the response headers.

To enable it for the Jersey Test framework you'd do something like this in your test class:

public class MyJerseyTest extends JerseyTest {

public MyJerseyTest() { super(new WebAppDescriptor .Builder("my.package.with.jaxrs.resources") .contextPath("myCtxPath") .servletPath("/myServletPath") .initParam("com.sun.jersey.config.feature.Trace", "true") .build()); }

// Your test methods here ...; you can get the trace headers via ClientResponse#getHeaders() }

To enable it for the server itself (though might be not such a good idea to enable this in production), you would set it in web.xml:
<web-app  ...>
        <servlet-name>Jersey REST Service for value codes</servlet-name>

</servlet> ... </web-app>
The headers, which you can obtain via e.g. curl -i or via ClientResponse#getHeaders(), might look like this:

{X-Jersey-Trace-008=[mapped exception to response: -> 415 (Unsupported Media Type)],
X-Jersey-Trace-002=[accept right hand path java.util.regex.Matcher[pattern=/myResource/([-0-9a-zA-Z_]+)(/.*)? region=0,17 lastmatch=/myResource/23/mySubresources]: "/myResource/23/mySubresources" -> "/myResource/23" : "/mySubresources"],
X-Jersey-Trace-003=[accept resource: "myResource/23" -> @Path("/myResource/{item: [-0-9a-zA-Z_]+}") com.example.MyExampleResource@41babddb],
X-Jersey-Trace-000=[accept root resource classes: "/myResource/23/mySubresources"],
X-Jersey-Trace-001=[match path "/myResource/23/mySubresources" -> "/application\.wadl(/.*)?", "/myResource/([-0-9a-zA-Z_]+)(/.*)?", "/myResource(/.*)?", "/mySubresources/([-0-9a-zA-Z_]+)(/.*)?"],
X-Jersey-Trace-006=[accept sub-resource methods: "myResource/23" : "/mySubresources", GET -> com.example.MyExampleResource@41babddb],
X-Jersey-Trace-007=[accept termination (matching failure): "/mySubresources"],
X-Jersey-Trace-004=[match path "/mySubresources" -> "/mySubresources(/)?", ""],
X-Jersey-Trace-005=[accept right hand path java.util.regex.Matcher[pattern=/mySubresources(/)? region=0,6 lastmatch=/mySubresources]: "/mySubresources" -> "/mySubresources" : ""]
, Transfer-Encoding=[chunked], Date=[Tue, 31 Jan 2012 14:48:26 GMT], server=[grizzly/2.1.2], Content-Type=[text/html; charset=iso-8859-1]}

provided that you have the class com.example.MyExampleResource annotated with @Path("/myResource/{item: [-0-9a-zA-Z_]+}") and a method annotated with @GET @Path("mySubresources") (and the class field @PathParam("item") Long item).

As you can see, there is still no info regarding accepted/supported MIME types.

Get Detailed Logging Into a File

Jersey uses Java logging, which is know for being difficult to configure. Here is a dirty trick to get detailed Jersey logs into a file:

public class MyJerseyTest extends JerseyTest {

@BeforeClass public static void setupJerseyLog() throws Exception { Handler fh = new ConsoleHandler(); // FileHandler("/tmp/jersey_test.log"); Logger.getLogger("").addHandler(fh); Logger.getLogger("com.sun.jersey").setLevel(Level.FINEST); }

// Your test methods here ... }

Notice that even thoug the log level is ALL, the logs still might be quite useless to troubleshoot some problems (such s the unsupported MIME type).

Configure Request/Response Logging Filters

Jersey provides a LoggingFilter that can be used to log request/response entities and it can be installed both into the server and the client. The com.sun.jersey.api.container.filter.LoggingFilter may be installed into the server via init-params, the com.sun.jersey.api.client.filter.LoggingFilter may be installed into the client via client.addFilter. JerseTest automatically installs the LoggingFilter into the client it uses if the system property "enableLogging" is set (to whatever- this must happen before JersyTest's constuctor).


// Alternatively, call this before the JersetTest constructor: System.setProperty("enableLogging", "true");
WebResource resource = resource();
resource.addFilter(new com.sun.jersey.api.client.filter.LoggingFilter());
ClientResponse response = resource.path(..)...get(..);


public MyJerseyIT() {
super(new WebAppDescriptor.Builder()
... // set paths, packages etc.
.initParam("com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.LoggingFilter")

Tips & Tricks

Beware: Remember to Call setUp!

If you write a JUnit 4 test, make sure that you by mistake don't override setUp without calling super.setUp. JerseyTest is JUnit 3 test and needs its setUp to be called to function properly. If your Grizzly container stops soon after being started and you're thus getting ConnectException: Connection refused, make sure that the method is called.

Sample AbstractJerseyTest Class with Best Practies

See my at GitHub - server setup, logging, tracing, response status check etc.

Tags: java api

Copyright © 2024 Jakub Holý
Powered by Cryogen
Theme by KingMob