Resteasy 3.0-beta-6 Released – Closer to Compliance

Leave a comment

A lot of fixes and changes as a result of trying to pass the JAX-RS 2.0 TCK.  Specifically, the TCK has become really strict.  Especially with the request matching algorithm which I talk about in this blog:

https://billburke.wordpress.com/2013/05/29/the-poor-jax-rs-request-dispatching-algorithm/

There were also some grey areas of the specification that the TCK now tests for.  Resteasy passed some, didn’t pass others, but at least now its consistent.  Also, I was going to write a ton of unit tests for the new client, filter, interceptor, and async apis, but the TCK already does a tremendous job in doing this.  I think you’ll see a great improvement in overall quality for the Resteasy 3.0-beta-6 release.

We still have about 2% of the tests failing due to challenges or bugs in the TCK.  Hopefully we’ll get those resolved soon and we can ship Resteasy 3.0 within the next month.

You know where to find everything!  http://jboss.org/resteasy

The poor JAX-RS Request Dispatching Algorithm

5 Comments

As we’re rolling out Resteasy 3.0, we have to pass the JAX-RS TCK.  The good thing about this is that the TCK has grown massively is size and has a lot more test coverage for all old and new features of JAX-RS.  It allowed me to uncover a few bugs I would not have found without the TCK.  An unfortunate downside the TCK also got a lot stricter in some of the weak areas of the JAX-RS specification, particularly the request dispatching algorithm.  I’ll be blunt, the algorithm is poor.  IMO, the old spec leads made a huge mistake in introducing implementation details to the specification and now we have a poor algorithm we are stuck with.  Us vendors cannot innovate and improve it because the TCK has backed us into a corner and the licensing fine print of Java EE makes it really hard for us to ship things that diverge from the spec.  Here are a bunch of problems that used to work in Resteasy, but will no longer work because the TCK tests every fine detail of the JAX-RS matching algorithm.

  •  The @Path annotation at the class level is matched first before matching any resource methods.  Only classes with the best and exact regular expressions are picked.  Then the rest of the request is matched with remaining methods.  So this won’t work anymore with a spec compliant algorithm:
Request: OPTIONS /foo

@Path("/foo")
public class Foo {
   @GET
   public String get() {...}
}
@Path("/{.*}")
public class OptionsDefault {
   @OPTIONS
   public String options() {...}
}

Earlier versions of Resteasy would match OptionsDefault.options().  Now, this method will not match according to the spec rules and you’ll get the default JAX-RS OPTIONS behavior.

  • Locators are never resolved if there are resource methods that match the request.  For example
PUT /foo/sub

@Path("/foo")
public class Foo {
   @GET
   @Path("sub")
   public String get() {...}

   @Path("{id}")
   public Locator locator() { return new Locator(); }
}

public class Locator{
   @PUT
   public void put() {...}
}

You’d think that the request would resolve to Locator.put() but you’d be wrong! Because there is a resource method whose path matches the request, but not the method you’d get a 405 response from the server. What’s interesting if you flip the expressions, a PUT request would work, but a GET request wouldn’t!

PUT /foo/sub

@Path("/foo")
public class Foo {
   @GET
   @Path("{id}")
   public String get() {...}

   @Path("sub")
   public Locator locator() { return new Locator(); }
}
  • It is possible to have poorer matches
GET /fart
Accept: text/plain

  @GET
  @PATH("foo") 
  @Produces("text/plain") 
  public String get1() {} 

  @GET
  @Path("{text}") 
  @Produces("text/plain") 
  public String get2() {} 

  @GET
  @Path("f{text}") 
  @Produces("text/*") 
  public String get3() {}

You would think that GET /fart would match the get3() method because it is more specific path, but you’d be wrong.  Because get3() has a less specific @Produces get2() would match.  This is weird because the spec originally tells you to sort expressions on a best-match basis but then ditches this information to match Accept headers.

Another related note is the default returned media type.Right now the default is dependent on the deployment.  If there is no Produce header, then the returned media type defaults to a union of the Accept header and explicit media types of all available MessageBodyWriters.  There goes your portability!  Instead, implementations should be allowed to specify their own default or even make it configurable.  But, of course we can’t do that!

Granted some of these issues are edge cases, but IMO, some are not.  The specification has 2 pages on english/pseudo-academic algorithm syntax to describe this very complex, but poor algorithm.  Users will get frustrated trying to understand it.  The experts themselves argued for days on interpretation of the specification.  Users will scratch there head wondering why certain classes will match and some won’t and blame the vendor’s implementation.  Resteasy had at least 4 user-reported regression tests that failed as a result of following the specfication matching algorithm religiously.  I know these users will be back complaining that Resteasy 3.0 does not work for them when Resteasy 2.3.x did.

Resteasy 3.0-beta-5 Released

5 Comments

Did a bit of refactoring of the SPIs to improve generics support among other bug fixes.  A side effect to this is that there is now a programmatic interface that allows you to register un-annotated resource classes.  Also, bumped Jackson to 1.9.12 and also added an additional Jackson2 provider.  See docs for more details.

http://jboss.org/resteasy