You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by John Baker <jb...@dryfish.org.uk> on 2013/07/29 12:52:05 UTC

JAX-RS method matching

Hello

I'm having trouble with CXF's JAX-RS implementation. The service is
struggling to match the correct method defined on an interface. Please
consider:

@Path("/update")
public interface MyService {

  @PUT
  @Path("/{id}")
  public void updateFruit(@QueryParam("id") String id, Banana banana);
  
  @PUT
  @Path("/{id}")
  public void updateAnimal(@QueryParam("id") String id, Dog dog);
}

where both Banana and Dog extend a common object. When making a call to
updateFruit from a jax-rs client, it calls updateAnimal. I've traced the
problem into JAXRSUtils and I'm wondering if there's any solution, or do
these methods need unique paths?

Thanks


John
-- 
John Baker

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 29/07/13 12:48, John Baker wrote:
> I can only determine the parameter type / method by the message body, so
> I guess I need a generic "put" method with an object, and use the
> provider to create the correct object, before using an if instanceof in
> the method.
>
May be you can use a custom provider which will have "@Context Providers 
providers;" injected - your provider will read IS, do the checks to 
determine the type, restore IS and then ask 'providers' to find the 
right provider, or if you know that it is always JAXB for ex, then 
simply delegate to JAXBElementProvider after the actual type has been 
determined, it will be simpler
Cheers, Sergey

Re: JAX-RS method matching

Posted by John Baker <jb...@dryfish.org.uk>.
I can only determine the parameter type / method by the message body, so
I guess I need a generic "put" method with an object, and use the
provider to create the correct object, before using an if instanceof in
the method.

-- 
John Baker

On Mon, Jul 29, 2013, at 12:46 PM, Sergey Beryozkin wrote:
> On 29/07/13 12:41, John Baker wrote:
> > Sergey,
> >
> >
> >> You can have "@Context MessageContext" or any of standard JAX-RS
> >> contexts injected into your custom provider
> >
> > Just to clarify, should I inject this as a class variable on the
> > provider, ie are the providers thread safe and is the message updated on
> > each call to readFrom?
> >
> Yes, the injected contexts ate thread-safe
> 
> Sergey
> >
> > John
> >
> 
> 

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 29/07/13 12:41, John Baker wrote:
> Sergey,
>
>
>> You can have "@Context MessageContext" or any of standard JAX-RS
>> contexts injected into your custom provider
>
> Just to clarify, should I inject this as a class variable on the
> provider, ie are the providers thread safe and is the message updated on
> each call to readFrom?
>
Yes, the injected contexts ate thread-safe

Sergey
>
> John
>



Re: JAX-RS method matching

Posted by John Baker <jb...@dryfish.org.uk>.
Sergey,


> You can have "@Context MessageContext" or any of standard JAX-RS 
> contexts injected into your custom provider

Just to clarify, should I inject this as a class variable on the
provider, ie are the providers thread safe and is the message updated on
each call to readFrom?


John

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 29/07/13 12:27, John Baker wrote:
> Hello
>
> Thanks for responding so quickly.
>
>> JAX-RS selection algorithm sees these 2 methods as equal candidates.
>> IMHO having unique paths for different type of resources (from the
>> client's POV, not from the Java hierarchy perspective) is reasonable
>> IMHO, but if you prefer or *have to* use the same method signatures, then
>
> Yes, it is very bad, I agree. But the client disagrees. :)
>
> I'll look into your solution but could you also tell me how to get the
> Message object, or more importantly, the URI from an extension of an
> AbstractConfigurableProvider? I've got something to translate the
> message body into objects (ie deserialise it) and there may be a
> solution in there..
>
You can have "@Context MessageContext" or any of standard JAX-RS 
contexts injected into your custom provider

Sergey
>
> John
>



Re: JAX-RS method matching

Posted by John Baker <jb...@dryfish.org.uk>.
Hello

Thanks for responding so quickly.

> JAX-RS selection algorithm sees these 2 methods as equal candidates.
> IMHO having unique paths for different type of resources (from the 
> client's POV, not from the Java hierarchy perspective) is reasonable 
> IMHO, but if you prefer or *have to* use the same method signatures, then

Yes, it is very bad, I agree. But the client disagrees. :)

