I am currently working on an Android app which uses Resteasy-mobile for the REST(JAXRS) implementation, and Jackson for serialization to and from JSON. The latter is done by Resteasy in the background.
Serverside I have a Jersey webservice, also using jackson for serialization to/from JSON.
Both the Resteasy client, and the webresource implement a JAXRS annotated interface like this:
@Path("/equipment")
@Consumes({"application/json"})
@Produces({"application/json"})
public interface AndroidEquipmentResourceIF
{
@GET
public Model getModel();
@GET
@Path("/version")
public String getVersion();
@GET
@Path("/{eId}")
public List<Equipment> getEquipmentListWithId(@PathParam("eId") String eId);
}
Clientside, this interface is loaded into a factory, and returns an object with this interface, which deals with the HTTP/URL/serialization process when a method is called.
When an Equipment entity with a invalid ID is called, the server throws a WebApplicationException if the entity was not found. Clientside, this probably causes the method to return null.
If i’d like to do something with the status code, like 404 - Not found or 401 - Unauthorized, I probably need the response object right? I think Resteasy and Jersey do work with Response objects in the background, so is there a way to access the Response object, or status code, with Resteasy?
EDIT1: Forgot to mention the second option. I noticed it is possible to wrap an object in a response object. So if i’d replace all the specific domain object return types to Response, and wrap the object in the response somehow, i could extract the object from the Response object? This does not sound very clean to me, just for being able to see the status code. Also I’d like to keep away from manual parsing of JSON as far as possible.
EDIT2: Found out it is possible to intercept the WebApplicationException, the Resteasy client throws a ClientResponseFailure exception, which can be caught in a interceptor. Problem is, the exception is never thrown? It looks like it gets stuck in a infenite loop somewhere.
I am getting the following exception:
10-26 10:52:10.048: E/AndroidRuntime(282): Caused by: java.lang.StackOverflowError
10-26 10:52:10.048: E/AndroidRuntime(282): at java.util.regex.Matcher.reset(Matcher.java:151)
10-26 10:52:10.048: E/AndroidRuntime(282): at java.util.regex.Matcher.reset(Matcher.java:211)
10-26 10:52:10.048: E/AndroidRuntime(282): at java.util.regex.Matcher.<init>(Matcher.java:127)
10-26 10:52:10.048: E/AndroidRuntime(282): at java.util.regex.Pattern.split(Pattern.java:405)
10-26 10:52:10.048: E/AndroidRuntime(282): at java.util.regex.Pattern.split(Pattern.java:355)
10-26 10:52:10.048: E/AndroidRuntime(282): at java.lang.String.split(String.java:2125)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.plugins.delegates.MediaTypeHeaderDelegate.parse(MediaTypeHeaderDelegate.java:33)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.plugins.delegates.MediaTypeHeaderDelegate.fromString(MediaTypeHeaderDelegate.java:18)
10-26 10:52:10.048: E/AndroidRuntime(282): at javax.ws.rs.core.MediaType.valueOf(MediaType.java:173)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getMediaType(BaseClientResponse.java:362)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:346)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:319)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:442)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.copyFromError(BaseClientResponse.java:94)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.ClientResponseFailure.<init>(ClientResponseFailure.java:32)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.createResponseFailure(BaseClientResponse.java:488)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.createResponseFailure(BaseClientResponse.java:479)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.readFrom(BaseClientResponse.java:384)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:346)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:319)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:442)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.copyFromError(BaseClientResponse.java:94)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.ClientResponseFailure.<init>(ClientResponseFailure.java:32)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.createResponseFailure(BaseClientResponse.java:488)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.createResponseFailure(BaseClientResponse.java:479)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.readFrom(BaseClientResponse.java:384)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:346)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:319)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.getEntity(BaseClientResponse.java:442)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.core.BaseClientResponse.copyFromError(BaseClientResponse.java:94)
10-26 10:52:10.048: E/AndroidRuntime(282): at org.jboss.resteasy.client.ClientResponseFailure.<init>(ClientResponseFailure.java:32)
Got it working the following way:
Resteasy-mobile is a pruned version of JBOSS Resteasy 2.2.1 GA. A lot of things have been removed to make it work on Android. But a bit too much is removed.
I found out that if a
ClientResponseFailureis thrown, the Response its entity is cast to aBytearray, and will be read again for copying to theClientResponseFailure. Problem is, the pruned version only contains Messagebody reader/writers for Strings and text/plain, not for byte arrays. So when trying to read the entity, it tries to throw a newClientResponseFailurefor not being able to find aMessageBodyReaderthat is compatible withByte[]. It then casts this entity to a byte array again, tries to read it again, thus entering a infinite loop which will end with aStackOverflowError.By adding the missing
ByteArrayProviderandReadFromStreamclass from the non-pruned version of Resteasy to classpath(atorg.jboss.resteasy.plugins.providers.ByteArrayProviderandorg.jboss.resteasy.util.ReadFromStream), theClientErrorInterceptor(link) works like a charm!EDIT: I spoke a bit too soon, didn’t work like a charm yet.
The ResteasyProviderFactory has also been modified to get it working in Android. The .getInstance() method keeps returning a new instance, so if you register a ClientErrorInterceptor with:
ResteasyProviderFactory.getInstance().addClientErrorInterceptor(new DataExceptionInterceptor());It does not work. The ProxyFactory will use a new instance of the ResteasyProviderFactory internaly.To work around this problem, the ResteasyProviderFactory used for adding the interceptor, should be passed to the
ProxyFactory.create().Like this: