users@jersey.java.net

Re: [Jersey] strange "double dispatching" problem

From: Bryon Jacob <bryon_at_jacobtx.net>
Date: Wed, 21 Jan 2009 06:38:03 -0600

Paul - you are, of course, exactly correct. The "client" that I was
using is Firefox -- I was just hitting the URL in my browser to view
the responses. Firefox must be doing some sort of handling of an Atom
Feed that causes it to make the request twice. Weird, but not all
that relevant. When I curl-ed the URL, I only see one request, as
expected. Same with a quick test using Jersey's client.

didn't think about the browser maybe treating an Atom feed specially,
but of course that makes some kind of sense... thanks!

  - Bryon


On Jan 21, 2009, at 3:17 AM, Paul Sandoz wrote:

> Hi Byron,
>
> Is it possible send the output from the thread dump you mention?
> that will give me some more clues.
>
> How is the client consuming the Feed, does the client make another
> request for the consumption of Atom feed documents and does not make
> another request for the consumption of Atom entry documents? That
> would be my initial guess because there is nothing in Jersey itself
> to result in another dispatch to the resource method.
>
> One way to check if this is the case is to add a logging filter to
> the server side, add the following to your init params of your
> servlet declaration:
>
> <init-param>
> <param-
> name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
> <param-
> value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
> </init-param>
> <init-param>
> <param-
> name>com.sun.jersey.spi.container.ContainerResponseFilters</param-
> name>
> <param-
> value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
> </init-param>
>
> The logging filter currently prints request and responses to
> System.out (perhaps it should use JDK logging by default? as pointed
> out by another developer for the client side logging filter).
>
> Another way to check is to use a different client, say curl.
>
> Paul.
>
> On Jan 20, 2009, at 11:58 PM, Bryon Jacob wrote:
>
>> Hi all - I am having an extremely strange problem with Jersey
>> dispatching to one of my resource methods TWICE. The strangest
>> thing about it seems to be that the problem is related to the
>> return type of the method!
>>
>> This problem is happening pretty deep in my resource hierarchy, so
>> I'm not pasting ALL of the code here - but I have boiled it down to
>> a pretty simple example. If I have this method defined:
>>
>> @GET
>> @Path("/_/{whatever : .*}")
>> public Entry getConfused(@PathParam("whatever")String whatever) {
>> System.out.println(whatever);
>> Entry entry = AbderaMarshaller.factory().newEntry();
>> entry.setId(whatever);
>> return entry;
>> }
>>
>> everything works fine -- that is, when I go to the URL http://localhost:8000/A/B/C/_/blah
>> (where A/B/C is the path to the resource on which this method is
>> defined), I see "blah" printed to the console exactly once, and I
>> get an <entry> as a response. When I replace that method with this:
>>
>> @GET
>> @Path("/_/{whatever : .*}")
>> public Feed getConfused(@PathParam("whatever")String whatever) {
>> System.out.println(whatever);
>> Feed feed = AbderaMarshaller.factory().newFeed();
>> Entry entry = AbderaMarshaller.factory().newEntry();
>> entry.setId(whatever);
>> feed.addEntry(entry);
>> return feed;
>> }
>>
>> and go to the same URL, I get the <feed> element back, just as I
>> expected, but "blah" is printed to the console TWICE! How can this
>> be?
>>
>> The Entry and Feed elements are from Abdera's object model, and my
>> registered provider is my custom AbderaMarshaller class (source
>> below) - everything seems to work properly, no exceptions are being
>> thrown, and my provider is being called to marshall the Abdera
>> objects to XML, but with the second variation of the method, it is
>> always called TWICE when a matching URL is accessed.
>>
>> Other variables that might be interesting...
>> - I'm using Jersey 1.0.1
>> - I'm using jersey-spring (also 1.0.1) to compose the application
>> with Spring 2.5 annotations.
>> - I'm running java 1.6, and I'm running all of this using the
>> com.sun.net.httpserver.HttpServer class.
>>
>> I figure either I must be doing something blindingly dumb, or
>> there's a subtle bug in Jersey, since I can't imagine any reason
>> for jersey to dispatch to my methods twice (BTW - I did verify that
>> this is exactly what is happening... I did a Thread.dumpStack() in
>> the failing method above, and the exact same stack, starting
>> somewhere in Jersey and ending up with a call to my method, was
>> repeated twice...)
>>
>> A quick search of the list archives didn't turn up anything
>> relevant... so I thought I'd post it here and see if anyone had
>> seen this. Anyways - here's the source to my provider class -
>> thanks for any help y'all can offer!
>>
>>
>> @Provider
>> @Produces({APPLICATION_ATOM_XML, APPLICATION_XML, TEXT_XML})
>> @Consumes({APPLICATION_ATOM_XML, APPLICATION_XML, TEXT_XML})
>> public class AbderaMarshaller implements MessageBodyWriter,
>> MessageBodyReader {
>>
>> public void writeTo(Object o, Class aClass, Type type,
>> Annotation[] annotations, MediaType mediaType,
>> MultivaluedMap multivaluedMap, OutputStream
>> outputStream)
>> throws IOException, WebApplicationException {
>> ((ExtensibleElement) o).writeTo(outputStream);
>> }
>>
>> public Object readFrom(Class aClass, Type type, Annotation[]
>> annotations, MediaType mediaType,
>> MultivaluedMap multivaluedMap,
>> InputStream inputStream)
>> throws IOException, WebApplicationException {
>> return parser().parse(inputStream).getRoot();
>> }
>>
>> public boolean isWriteable(Class aClass, Type type,
>> Annotation[] annotations, MediaType mediaType) {
>> return isAbderaExtensibleElement(aClass);
>> }
>>
>> public long getSize(Object o, Class aClass, Type type,
>> Annotation[] annotations, MediaType mediaType) {
>> return -1;
>> }
>>
>> public boolean isReadable(Class aClass, Type type, Annotation[]
>> annotations, MediaType mediaType) {
>> return isAbderaExtensibleElement(aClass);
>> }
>>
>> private boolean isAbderaExtensibleElement(Class clazz) {
>> return ExtensibleElement.class.isAssignableFrom((Class<?>)
>> clazz);
>> }
>>
>> private static final transient ThreadLocal<Parser> PARSER = new
>> ThreadLocal<Parser>() {
>> protected Parser initialValue() {
>> return new FOMParser();
>> }
>> };
>>
>> public static Parser parser() {
>> return PARSER.get();
>> }
>>
>> private static final transient ThreadLocal<FOMFactory> FACTORY =
>> new ThreadLocal<FOMFactory>() {
>> protected FOMFactory initialValue() {
>> return new FOMFactory();
>> }
>> };
>>
>> public static FOMFactory factory() {
>> return FACTORY.get();
>> }
>>
>> }
>>
>>
>