I'll look into your solution but could you also tell me how to get the
Message object, or more importantly, the URI from an extension of an
AbstractConfigurableProvider? I've got something to translate the
message body into objects (ie deserialise it) and there may be a
solution in there..


John

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 29/07/13 12:52, John Baker wrote:
>
>> use CXF ResourceComparator,
>> http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Customselectionbetweenmultipleresources
>
> How is the ResourceComparator implementation registered with the
> service? Can it be done via an annotation on the interface?
>

Have you read the section :-) ?
It says:
"Starting from CXF 2.2.5 it is possible to register a custom 
ResourceComparator implementation using a 
jaxrs:server/resourceComparator element."

Actually, it has to be "jaxrs:server/jaxrs:resourceComparator", will fix it

Sergey

-- 
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
It looks quite perfect, though you can probably simplify and avoid 
checking a super-class, unless you can also have for example multiple 
PUT operations with intersecting path values or some complex Consumes 
expressions...

re returning 0: means you don't mind which method will be selected which 
is not your case, but yes, you can default to 0

Cheers, Sergey
On 29/07/13 14:01, John Baker wrote:
> This appears to be the answer:
>
>
>
>            // Check if CXF can make a decision
>
>            int cxfResult= super.compare(oper1, oper2);
>
>            if (cxfResult != 0)
>
>              return cxfResult;
>
>
>
>            Int result = 0;
>
>            String s = … message body.
>
>            String target= null;
>
>            if (s.startsWith(“banana”))
>
>              target= "updateFruit";
>
>            else if (s.startsWith(“dog”))
>
>              target= "updateAnimal";
>
>            if (target!=null) {
>
>              if (m1.equals(m2) && m1.equals(target))
>
>                result = 0;
>
>              else if (m1.equals(target))
>
>                result= -1;
>
>              else if (m2.equals(target))
>
>                result= 1;
>
>              else
>
>                result= m1.compareTo(m2);
>
>            }
>
>            return result;
>



Re: JAX-RS method matching

Posted by John Baker <jb...@dryfish.org.uk>.
This appears to be the answer:

 

          // Check if CXF can make a decision

          int cxfResult= super.compare(oper1, oper2);

          if (cxfResult != 0)

            return cxfResult;

 

          Int result = 0;        

          String s = … message body.

          String target= null;

          if (s.startsWith(“banana”))

            target= "updateFruit";

          else if (s.startsWith(“dog”))

            target= "updateAnimal";

          if (target!=null) {

            if (m1.equals(m2) && m1.equals(target))

              result = 0;

            else if (m1.equals(target))

              result= -1;

            else if (m2.equals(target))

              result= 1;

            else

              result= m1.compareTo(m2);

          }

          return result;

-- 
John Baker

On Mon, Jul 29, 2013, at 01:51 PM, John Baker wrote:
> 
> > The relevant RC method needs to help with ordering the method 
> > candidates, so return either -1 or 1 to help the runtime choose the 
> > right candidate
> 
> But what is "equal"? Is it the two resource infos pointing to
> updateFruit when i know the target is that method? So return 0 in that
> case?
> 
> And what's the algorithm when two different resource infos are passed,
> that may or may not be updateFruit?
> 
> (I think there may be an easier way to write this interface - simply
> list through the methods calling a method on an interface that returns
> true or false - but that's a discussion for another day.)

Re: JAX-RS method matching

Posted by John Baker <jb...@dryfish.org.uk>.
> The relevant RC method needs to help with ordering the method 
> candidates, so return either -1 or 1 to help the runtime choose the 
> right candidate

But what is "equal"? Is it the two resource infos pointing to
updateFruit when i know the target is that method? So return 0 in that
case?

And what's the algorithm when two different resource infos are passed,
that may or may not be updateFruit?

(I think there may be an easier way to write this interface - simply
list through the methods calling a method on an interface that returns
true or false - but that's a discussion for another day.)

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 29/07/13 13:39, John Baker wrote:
> Ok I'm stumped. Given my example:
>
>    @PUT
>    @Path("/{id}")
>    public void updateFruit(@QueryParam("id") String id, Banana banana);
>
>    @PUT
>    @Path("/{id}")
>    public void updateAnimal(@QueryParam("id") String id, Dog dog);
>
> what should I be testing in the compare(OperationalResourceInfo r1,
> OperationalResourceInfo r2) method? I see the OperationalResourceInfo
> methods have a getAnnotatedMethod property, and let's assume I know it
> should be calling updateAnimal (because of the Message body), what
> should I return for the various potential values?
>
The relevant RC method needs to help with ordering the method 
candidates, so return either -1 or 1 to help the runtime choose the 
right candidate

-- 
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 29/07/13 13:05, John Baker wrote:
> Defining in the XML Is fine. I'm just working out how the compare method
> for a method is supposed to work with my example...
>
What you can do is to do a check in your custom RC on the input stream 
available on the current message 
(message.getContent(InputStream.class)), determine the type, restore IS 
(via message.putContent) and also save the determined type as a message 
property, ex, message.put("custom.type", Banana.class);


Next your custom MessageBodyReader  will get this actual type from the 
injected MessageContext.get(...) and use it when calling on 
JAXBElementProvider

May be there is a cleaner way

Sergey



Re: JAX-RS method matching

Posted by John Baker <jb...@dryfish.org.uk>.
Defining in the XML Is fine. I'm just working out how the compare method
for a method is supposed to work with my example... 

-- 
John Baker

On Mon, Jul 29, 2013, at 01:02 PM, Sergey Beryozkin wrote:
> On 29/07/13 12:59, John Baker wrote:
> >
> >> Have you read the section :-) ?
> >
> > Yes but not well enough.
> >
> >> It says:
> >> "Starting from CXF 2.2.5 it is possible to register a custom
> >> ResourceComparator implementation using a
> >> jaxrs:server/resourceComparator element."
> >>
> >> Actually, it has to be "jaxrs:server/jaxrs:resourceComparator", will fix
> >> it
> >
> > Ah, you're referring to the Spring element. I think a short example that
> > looks like XML would be superb at that position :)
> >
> OK. I can do that.
> 
> re the possible annotation: probably not possible, RC can select both 
> classes & operations, the former function is mostly redundant with 
> JAX-RS 2.0, but it would be tricky to manage RCs declared at multiple 
> class or method levels
> 
> Sergey
> 
> > Thanks!
> >
> 
> 

Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
On 29/07/13 12:59, John Baker wrote:
>
>> Have you read the section :-) ?
>
> Yes but not well enough.
>
>> It says:
>> "Starting from CXF 2.2.5 it is possible to register a custom
>> ResourceComparator implementation using a
>> jaxrs:server/resourceComparator element."
>>
>> Actually, it has to be "jaxrs:server/jaxrs:resourceComparator", will fix
>> it
>
> Ah, you're referring to the Spring element. I think a short example that
> looks like XML would be superb at that position :)
>
OK. I can do that.

re the possible annotation: probably not possible, RC can select both 
classes & operations, the former function is mostly redundant with 
JAX-RS 2.0, but it would be tricky to manage RCs declared at multiple 
class or method levels

Sergey

> Thanks!
>



Re: JAX-RS method matching

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi
On 29/07/13 11:52, John Baker wrote:
> Hello
>
> I'm having trouble with CXF's JAX-RS implementation. The service is
> struggling to match the correct method defined on an interface. Please
> consider:
>
> @Path("/update")
> public interface MyService {
>
>    @PUT
>    @Path("/{id}")
>    public void updateFruit(@QueryParam("id") String id, Banana banana);
>
>    @PUT
>    @Path("/{id}")
>    public void updateAnimal(@QueryParam("id") String id, Dog dog);
> }
>
> where both Banana and Dog extend a common object. When making a call to
> updateFruit from a jax-rs client, it calls updateAnimal. I've traced the
> problem into JAXRSUtils and I'm wondering if there's any solution, or do
> these methods need unique paths?
>
JAX-RS selection algorithm sees these 2 methods as equal candidates.
IMHO having unique paths for different type of resources (from the 
client's POV, not from the Java hierarchy perspective) is reasonable 
IMHO, but if you prefer or *have to* use the same method signatures, then

use CXF ResourceComparator,
http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Customselectionbetweenmultipleresources

Another approach is to do Subresource locators:

@Path("/update")
  public interface MyService {


     @Path("/{id}")
     public void update(@PathParam("id") String id, @QueryParam("type") 
String type) {
        if ("banana".equals(type)) {
             return new Bananaresource(id);
        }
        ...
     }


  public class BananaResource {
     private String id;

     @PUT
     public void update(Banana b) {
     }

etc

Sergey

> Thanks
>
>
> John
>