You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Christian Balzer <ch...@gmail.com> on 2016/10/05 23:20:05 UTC

JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Hi all,

We are using cxf with Spring at work, and I have a newbie question...

This is my method signature:

@GET
@Path("foo")
public Response foo(@MatrixParam("l") List<String> myList) {

From that, I want to get a list back with the initial input String (to
my matrix parameter l) being split into list elements at any comma,
i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
b and c.

My converter below is registered (a breakpoint in it is triggered for
myList), but instead of passing rawType as List, I get rawType as
String (so it doesn’t do anything).

public class StringListHandler implements ParamConverterProvider {
    @Override
    public <T> ParamConverter<T> getConverter(final Class<T> rawType,
Type genericType, Annotation[] annotations) {
        if(rawType == List.class) {
            return new ParamConverter<T>() {
                @Override
                public T fromString(String value) {
                    return
rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
                }

                @Override
                public String toString(T value) {
                    return value.toString();
                }
            };
        }
        return null;
    }
}


Interestingly enough, I do get a list back. But instead of three
elements a, b and c, it only seems to have one: a,b,c

What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)

Is it maybe because cxf comes with a default implementation for a
List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
thought that c would overwrite a in that situation?)

Do I have to use a custom class with a List<String> property, like
class MyContainer {
  public List<List> l;
}

and change the method signature from above to
public Response foo(@MatrixParam("l") MyContainer myContainer) {

then check for MyContainer.class in ParamConverterProvider and change
the fromString() method to
public T fromString(String value) {
  MyContainer mC = new MyContainer();
  mc.l = Arrays.asList(value.split("\\s*,\\s*"));
  return rawType.cast(mc);
}

Or should I create a custom argument annotation, say @CommaSeparated,
and have ParamConverterProvider check for that (and
rawType==String.class)?
But if I do that, won't I get a List<List<String>> back?

Any help much appreciated!

Kind regards,

Christian

Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi Christian

ParamConverterProvider can be used to convert an individual collection 
value.
This is because the JAX-RS runtime has a long time support for List/Set 
collections of the input parameters - the individual parameter is 
created somehow but the collection container is created by the runtime

Sergey
On 06/10/16 00:20, Christian Balzer wrote:
> Hi all,
>
> We are using cxf with Spring at work, and I have a newbie question...
>
> This is my method signature:
>
> @GET
> @Path("foo")
> public Response foo(@MatrixParam("l") List<String> myList) {
>
> From that, I want to get a list back with the initial input String (to
> my matrix parameter l) being split into list elements at any comma,
> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
> b and c.
>
> My converter below is registered (a breakpoint in it is triggered for
> myList), but instead of passing rawType as List, I get rawType as
> String (so it doesn\u2019t do anything).
>
> public class StringListHandler implements ParamConverterProvider {
>     @Override
>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
> Type genericType, Annotation[] annotations) {
>         if(rawType == List.class) {
>             return new ParamConverter<T>() {
>                 @Override
>                 public T fromString(String value) {
>                     return
> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>                 }
>
>                 @Override
>                 public String toString(T value) {
>                     return value.toString();
>                 }
>             };
>         }
>         return null;
>     }
> }
>
>
> Interestingly enough, I do get a list back. But instead of three
> elements a, b and c, it only seems to have one: a,b,c
>
> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>
> Is it maybe because cxf comes with a default implementation for a
> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
> thought that c would overwrite a in that situation?)
>
> Do I have to use a custom class with a List<String> property, like
> class MyContainer {
>   public List<List> l;
> }
>
> and change the method signature from above to
> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>
> then check for MyContainer.class in ParamConverterProvider and change
> the fromString() method to
> public T fromString(String value) {
>   MyContainer mC = new MyContainer();
>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>   return rawType.cast(mc);
> }
>
> Or should I create a custom argument annotation, say @CommaSeparated,
> and have ParamConverterProvider check for that (and
> rawType==String.class)?
> But if I do that, won't I get a List<List<String>> back?
>
> Any help much appreciated!
>
> Kind regards,
>
> Christian
>


-- 
Sergey Beryozkin

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

Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Christian Balzer <ch...@gmail.com>.
No, I've been asked not to change the existing REST interface when
changing the service. No worries, though. :)

On Thu, Oct 6, 2016 at 4:00 PM, Sergey Beryozkin <sb...@gmail.com> wrote:
> May be you can do /a;from=10;till=90 or
> /a;range=10;range=90
>
> In the former case you'd have
>
> @MatrixParam("from") String
> @MatrixParam("till") String
>
> in the latter
>
> @MatrixParam("range") List<String>
>
> Sergey
>
> On 06/10/16 15:27, Christian Balzer wrote:
>>
>> I wanted to avoid having to do matrixParams.getList().get(0) when all
>> I wanted was matrixParams.get(0)... :)
>> That's the only reason, really...
>>
>> I thought if I have to convert partially in my class, I can just do
>> all of the conversion in my class, and not use cxf's facility at all.
>> And I thought it would work. I'm pretty sure I saw REST URLs in the
>> past that used ;range=10,90 for example - so I was sure cxf would
>> support it somehow... :)
>>
>> Cheers,
>>
>> Christian
>>
>> On Thu, Oct 6, 2016 at 2:58 PM, Sergey Beryozkin <sb...@gmail.com>
>> wrote:
>>>
>>> Hi, why would you not have
>>>
>>> class MatrixParams {
>>>     List<String> list;
>>> }
>>>
>>> @MatrixParam("a") MatrixParams
>>>
>>> Sergey
>>>
>>>
>>> On 06/10/16 14:23, Christian Balzer wrote:
>>>>
>>>>
>>>> Hello,
>>>>
>>>> Well, you just pointed out that the converters are only supposed to
>>>> work on an individual element of a collection, and cxf' code is
>>>> looking at the param type to see if it is dealing with a collection
>>>> target type (which it will create at runtime, as you pointed out), to
>>>> which it needs to add these elements. So it basically gets confused
>>>> because I've defined my own collection type. It assumes it needs to
>>>> create a collection to add elements to, but the elements I create are
>>>> my own collection. That's how I end up with
>>>> Collection<Collection<Enum>>. Short of cxf checking the actual type of
>>>> the target parameter instead of whether it implements the Collection
>>>> interface, I don't think there is any way around it?
>>>>
>>>> In other words, what I wanted to do won't work in cxf. :/
>>>>
>>>> Regards,
>>>>
>>>> Christian
>>>>
>>>> On Thu, Oct 6, 2016 at 1:41 PM, Sergey Beryozkin <sb...@gmail.com>
>>>> wrote:
>>>>>
>>>>>
>>>>> Hi
>>>>>
>>>>> Looks like a linking issue,
>>>>>
>>>>> Sergey
>>>>>
>>>>> On 06/10/16 13:24, Christian Balzer wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> Hi all,
>>>>>>
>>>>>> So, I played around a bit more, and I now have a semi-working
>>>>>> solution. Semi-working, because I do get a List of enums back, which
>>>>>> is what I was actually after.
>>>>>> Not fully working, because it now blows up in the bowels of cxf
>>>>>> (3.1.6).
>>>>>>
>>>>>> Here is the mehtod signature - as a reminder, I'm trying to call
>>>>>> /foo;d=2016-10-06;f=a,b,c - and the LocalDate conversion works just
>>>>>> fine...
>>>>>> @GET
>>>>>> @Path("foo")
>>>>>> public Response foo(@MatrixParam("d") LocalDate date,
>>>>>> @MatrixParam("f") Foo foos) {
>>>>>> //...
>>>>>>
>>>>>> Here is my ParamConverterProvider - it is registered and gets called
>>>>>> for matrix parameter f - yay!
>>>>>> public class StringListHandler implements ParamConverterProvider{
>>>>>>     @Override
>>>>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>>>>> Type genericType, Annotation[] annotations) {
>>>>>>         if(rawType == CommaSeparatedList.class) {
>>>>>>             return (ParamConverter<T>) new
>>>>>> ParamConverter<CommaSeparatedList>() {
>>>>>>                 @Override
>>>>>>                 public CommaSeparatedList fromString(String value) {
>>>>>>                     CommaSeparatedList list = new
>>>>>> CommaSeparatedList(value);
>>>>>>                     return list;
>>>>>>                 }
>>>>>>                 @Override
>>>>>>                 public String toString(CommaSeparatedList value) {
>>>>>>                     return value.toString();
>>>>>>                 }
>>>>>>             };
>>>>>>         }
>>>>>>         return null;
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>> Here is my target type. It extends ArrayList - and I think that is now
>>>>>> the problem...
>>>>>> public class CommaSeparatedList extends ArrayList<Foo> {
>>>>>>     public CommaSeparatedList(String value) {
>>>>>>         List<String> list = Arrays.asList(value.split("\\s*,\\s*"));
>>>>>>         List<Foo> foos = new ArrayList<>(list.size());
>>>>>>         for (String element : list) {
>>>>>>             foos.add(Foo.valueOf(element.toUpperCase()));
>>>>>>         }
>>>>>>         addAll(foos);
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>> Here is the enum I'm using:
>>>>>> public enum Foo {
>>>>>>     A, B, C
>>>>>> }
>>>>>>
>>>>>> And here are the Exception and StackTrace cxf now throws at me after I
>>>>>> return my CommaSeparatedList , i.e. ArrayList<Foo> (extending
>>>>>> Collection):
>>>>>> java.lang.IllegalArgumentException: argument type mismatch
>>>>>> argument type mismatch while invoking public javax.ws.rs.core.Response
>>>>>>
>>>>>>
>>>>>>
>>>>>> com.example.rs.MyEndpoint.foo(org.joda.time.LocalDate,com.example.common.CommaSeparatedList)
>>>>>> with params [2016-10-06, [[A, B, C]]].
>>>>>> 0 = {StackTraceElement@3424}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:166)"
>>>>>> 1 = {StackTraceElement@3425}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:140)"
>>>>>> 2 = {StackTraceElement@3426}
>>>>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)"
>>>>>> 3 = {StackTraceElement@3427}
>>>>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)"
>>>>>> 4 = {StackTraceElement@3428}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)"
>>>>>> 5 = {StackTraceElement@3429}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)"
>>>>>> 6 = {StackTraceElement@3430}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>>>>> 7 = {StackTraceElement@3431}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)"
>>>>>> 8 = {StackTraceElement@3432}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.transport.local.LocalConduit.dispatchDirect(LocalConduit.java:191)"
>>>>>> 9 = {StackTraceElement@3433}
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.transport.local.LocalConduit.close(LocalConduit.java:156)"
>>>>>> 10 = {StackTraceElement@3434}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)"
>>>>>> 11 = {StackTraceElement@3435}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>>>>> 12 = {StackTraceElement@3436}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:652)"
>>>>>> 13 = {StackTraceElement@3437}
>>>>>>
>>>>>>
>>>>>>
>>>>>> "org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1097)"
>>>>>> 14 = {StackTraceElement@3438}
>>>>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)"
>>>>>> 15 = {StackTraceElement@3439}
>>>>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)"
>>>>>> 16 = {StackTraceElement@3440}
>>>>>> "org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:331)"
>>>>>> 17 = {StackTraceElement@3441}
>>>>>> "org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:357)"
>>>>>>
>>>>>> I think the problem is in
>>>>>> AbstractInvoker.performInvocation(performInvocation.java:172) - mainly
>>>>>> because my debugger output at that position looks like this:
>>>>>> paramArray: Object[]  = {Object[2]@3320}
>>>>>>   0 = {LocalDate@3270} "2016-10-06"
>>>>>>   1 = {ArrayList@3295}  size = 1
>>>>>>     0 = {CommaSeparatedList@3294}  size = 3
>>>>>>
>>>>>> I think what I'd actually need - to conform with the method signature
>>>>>> of my foo() method in my endpoint from above - is something that looks
>>>>>> a bit more like this (i.e. without the extra ArrayList layer):
>>>>>> paramArray: Object[]  = {Object[2]}
>>>>>> 0 = {LocalDate}
>>>>>> 1 =  {CommaSeparatedLis}  size = 3
>>>>>>
>>>>>> Any idea how I can get that sorted, please?
>>>>>>
>>>>>> Again, the goal is to convert ;f=a,b,c into List<Foo>[3] with elements
>>>>>> A, B, and C...
>>>>>>
>>>>>> Kind regards,
>>>>>> Christian
>>>>>>
>>>>>> On Thu, Oct 6, 2016 at 12:20 AM, Christian Balzer
>>>>>> <ch...@gmail.com> wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Hi all,
>>>>>>>
>>>>>>> We are using cxf with Spring at work, and I have a newbie question...
>>>>>>>
>>>>>>> This is my method signature:
>>>>>>>
>>>>>>> @GET
>>>>>>> @Path("foo")
>>>>>>> public Response foo(@MatrixParam("l") List<String> myList) {
>>>>>>>
>>>>>>> From that, I want to get a list back with the initial input String
>>>>>>> (to
>>>>>>> my matrix parameter l) being split into list elements at any comma,
>>>>>>> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
>>>>>>> b and c.
>>>>>>>
>>>>>>> My converter below is registered (a breakpoint in it is triggered for
>>>>>>> myList), but instead of passing rawType as List, I get rawType as
>>>>>>> String (so it doesn’t do anything).
>>>>>>>
>>>>>>> public class StringListHandler implements ParamConverterProvider {
>>>>>>>     @Override
>>>>>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>>>>>> Type genericType, Annotation[] annotations) {
>>>>>>>         if(rawType == List.class) {
>>>>>>>             return new ParamConverter<T>() {
>>>>>>>                 @Override
>>>>>>>                 public T fromString(String value) {
>>>>>>>                     return
>>>>>>> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>>>>>>>                 }
>>>>>>>
>>>>>>>                 @Override
>>>>>>>                 public String toString(T value) {
>>>>>>>                     return value.toString();
>>>>>>>                 }
>>>>>>>             };
>>>>>>>         }
>>>>>>>         return null;
>>>>>>>     }
>>>>>>> }
>>>>>>>
>>>>>>>
>>>>>>> Interestingly enough, I do get a list back. But instead of three
>>>>>>> elements a, b and c, it only seems to have one: a,b,c
>>>>>>>
>>>>>>> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>>>>>>>
>>>>>>> Is it maybe because cxf comes with a default implementation for a
>>>>>>> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
>>>>>>> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
>>>>>>> thought that c would overwrite a in that situation?)
>>>>>>>
>>>>>>> Do I have to use a custom class with a List<String> property, like
>>>>>>> class MyContainer {
>>>>>>>   public List<List> l;
>>>>>>> }
>>>>>>>
>>>>>>> and change the method signature from above to
>>>>>>> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>>>>>>>
>>>>>>> then check for MyContainer.class in ParamConverterProvider and change
>>>>>>> the fromString() method to
>>>>>>> public T fromString(String value) {
>>>>>>>   MyContainer mC = new MyContainer();
>>>>>>>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>>>>>>>   return rawType.cast(mc);
>>>>>>> }
>>>>>>>
>>>>>>> Or should I create a custom argument annotation, say @CommaSeparated,
>>>>>>> and have ParamConverterProvider check for that (and
>>>>>>> rawType==String.class)?
>>>>>>> But if I do that, won't I get a List<List<String>> back?
>>>>>>>
>>>>>>> Any help much appreciated!
>>>>>>>
>>>>>>> Kind regards,
>>>>>>>
>>>>>>> Christian
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Sergey Beryozkin
>>>>>
>>>>> Talend Community Coders
>>>>> http://coders.talend.com/
>>>
>>>
>>>
>
>
> --
> Sergey Beryozkin
>
> Talend Community Coders
> http://coders.talend.com/

Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Sergey Beryozkin <sb...@gmail.com>.
May be you can do /a;from=10;till=90 or
/a;range=10;range=90

In the former case you'd have

@MatrixParam("from") String
@MatrixParam("till") String

in the latter

@MatrixParam("range") List<String>

Sergey
On 06/10/16 15:27, Christian Balzer wrote:
> I wanted to avoid having to do matrixParams.getList().get(0) when all
> I wanted was matrixParams.get(0)... :)
> That's the only reason, really...
>
> I thought if I have to convert partially in my class, I can just do
> all of the conversion in my class, and not use cxf's facility at all.
> And I thought it would work. I'm pretty sure I saw REST URLs in the
> past that used ;range=10,90 for example - so I was sure cxf would
> support it somehow... :)
>
> Cheers,
>
> Christian
>
> On Thu, Oct 6, 2016 at 2:58 PM, Sergey Beryozkin <sb...@gmail.com> wrote:
>> Hi, why would you not have
>>
>> class MatrixParams {
>>     List<String> list;
>> }
>>
>> @MatrixParam("a") MatrixParams
>>
>> Sergey
>>
>>
>> On 06/10/16 14:23, Christian Balzer wrote:
>>>
>>> Hello,
>>>
>>> Well, you just pointed out that the converters are only supposed to
>>> work on an individual element of a collection, and cxf' code is
>>> looking at the param type to see if it is dealing with a collection
>>> target type (which it will create at runtime, as you pointed out), to
>>> which it needs to add these elements. So it basically gets confused
>>> because I've defined my own collection type. It assumes it needs to
>>> create a collection to add elements to, but the elements I create are
>>> my own collection. That's how I end up with
>>> Collection<Collection<Enum>>. Short of cxf checking the actual type of
>>> the target parameter instead of whether it implements the Collection
>>> interface, I don't think there is any way around it?
>>>
>>> In other words, what I wanted to do won't work in cxf. :/
>>>
>>> Regards,
>>>
>>> Christian
>>>
>>> On Thu, Oct 6, 2016 at 1:41 PM, Sergey Beryozkin <sb...@gmail.com>
>>> wrote:
>>>>
>>>> Hi
>>>>
>>>> Looks like a linking issue,
>>>>
>>>> Sergey
>>>>
>>>> On 06/10/16 13:24, Christian Balzer wrote:
>>>>>
>>>>>
>>>>> Hi all,
>>>>>
>>>>> So, I played around a bit more, and I now have a semi-working
>>>>> solution. Semi-working, because I do get a List of enums back, which
>>>>> is what I was actually after.
>>>>> Not fully working, because it now blows up in the bowels of cxf (3.1.6).
>>>>>
>>>>> Here is the mehtod signature - as a reminder, I'm trying to call
>>>>> /foo;d=2016-10-06;f=a,b,c - and the LocalDate conversion works just
>>>>> fine...
>>>>> @GET
>>>>> @Path("foo")
>>>>> public Response foo(@MatrixParam("d") LocalDate date,
>>>>> @MatrixParam("f") Foo foos) {
>>>>> //...
>>>>>
>>>>> Here is my ParamConverterProvider - it is registered and gets called
>>>>> for matrix parameter f - yay!
>>>>> public class StringListHandler implements ParamConverterProvider{
>>>>>     @Override
>>>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>>>> Type genericType, Annotation[] annotations) {
>>>>>         if(rawType == CommaSeparatedList.class) {
>>>>>             return (ParamConverter<T>) new
>>>>> ParamConverter<CommaSeparatedList>() {
>>>>>                 @Override
>>>>>                 public CommaSeparatedList fromString(String value) {
>>>>>                     CommaSeparatedList list = new
>>>>> CommaSeparatedList(value);
>>>>>                     return list;
>>>>>                 }
>>>>>                 @Override
>>>>>                 public String toString(CommaSeparatedList value) {
>>>>>                     return value.toString();
>>>>>                 }
>>>>>             };
>>>>>         }
>>>>>         return null;
>>>>>     }
>>>>> }
>>>>>
>>>>> Here is my target type. It extends ArrayList - and I think that is now
>>>>> the problem...
>>>>> public class CommaSeparatedList extends ArrayList<Foo> {
>>>>>     public CommaSeparatedList(String value) {
>>>>>         List<String> list = Arrays.asList(value.split("\\s*,\\s*"));
>>>>>         List<Foo> foos = new ArrayList<>(list.size());
>>>>>         for (String element : list) {
>>>>>             foos.add(Foo.valueOf(element.toUpperCase()));
>>>>>         }
>>>>>         addAll(foos);
>>>>>     }
>>>>> }
>>>>>
>>>>> Here is the enum I'm using:
>>>>> public enum Foo {
>>>>>     A, B, C
>>>>> }
>>>>>
>>>>> And here are the Exception and StackTrace cxf now throws at me after I
>>>>> return my CommaSeparatedList , i.e. ArrayList<Foo> (extending
>>>>> Collection):
>>>>> java.lang.IllegalArgumentException: argument type mismatch
>>>>> argument type mismatch while invoking public javax.ws.rs.core.Response
>>>>>
>>>>>
>>>>> com.example.rs.MyEndpoint.foo(org.joda.time.LocalDate,com.example.common.CommaSeparatedList)
>>>>> with params [2016-10-06, [[A, B, C]]].
>>>>> 0 = {StackTraceElement@3424}
>>>>>
>>>>>
>>>>> "org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:166)"
>>>>> 1 = {StackTraceElement@3425}
>>>>>
>>>>>
>>>>> "org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:140)"
>>>>> 2 = {StackTraceElement@3426}
>>>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)"
>>>>> 3 = {StackTraceElement@3427}
>>>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)"
>>>>> 4 = {StackTraceElement@3428}
>>>>>
>>>>>
>>>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)"
>>>>> 5 = {StackTraceElement@3429}
>>>>>
>>>>>
>>>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)"
>>>>> 6 = {StackTraceElement@3430}
>>>>>
>>>>>
>>>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>>>> 7 = {StackTraceElement@3431}
>>>>>
>>>>>
>>>>> "org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)"
>>>>> 8 = {StackTraceElement@3432}
>>>>>
>>>>>
>>>>> "org.apache.cxf.transport.local.LocalConduit.dispatchDirect(LocalConduit.java:191)"
>>>>> 9 = {StackTraceElement@3433}
>>>>>
>>>>> "org.apache.cxf.transport.local.LocalConduit.close(LocalConduit.java:156)"
>>>>> 10 = {StackTraceElement@3434}
>>>>>
>>>>>
>>>>> "org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)"
>>>>> 11 = {StackTraceElement@3435}
>>>>>
>>>>>
>>>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>>>> 12 = {StackTraceElement@3436}
>>>>>
>>>>>
>>>>> "org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:652)"
>>>>> 13 = {StackTraceElement@3437}
>>>>>
>>>>>
>>>>> "org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1097)"
>>>>> 14 = {StackTraceElement@3438}
>>>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)"
>>>>> 15 = {StackTraceElement@3439}
>>>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)"
>>>>> 16 = {StackTraceElement@3440}
>>>>> "org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:331)"
>>>>> 17 = {StackTraceElement@3441}
>>>>> "org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:357)"
>>>>>
>>>>> I think the problem is in
>>>>> AbstractInvoker.performInvocation(performInvocation.java:172) - mainly
>>>>> because my debugger output at that position looks like this:
>>>>> paramArray: Object[]  = {Object[2]@3320}
>>>>>   0 = {LocalDate@3270} "2016-10-06"
>>>>>   1 = {ArrayList@3295}  size = 1
>>>>>     0 = {CommaSeparatedList@3294}  size = 3
>>>>>
>>>>> I think what I'd actually need - to conform with the method signature
>>>>> of my foo() method in my endpoint from above - is something that looks
>>>>> a bit more like this (i.e. without the extra ArrayList layer):
>>>>> paramArray: Object[]  = {Object[2]}
>>>>> 0 = {LocalDate}
>>>>> 1 =  {CommaSeparatedLis}  size = 3
>>>>>
>>>>> Any idea how I can get that sorted, please?
>>>>>
>>>>> Again, the goal is to convert ;f=a,b,c into List<Foo>[3] with elements
>>>>> A, B, and C...
>>>>>
>>>>> Kind regards,
>>>>> Christian
>>>>>
>>>>> On Thu, Oct 6, 2016 at 12:20 AM, Christian Balzer
>>>>> <ch...@gmail.com> wrote:
>>>>>>
>>>>>>
>>>>>> Hi all,
>>>>>>
>>>>>> We are using cxf with Spring at work, and I have a newbie question...
>>>>>>
>>>>>> This is my method signature:
>>>>>>
>>>>>> @GET
>>>>>> @Path("foo")
>>>>>> public Response foo(@MatrixParam("l") List<String> myList) {
>>>>>>
>>>>>> From that, I want to get a list back with the initial input String (to
>>>>>> my matrix parameter l) being split into list elements at any comma,
>>>>>> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
>>>>>> b and c.
>>>>>>
>>>>>> My converter below is registered (a breakpoint in it is triggered for
>>>>>> myList), but instead of passing rawType as List, I get rawType as
>>>>>> String (so it doesn\u2019t do anything).
>>>>>>
>>>>>> public class StringListHandler implements ParamConverterProvider {
>>>>>>     @Override
>>>>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>>>>> Type genericType, Annotation[] annotations) {
>>>>>>         if(rawType == List.class) {
>>>>>>             return new ParamConverter<T>() {
>>>>>>                 @Override
>>>>>>                 public T fromString(String value) {
>>>>>>                     return
>>>>>> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>>>>>>                 }
>>>>>>
>>>>>>                 @Override
>>>>>>                 public String toString(T value) {
>>>>>>                     return value.toString();
>>>>>>                 }
>>>>>>             };
>>>>>>         }
>>>>>>         return null;
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>>
>>>>>> Interestingly enough, I do get a list back. But instead of three
>>>>>> elements a, b and c, it only seems to have one: a,b,c
>>>>>>
>>>>>> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>>>>>>
>>>>>> Is it maybe because cxf comes with a default implementation for a
>>>>>> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
>>>>>> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
>>>>>> thought that c would overwrite a in that situation?)
>>>>>>
>>>>>> Do I have to use a custom class with a List<String> property, like
>>>>>> class MyContainer {
>>>>>>   public List<List> l;
>>>>>> }
>>>>>>
>>>>>> and change the method signature from above to
>>>>>> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>>>>>>
>>>>>> then check for MyContainer.class in ParamConverterProvider and change
>>>>>> the fromString() method to
>>>>>> public T fromString(String value) {
>>>>>>   MyContainer mC = new MyContainer();
>>>>>>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>>>>>>   return rawType.cast(mc);
>>>>>> }
>>>>>>
>>>>>> Or should I create a custom argument annotation, say @CommaSeparated,
>>>>>> and have ParamConverterProvider check for that (and
>>>>>> rawType==String.class)?
>>>>>> But if I do that, won't I get a List<List<String>> back?
>>>>>>
>>>>>> Any help much appreciated!
>>>>>>
>>>>>> Kind regards,
>>>>>>
>>>>>> Christian
>>>>
>>>>
>>>>
>>>>
>>>> --
>>>> Sergey Beryozkin
>>>>
>>>> Talend Community Coders
>>>> http://coders.talend.com/
>>
>>


-- 
Sergey Beryozkin

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

Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Christian Balzer <ch...@gmail.com>.
I wanted to avoid having to do matrixParams.getList().get(0) when all
I wanted was matrixParams.get(0)... :)
That's the only reason, really...

I thought if I have to convert partially in my class, I can just do
all of the conversion in my class, and not use cxf's facility at all.
And I thought it would work. I'm pretty sure I saw REST URLs in the
past that used ;range=10,90 for example - so I was sure cxf would
support it somehow... :)

Cheers,

Christian

On Thu, Oct 6, 2016 at 2:58 PM, Sergey Beryozkin <sb...@gmail.com> wrote:
> Hi, why would you not have
>
> class MatrixParams {
>     List<String> list;
> }
>
> @MatrixParam("a") MatrixParams
>
> Sergey
>
>
> On 06/10/16 14:23, Christian Balzer wrote:
>>
>> Hello,
>>
>> Well, you just pointed out that the converters are only supposed to
>> work on an individual element of a collection, and cxf' code is
>> looking at the param type to see if it is dealing with a collection
>> target type (which it will create at runtime, as you pointed out), to
>> which it needs to add these elements. So it basically gets confused
>> because I've defined my own collection type. It assumes it needs to
>> create a collection to add elements to, but the elements I create are
>> my own collection. That's how I end up with
>> Collection<Collection<Enum>>. Short of cxf checking the actual type of
>> the target parameter instead of whether it implements the Collection
>> interface, I don't think there is any way around it?
>>
>> In other words, what I wanted to do won't work in cxf. :/
>>
>> Regards,
>>
>> Christian
>>
>> On Thu, Oct 6, 2016 at 1:41 PM, Sergey Beryozkin <sb...@gmail.com>
>> wrote:
>>>
>>> Hi
>>>
>>> Looks like a linking issue,
>>>
>>> Sergey
>>>
>>> On 06/10/16 13:24, Christian Balzer wrote:
>>>>
>>>>
>>>> Hi all,
>>>>
>>>> So, I played around a bit more, and I now have a semi-working
>>>> solution. Semi-working, because I do get a List of enums back, which
>>>> is what I was actually after.
>>>> Not fully working, because it now blows up in the bowels of cxf (3.1.6).
>>>>
>>>> Here is the mehtod signature - as a reminder, I'm trying to call
>>>> /foo;d=2016-10-06;f=a,b,c - and the LocalDate conversion works just
>>>> fine...
>>>> @GET
>>>> @Path("foo")
>>>> public Response foo(@MatrixParam("d") LocalDate date,
>>>> @MatrixParam("f") Foo foos) {
>>>> //...
>>>>
>>>> Here is my ParamConverterProvider - it is registered and gets called
>>>> for matrix parameter f - yay!
>>>> public class StringListHandler implements ParamConverterProvider{
>>>>     @Override
>>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>>> Type genericType, Annotation[] annotations) {
>>>>         if(rawType == CommaSeparatedList.class) {
>>>>             return (ParamConverter<T>) new
>>>> ParamConverter<CommaSeparatedList>() {
>>>>                 @Override
>>>>                 public CommaSeparatedList fromString(String value) {
>>>>                     CommaSeparatedList list = new
>>>> CommaSeparatedList(value);
>>>>                     return list;
>>>>                 }
>>>>                 @Override
>>>>                 public String toString(CommaSeparatedList value) {
>>>>                     return value.toString();
>>>>                 }
>>>>             };
>>>>         }
>>>>         return null;
>>>>     }
>>>> }
>>>>
>>>> Here is my target type. It extends ArrayList - and I think that is now
>>>> the problem...
>>>> public class CommaSeparatedList extends ArrayList<Foo> {
>>>>     public CommaSeparatedList(String value) {
>>>>         List<String> list = Arrays.asList(value.split("\\s*,\\s*"));
>>>>         List<Foo> foos = new ArrayList<>(list.size());
>>>>         for (String element : list) {
>>>>             foos.add(Foo.valueOf(element.toUpperCase()));
>>>>         }
>>>>         addAll(foos);
>>>>     }
>>>> }
>>>>
>>>> Here is the enum I'm using:
>>>> public enum Foo {
>>>>     A, B, C
>>>> }
>>>>
>>>> And here are the Exception and StackTrace cxf now throws at me after I
>>>> return my CommaSeparatedList , i.e. ArrayList<Foo> (extending
>>>> Collection):
>>>> java.lang.IllegalArgumentException: argument type mismatch
>>>> argument type mismatch while invoking public javax.ws.rs.core.Response
>>>>
>>>>
>>>> com.example.rs.MyEndpoint.foo(org.joda.time.LocalDate,com.example.common.CommaSeparatedList)
>>>> with params [2016-10-06, [[A, B, C]]].
>>>> 0 = {StackTraceElement@3424}
>>>>
>>>>
>>>> "org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:166)"
>>>> 1 = {StackTraceElement@3425}
>>>>
>>>>
>>>> "org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:140)"
>>>> 2 = {StackTraceElement@3426}
>>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)"
>>>> 3 = {StackTraceElement@3427}
>>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)"
>>>> 4 = {StackTraceElement@3428}
>>>>
>>>>
>>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)"
>>>> 5 = {StackTraceElement@3429}
>>>>
>>>>
>>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)"
>>>> 6 = {StackTraceElement@3430}
>>>>
>>>>
>>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>>> 7 = {StackTraceElement@3431}
>>>>
>>>>
>>>> "org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)"
>>>> 8 = {StackTraceElement@3432}
>>>>
>>>>
>>>> "org.apache.cxf.transport.local.LocalConduit.dispatchDirect(LocalConduit.java:191)"
>>>> 9 = {StackTraceElement@3433}
>>>>
>>>> "org.apache.cxf.transport.local.LocalConduit.close(LocalConduit.java:156)"
>>>> 10 = {StackTraceElement@3434}
>>>>
>>>>
>>>> "org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)"
>>>> 11 = {StackTraceElement@3435}
>>>>
>>>>
>>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>>> 12 = {StackTraceElement@3436}
>>>>
>>>>
>>>> "org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:652)"
>>>> 13 = {StackTraceElement@3437}
>>>>
>>>>
>>>> "org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1097)"
>>>> 14 = {StackTraceElement@3438}
>>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)"
>>>> 15 = {StackTraceElement@3439}
>>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)"
>>>> 16 = {StackTraceElement@3440}
>>>> "org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:331)"
>>>> 17 = {StackTraceElement@3441}
>>>> "org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:357)"
>>>>
>>>> I think the problem is in
>>>> AbstractInvoker.performInvocation(performInvocation.java:172) - mainly
>>>> because my debugger output at that position looks like this:
>>>> paramArray: Object[]  = {Object[2]@3320}
>>>>   0 = {LocalDate@3270} "2016-10-06"
>>>>   1 = {ArrayList@3295}  size = 1
>>>>     0 = {CommaSeparatedList@3294}  size = 3
>>>>
>>>> I think what I'd actually need - to conform with the method signature
>>>> of my foo() method in my endpoint from above - is something that looks
>>>> a bit more like this (i.e. without the extra ArrayList layer):
>>>> paramArray: Object[]  = {Object[2]}
>>>> 0 = {LocalDate}
>>>> 1 =  {CommaSeparatedLis}  size = 3
>>>>
>>>> Any idea how I can get that sorted, please?
>>>>
>>>> Again, the goal is to convert ;f=a,b,c into List<Foo>[3] with elements
>>>> A, B, and C...
>>>>
>>>> Kind regards,
>>>> Christian
>>>>
>>>> On Thu, Oct 6, 2016 at 12:20 AM, Christian Balzer
>>>> <ch...@gmail.com> wrote:
>>>>>
>>>>>
>>>>> Hi all,
>>>>>
>>>>> We are using cxf with Spring at work, and I have a newbie question...
>>>>>
>>>>> This is my method signature:
>>>>>
>>>>> @GET
>>>>> @Path("foo")
>>>>> public Response foo(@MatrixParam("l") List<String> myList) {
>>>>>
>>>>> From that, I want to get a list back with the initial input String (to
>>>>> my matrix parameter l) being split into list elements at any comma,
>>>>> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
>>>>> b and c.
>>>>>
>>>>> My converter below is registered (a breakpoint in it is triggered for
>>>>> myList), but instead of passing rawType as List, I get rawType as
>>>>> String (so it doesn’t do anything).
>>>>>
>>>>> public class StringListHandler implements ParamConverterProvider {
>>>>>     @Override
>>>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>>>> Type genericType, Annotation[] annotations) {
>>>>>         if(rawType == List.class) {
>>>>>             return new ParamConverter<T>() {
>>>>>                 @Override
>>>>>                 public T fromString(String value) {
>>>>>                     return
>>>>> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>>>>>                 }
>>>>>
>>>>>                 @Override
>>>>>                 public String toString(T value) {
>>>>>                     return value.toString();
>>>>>                 }
>>>>>             };
>>>>>         }
>>>>>         return null;
>>>>>     }
>>>>> }
>>>>>
>>>>>
>>>>> Interestingly enough, I do get a list back. But instead of three
>>>>> elements a, b and c, it only seems to have one: a,b,c
>>>>>
>>>>> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>>>>>
>>>>> Is it maybe because cxf comes with a default implementation for a
>>>>> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
>>>>> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
>>>>> thought that c would overwrite a in that situation?)
>>>>>
>>>>> Do I have to use a custom class with a List<String> property, like
>>>>> class MyContainer {
>>>>>   public List<List> l;
>>>>> }
>>>>>
>>>>> and change the method signature from above to
>>>>> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>>>>>
>>>>> then check for MyContainer.class in ParamConverterProvider and change
>>>>> the fromString() method to
>>>>> public T fromString(String value) {
>>>>>   MyContainer mC = new MyContainer();
>>>>>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>>>>>   return rawType.cast(mc);
>>>>> }
>>>>>
>>>>> Or should I create a custom argument annotation, say @CommaSeparated,
>>>>> and have ParamConverterProvider check for that (and
>>>>> rawType==String.class)?
>>>>> But if I do that, won't I get a List<List<String>> back?
>>>>>
>>>>> Any help much appreciated!
>>>>>
>>>>> Kind regards,
>>>>>
>>>>> Christian
>>>
>>>
>>>
>>>
>>> --
>>> Sergey Beryozkin
>>>
>>> Talend Community Coders
>>> http://coders.talend.com/
>
>

Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi, why would you not have

class MatrixParams {
     List<String> list;
}

@MatrixParam("a") MatrixParams

Sergey

On 06/10/16 14:23, Christian Balzer wrote:
> Hello,
>
> Well, you just pointed out that the converters are only supposed to
> work on an individual element of a collection, and cxf' code is
> looking at the param type to see if it is dealing with a collection
> target type (which it will create at runtime, as you pointed out), to
> which it needs to add these elements. So it basically gets confused
> because I've defined my own collection type. It assumes it needs to
> create a collection to add elements to, but the elements I create are
> my own collection. That's how I end up with
> Collection<Collection<Enum>>. Short of cxf checking the actual type of
> the target parameter instead of whether it implements the Collection
> interface, I don't think there is any way around it?
>
> In other words, what I wanted to do won't work in cxf. :/
>
> Regards,
>
> Christian
>
> On Thu, Oct 6, 2016 at 1:41 PM, Sergey Beryozkin <sb...@gmail.com> wrote:
>> Hi
>>
>> Looks like a linking issue,
>>
>> Sergey
>>
>> On 06/10/16 13:24, Christian Balzer wrote:
>>>
>>> Hi all,
>>>
>>> So, I played around a bit more, and I now have a semi-working
>>> solution. Semi-working, because I do get a List of enums back, which
>>> is what I was actually after.
>>> Not fully working, because it now blows up in the bowels of cxf (3.1.6).
>>>
>>> Here is the mehtod signature - as a reminder, I'm trying to call
>>> /foo;d=2016-10-06;f=a,b,c - and the LocalDate conversion works just
>>> fine...
>>> @GET
>>> @Path("foo")
>>> public Response foo(@MatrixParam("d") LocalDate date,
>>> @MatrixParam("f") Foo foos) {
>>> //...
>>>
>>> Here is my ParamConverterProvider - it is registered and gets called
>>> for matrix parameter f - yay!
>>> public class StringListHandler implements ParamConverterProvider{
>>>     @Override
>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>> Type genericType, Annotation[] annotations) {
>>>         if(rawType == CommaSeparatedList.class) {
>>>             return (ParamConverter<T>) new
>>> ParamConverter<CommaSeparatedList>() {
>>>                 @Override
>>>                 public CommaSeparatedList fromString(String value) {
>>>                     CommaSeparatedList list = new
>>> CommaSeparatedList(value);
>>>                     return list;
>>>                 }
>>>                 @Override
>>>                 public String toString(CommaSeparatedList value) {
>>>                     return value.toString();
>>>                 }
>>>             };
>>>         }
>>>         return null;
>>>     }
>>> }
>>>
>>> Here is my target type. It extends ArrayList - and I think that is now
>>> the problem...
>>> public class CommaSeparatedList extends ArrayList<Foo> {
>>>     public CommaSeparatedList(String value) {
>>>         List<String> list = Arrays.asList(value.split("\\s*,\\s*"));
>>>         List<Foo> foos = new ArrayList<>(list.size());
>>>         for (String element : list) {
>>>             foos.add(Foo.valueOf(element.toUpperCase()));
>>>         }
>>>         addAll(foos);
>>>     }
>>> }
>>>
>>> Here is the enum I'm using:
>>> public enum Foo {
>>>     A, B, C
>>> }
>>>
>>> And here are the Exception and StackTrace cxf now throws at me after I
>>> return my CommaSeparatedList , i.e. ArrayList<Foo> (extending
>>> Collection):
>>> java.lang.IllegalArgumentException: argument type mismatch
>>> argument type mismatch while invoking public javax.ws.rs.core.Response
>>>
>>> com.example.rs.MyEndpoint.foo(org.joda.time.LocalDate,com.example.common.CommaSeparatedList)
>>> with params [2016-10-06, [[A, B, C]]].
>>> 0 = {StackTraceElement@3424}
>>>
>>> "org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:166)"
>>> 1 = {StackTraceElement@3425}
>>>
>>> "org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:140)"
>>> 2 = {StackTraceElement@3426}
>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)"
>>> 3 = {StackTraceElement@3427}
>>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)"
>>> 4 = {StackTraceElement@3428}
>>>
>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)"
>>> 5 = {StackTraceElement@3429}
>>>
>>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)"
>>> 6 = {StackTraceElement@3430}
>>>
>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>> 7 = {StackTraceElement@3431}
>>>
>>> "org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)"
>>> 8 = {StackTraceElement@3432}
>>>
>>> "org.apache.cxf.transport.local.LocalConduit.dispatchDirect(LocalConduit.java:191)"
>>> 9 = {StackTraceElement@3433}
>>> "org.apache.cxf.transport.local.LocalConduit.close(LocalConduit.java:156)"
>>> 10 = {StackTraceElement@3434}
>>>
>>> "org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)"
>>> 11 = {StackTraceElement@3435}
>>>
>>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>>> 12 = {StackTraceElement@3436}
>>>
>>> "org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:652)"
>>> 13 = {StackTraceElement@3437}
>>>
>>> "org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1097)"
>>> 14 = {StackTraceElement@3438}
>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)"
>>> 15 = {StackTraceElement@3439}
>>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)"
>>> 16 = {StackTraceElement@3440}
>>> "org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:331)"
>>> 17 = {StackTraceElement@3441}
>>> "org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:357)"
>>>
>>> I think the problem is in
>>> AbstractInvoker.performInvocation(performInvocation.java:172) - mainly
>>> because my debugger output at that position looks like this:
>>> paramArray: Object[]  = {Object[2]@3320}
>>>   0 = {LocalDate@3270} "2016-10-06"
>>>   1 = {ArrayList@3295}  size = 1
>>>     0 = {CommaSeparatedList@3294}  size = 3
>>>
>>> I think what I'd actually need - to conform with the method signature
>>> of my foo() method in my endpoint from above - is something that looks
>>> a bit more like this (i.e. without the extra ArrayList layer):
>>> paramArray: Object[]  = {Object[2]}
>>> 0 = {LocalDate}
>>> 1 =  {CommaSeparatedLis}  size = 3
>>>
>>> Any idea how I can get that sorted, please?
>>>
>>> Again, the goal is to convert ;f=a,b,c into List<Foo>[3] with elements
>>> A, B, and C...
>>>
>>> Kind regards,
>>> Christian
>>>
>>> On Thu, Oct 6, 2016 at 12:20 AM, Christian Balzer
>>> <ch...@gmail.com> wrote:
>>>>
>>>> Hi all,
>>>>
>>>> We are using cxf with Spring at work, and I have a newbie question...
>>>>
>>>> This is my method signature:
>>>>
>>>> @GET
>>>> @Path("foo")
>>>> public Response foo(@MatrixParam("l") List<String> myList) {
>>>>
>>>> From that, I want to get a list back with the initial input String (to
>>>> my matrix parameter l) being split into list elements at any comma,
>>>> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
>>>> b and c.
>>>>
>>>> My converter below is registered (a breakpoint in it is triggered for
>>>> myList), but instead of passing rawType as List, I get rawType as
>>>> String (so it doesn\u2019t do anything).
>>>>
>>>> public class StringListHandler implements ParamConverterProvider {
>>>>     @Override
>>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>>> Type genericType, Annotation[] annotations) {
>>>>         if(rawType == List.class) {
>>>>             return new ParamConverter<T>() {
>>>>                 @Override
>>>>                 public T fromString(String value) {
>>>>                     return
>>>> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>>>>                 }
>>>>
>>>>                 @Override
>>>>                 public String toString(T value) {
>>>>                     return value.toString();
>>>>                 }
>>>>             };
>>>>         }
>>>>         return null;
>>>>     }
>>>> }
>>>>
>>>>
>>>> Interestingly enough, I do get a list back. But instead of three
>>>> elements a, b and c, it only seems to have one: a,b,c
>>>>
>>>> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>>>>
>>>> Is it maybe because cxf comes with a default implementation for a
>>>> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
>>>> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
>>>> thought that c would overwrite a in that situation?)
>>>>
>>>> Do I have to use a custom class with a List<String> property, like
>>>> class MyContainer {
>>>>   public List<List> l;
>>>> }
>>>>
>>>> and change the method signature from above to
>>>> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>>>>
>>>> then check for MyContainer.class in ParamConverterProvider and change
>>>> the fromString() method to
>>>> public T fromString(String value) {
>>>>   MyContainer mC = new MyContainer();
>>>>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>>>>   return rawType.cast(mc);
>>>> }
>>>>
>>>> Or should I create a custom argument annotation, say @CommaSeparated,
>>>> and have ParamConverterProvider check for that (and
>>>> rawType==String.class)?
>>>> But if I do that, won't I get a List<List<String>> back?
>>>>
>>>> Any help much appreciated!
>>>>
>>>> Kind regards,
>>>>
>>>> Christian
>>
>>
>>
>> --
>> Sergey Beryozkin
>>
>> Talend Community Coders
>> http://coders.talend.com/


Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Christian Balzer <ch...@gmail.com>.
Hello,

Well, you just pointed out that the converters are only supposed to
work on an individual element of a collection, and cxf' code is
looking at the param type to see if it is dealing with a collection
target type (which it will create at runtime, as you pointed out), to
which it needs to add these elements. So it basically gets confused
because I've defined my own collection type. It assumes it needs to
create a collection to add elements to, but the elements I create are
my own collection. That's how I end up with
Collection<Collection<Enum>>. Short of cxf checking the actual type of
the target parameter instead of whether it implements the Collection
interface, I don't think there is any way around it?

In other words, what I wanted to do won't work in cxf. :/

Regards,

Christian

On Thu, Oct 6, 2016 at 1:41 PM, Sergey Beryozkin <sb...@gmail.com> wrote:
> Hi
>
> Looks like a linking issue,
>
> Sergey
>
> On 06/10/16 13:24, Christian Balzer wrote:
>>
>> Hi all,
>>
>> So, I played around a bit more, and I now have a semi-working
>> solution. Semi-working, because I do get a List of enums back, which
>> is what I was actually after.
>> Not fully working, because it now blows up in the bowels of cxf (3.1.6).
>>
>> Here is the mehtod signature - as a reminder, I'm trying to call
>> /foo;d=2016-10-06;f=a,b,c - and the LocalDate conversion works just
>> fine...
>> @GET
>> @Path("foo")
>> public Response foo(@MatrixParam("d") LocalDate date,
>> @MatrixParam("f") Foo foos) {
>> //...
>>
>> Here is my ParamConverterProvider - it is registered and gets called
>> for matrix parameter f - yay!
>> public class StringListHandler implements ParamConverterProvider{
>>     @Override
>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>> Type genericType, Annotation[] annotations) {
>>         if(rawType == CommaSeparatedList.class) {
>>             return (ParamConverter<T>) new
>> ParamConverter<CommaSeparatedList>() {
>>                 @Override
>>                 public CommaSeparatedList fromString(String value) {
>>                     CommaSeparatedList list = new
>> CommaSeparatedList(value);
>>                     return list;
>>                 }
>>                 @Override
>>                 public String toString(CommaSeparatedList value) {
>>                     return value.toString();
>>                 }
>>             };
>>         }
>>         return null;
>>     }
>> }
>>
>> Here is my target type. It extends ArrayList - and I think that is now
>> the problem...
>> public class CommaSeparatedList extends ArrayList<Foo> {
>>     public CommaSeparatedList(String value) {
>>         List<String> list = Arrays.asList(value.split("\\s*,\\s*"));
>>         List<Foo> foos = new ArrayList<>(list.size());
>>         for (String element : list) {
>>             foos.add(Foo.valueOf(element.toUpperCase()));
>>         }
>>         addAll(foos);
>>     }
>> }
>>
>> Here is the enum I'm using:
>> public enum Foo {
>>     A, B, C
>> }
>>
>> And here are the Exception and StackTrace cxf now throws at me after I
>> return my CommaSeparatedList , i.e. ArrayList<Foo> (extending
>> Collection):
>> java.lang.IllegalArgumentException: argument type mismatch
>> argument type mismatch while invoking public javax.ws.rs.core.Response
>>
>> com.example.rs.MyEndpoint.foo(org.joda.time.LocalDate,com.example.common.CommaSeparatedList)
>> with params [2016-10-06, [[A, B, C]]].
>> 0 = {StackTraceElement@3424}
>>
>> "org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:166)"
>> 1 = {StackTraceElement@3425}
>>
>> "org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:140)"
>> 2 = {StackTraceElement@3426}
>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)"
>> 3 = {StackTraceElement@3427}
>> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)"
>> 4 = {StackTraceElement@3428}
>>
>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)"
>> 5 = {StackTraceElement@3429}
>>
>> "org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)"
>> 6 = {StackTraceElement@3430}
>>
>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>> 7 = {StackTraceElement@3431}
>>
>> "org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)"
>> 8 = {StackTraceElement@3432}
>>
>> "org.apache.cxf.transport.local.LocalConduit.dispatchDirect(LocalConduit.java:191)"
>> 9 = {StackTraceElement@3433}
>> "org.apache.cxf.transport.local.LocalConduit.close(LocalConduit.java:156)"
>> 10 = {StackTraceElement@3434}
>>
>> "org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)"
>> 11 = {StackTraceElement@3435}
>>
>> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
>> 12 = {StackTraceElement@3436}
>>
>> "org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:652)"
>> 13 = {StackTraceElement@3437}
>>
>> "org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1097)"
>> 14 = {StackTraceElement@3438}
>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)"
>> 15 = {StackTraceElement@3439}
>> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)"
>> 16 = {StackTraceElement@3440}
>> "org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:331)"
>> 17 = {StackTraceElement@3441}
>> "org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:357)"
>>
>> I think the problem is in
>> AbstractInvoker.performInvocation(performInvocation.java:172) - mainly
>> because my debugger output at that position looks like this:
>> paramArray: Object[]  = {Object[2]@3320}
>>   0 = {LocalDate@3270} "2016-10-06"
>>   1 = {ArrayList@3295}  size = 1
>>     0 = {CommaSeparatedList@3294}  size = 3
>>
>> I think what I'd actually need - to conform with the method signature
>> of my foo() method in my endpoint from above - is something that looks
>> a bit more like this (i.e. without the extra ArrayList layer):
>> paramArray: Object[]  = {Object[2]}
>> 0 = {LocalDate}
>> 1 =  {CommaSeparatedLis}  size = 3
>>
>> Any idea how I can get that sorted, please?
>>
>> Again, the goal is to convert ;f=a,b,c into List<Foo>[3] with elements
>> A, B, and C...
>>
>> Kind regards,
>> Christian
>>
>> On Thu, Oct 6, 2016 at 12:20 AM, Christian Balzer
>> <ch...@gmail.com> wrote:
>>>
>>> Hi all,
>>>
>>> We are using cxf with Spring at work, and I have a newbie question...
>>>
>>> This is my method signature:
>>>
>>> @GET
>>> @Path("foo")
>>> public Response foo(@MatrixParam("l") List<String> myList) {
>>>
>>> From that, I want to get a list back with the initial input String (to
>>> my matrix parameter l) being split into list elements at any comma,
>>> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
>>> b and c.
>>>
>>> My converter below is registered (a breakpoint in it is triggered for
>>> myList), but instead of passing rawType as List, I get rawType as
>>> String (so it doesn’t do anything).
>>>
>>> public class StringListHandler implements ParamConverterProvider {
>>>     @Override
>>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>>> Type genericType, Annotation[] annotations) {
>>>         if(rawType == List.class) {
>>>             return new ParamConverter<T>() {
>>>                 @Override
>>>                 public T fromString(String value) {
>>>                     return
>>> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>>>                 }
>>>
>>>                 @Override
>>>                 public String toString(T value) {
>>>                     return value.toString();
>>>                 }
>>>             };
>>>         }
>>>         return null;
>>>     }
>>> }
>>>
>>>
>>> Interestingly enough, I do get a list back. But instead of three
>>> elements a, b and c, it only seems to have one: a,b,c
>>>
>>> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>>>
>>> Is it maybe because cxf comes with a default implementation for a
>>> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
>>> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
>>> thought that c would overwrite a in that situation?)
>>>
>>> Do I have to use a custom class with a List<String> property, like
>>> class MyContainer {
>>>   public List<List> l;
>>> }
>>>
>>> and change the method signature from above to
>>> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>>>
>>> then check for MyContainer.class in ParamConverterProvider and change
>>> the fromString() method to
>>> public T fromString(String value) {
>>>   MyContainer mC = new MyContainer();
>>>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>>>   return rawType.cast(mc);
>>> }
>>>
>>> Or should I create a custom argument annotation, say @CommaSeparated,
>>> and have ParamConverterProvider check for that (and
>>> rawType==String.class)?
>>> But if I do that, won't I get a List<List<String>> back?
>>>
>>> Any help much appreciated!
>>>
>>> Kind regards,
>>>
>>> Christian
>
>
>
> --
> Sergey Beryozkin
>
> Talend Community Coders
> http://coders.talend.com/

Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi

Looks like a linking issue,

Sergey
On 06/10/16 13:24, Christian Balzer wrote:
> Hi all,
>
> So, I played around a bit more, and I now have a semi-working
> solution. Semi-working, because I do get a List of enums back, which
> is what I was actually after.
> Not fully working, because it now blows up in the bowels of cxf (3.1.6).
>
> Here is the mehtod signature - as a reminder, I'm trying to call
> /foo;d=2016-10-06;f=a,b,c - and the LocalDate conversion works just
> fine...
> @GET
> @Path("foo")
> public Response foo(@MatrixParam("d") LocalDate date,
> @MatrixParam("f") Foo foos) {
> //...
>
> Here is my ParamConverterProvider - it is registered and gets called
> for matrix parameter f - yay!
> public class StringListHandler implements ParamConverterProvider{
>     @Override
>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
> Type genericType, Annotation[] annotations) {
>         if(rawType == CommaSeparatedList.class) {
>             return (ParamConverter<T>) new
> ParamConverter<CommaSeparatedList>() {
>                 @Override
>                 public CommaSeparatedList fromString(String value) {
>                     CommaSeparatedList list = new CommaSeparatedList(value);
>                     return list;
>                 }
>                 @Override
>                 public String toString(CommaSeparatedList value) {
>                     return value.toString();
>                 }
>             };
>         }
>         return null;
>     }
> }
>
> Here is my target type. It extends ArrayList - and I think that is now
> the problem...
> public class CommaSeparatedList extends ArrayList<Foo> {
>     public CommaSeparatedList(String value) {
>         List<String> list = Arrays.asList(value.split("\\s*,\\s*"));
>         List<Foo> foos = new ArrayList<>(list.size());
>         for (String element : list) {
>             foos.add(Foo.valueOf(element.toUpperCase()));
>         }
>         addAll(foos);
>     }
> }
>
> Here is the enum I'm using:
> public enum Foo {
>     A, B, C
> }
>
> And here are the Exception and StackTrace cxf now throws at me after I
> return my CommaSeparatedList , i.e. ArrayList<Foo> (extending
> Collection):
> java.lang.IllegalArgumentException: argument type mismatch
> argument type mismatch while invoking public javax.ws.rs.core.Response
> com.example.rs.MyEndpoint.foo(org.joda.time.LocalDate,com.example.common.CommaSeparatedList)
> with params [2016-10-06, [[A, B, C]]].
> 0 = {StackTraceElement@3424}
> "org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:166)"
> 1 = {StackTraceElement@3425}
> "org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:140)"
> 2 = {StackTraceElement@3426}
> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)"
> 3 = {StackTraceElement@3427}
> "org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)"
> 4 = {StackTraceElement@3428}
> "org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)"
> 5 = {StackTraceElement@3429}
> "org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)"
> 6 = {StackTraceElement@3430}
> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
> 7 = {StackTraceElement@3431}
> "org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)"
> 8 = {StackTraceElement@3432}
> "org.apache.cxf.transport.local.LocalConduit.dispatchDirect(LocalConduit.java:191)"
> 9 = {StackTraceElement@3433}
> "org.apache.cxf.transport.local.LocalConduit.close(LocalConduit.java:156)"
> 10 = {StackTraceElement@3434}
> "org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)"
> 11 = {StackTraceElement@3435}
> "org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
> 12 = {StackTraceElement@3436}
> "org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:652)"
> 13 = {StackTraceElement@3437}
> "org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1097)"
> 14 = {StackTraceElement@3438}
> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)"
> 15 = {StackTraceElement@3439}
> "org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)"
> 16 = {StackTraceElement@3440}
> "org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:331)"
> 17 = {StackTraceElement@3441}
> "org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:357)"
>
> I think the problem is in
> AbstractInvoker.performInvocation(performInvocation.java:172) - mainly
> because my debugger output at that position looks like this:
> paramArray: Object[]  = {Object[2]@3320}
>   0 = {LocalDate@3270} "2016-10-06"
>   1 = {ArrayList@3295}  size = 1
>     0 = {CommaSeparatedList@3294}  size = 3
>
> I think what I'd actually need - to conform with the method signature
> of my foo() method in my endpoint from above - is something that looks
> a bit more like this (i.e. without the extra ArrayList layer):
> paramArray: Object[]  = {Object[2]}
> 0 = {LocalDate}
> 1 =  {CommaSeparatedLis}  size = 3
>
> Any idea how I can get that sorted, please?
>
> Again, the goal is to convert ;f=a,b,c into List<Foo>[3] with elements
> A, B, and C...
>
> Kind regards,
> Christian
>
> On Thu, Oct 6, 2016 at 12:20 AM, Christian Balzer
> <ch...@gmail.com> wrote:
>> Hi all,
>>
>> We are using cxf with Spring at work, and I have a newbie question...
>>
>> This is my method signature:
>>
>> @GET
>> @Path("foo")
>> public Response foo(@MatrixParam("l") List<String> myList) {
>>
>> From that, I want to get a list back with the initial input String (to
>> my matrix parameter l) being split into list elements at any comma,
>> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
>> b and c.
>>
>> My converter below is registered (a breakpoint in it is triggered for
>> myList), but instead of passing rawType as List, I get rawType as
>> String (so it doesn\u2019t do anything).
>>
>> public class StringListHandler implements ParamConverterProvider {
>>     @Override
>>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
>> Type genericType, Annotation[] annotations) {
>>         if(rawType == List.class) {
>>             return new ParamConverter<T>() {
>>                 @Override
>>                 public T fromString(String value) {
>>                     return
>> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>>                 }
>>
>>                 @Override
>>                 public String toString(T value) {
>>                     return value.toString();
>>                 }
>>             };
>>         }
>>         return null;
>>     }
>> }
>>
>>
>> Interestingly enough, I do get a list back. But instead of three
>> elements a, b and c, it only seems to have one: a,b,c
>>
>> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>>
>> Is it maybe because cxf comes with a default implementation for a
>> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
>> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
>> thought that c would overwrite a in that situation?)
>>
>> Do I have to use a custom class with a List<String> property, like
>> class MyContainer {
>>   public List<List> l;
>> }
>>
>> and change the method signature from above to
>> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>>
>> then check for MyContainer.class in ParamConverterProvider and change
>> the fromString() method to
>> public T fromString(String value) {
>>   MyContainer mC = new MyContainer();
>>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>>   return rawType.cast(mc);
>> }
>>
>> Or should I create a custom argument annotation, say @CommaSeparated,
>> and have ParamConverterProvider check for that (and
>> rawType==String.class)?
>> But if I do that, won't I get a List<List<String>> back?
>>
>> Any help much appreciated!
>>
>> Kind regards,
>>
>> Christian


-- 
Sergey Beryozkin

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

Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List(3) using ParamConverterProvider doesn't work - how do I do it properly?

Posted by Christian Balzer <ch...@gmail.com>.
Hi all,

So, I played around a bit more, and I now have a semi-working
solution. Semi-working, because I do get a List of enums back, which
is what I was actually after.
Not fully working, because it now blows up in the bowels of cxf (3.1.6).

Here is the mehtod signature - as a reminder, I'm trying to call
/foo;d=2016-10-06;f=a,b,c - and the LocalDate conversion works just
fine...
@GET
@Path("foo")
public Response foo(@MatrixParam("d") LocalDate date,
@MatrixParam("f") Foo foos) {
//...

Here is my ParamConverterProvider - it is registered and gets called
for matrix parameter f - yay!
public class StringListHandler implements ParamConverterProvider{
    @Override
    public <T> ParamConverter<T> getConverter(final Class<T> rawType,
Type genericType, Annotation[] annotations) {
        if(rawType == CommaSeparatedList.class) {
            return (ParamConverter<T>) new
ParamConverter<CommaSeparatedList>() {
                @Override
                public CommaSeparatedList fromString(String value) {
                    CommaSeparatedList list = new CommaSeparatedList(value);
                    return list;
                }
                @Override
                public String toString(CommaSeparatedList value) {
                    return value.toString();
                }
            };
        }
        return null;
    }
}

Here is my target type. It extends ArrayList - and I think that is now
the problem...
public class CommaSeparatedList extends ArrayList<Foo> {
    public CommaSeparatedList(String value) {
        List<String> list = Arrays.asList(value.split("\\s*,\\s*"));
        List<Foo> foos = new ArrayList<>(list.size());
        for (String element : list) {
            foos.add(Foo.valueOf(element.toUpperCase()));
        }
        addAll(foos);
    }
}

Here is the enum I'm using:
public enum Foo {
    A, B, C
}

And here are the Exception and StackTrace cxf now throws at me after I
return my CommaSeparatedList , i.e. ArrayList<Foo> (extending
Collection):
java.lang.IllegalArgumentException: argument type mismatch
argument type mismatch while invoking public javax.ws.rs.core.Response
com.example.rs.MyEndpoint.foo(org.joda.time.LocalDate,com.example.common.CommaSeparatedList)
with params [2016-10-06, [[A, B, C]]].
0 = {StackTraceElement@3424}
"org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:166)"
1 = {StackTraceElement@3425}
"org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:140)"
2 = {StackTraceElement@3426}
"org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:189)"
3 = {StackTraceElement@3427}
"org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:99)"
4 = {StackTraceElement@3428}
"org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)"
5 = {StackTraceElement@3429}
"org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)"
6 = {StackTraceElement@3430}
"org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
7 = {StackTraceElement@3431}
"org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)"
8 = {StackTraceElement@3432}
"org.apache.cxf.transport.local.LocalConduit.dispatchDirect(LocalConduit.java:191)"
9 = {StackTraceElement@3433}
"org.apache.cxf.transport.local.LocalConduit.close(LocalConduit.java:156)"
10 = {StackTraceElement@3434}
"org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)"
11 = {StackTraceElement@3435}
"org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)"
12 = {StackTraceElement@3436}
"org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:652)"
13 = {StackTraceElement@3437}
"org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1097)"
14 = {StackTraceElement@3438}
"org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)"
15 = {StackTraceElement@3439}
"org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)"
16 = {StackTraceElement@3440}
"org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:331)"
17 = {StackTraceElement@3441}
"org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:357)"

I think the problem is in
AbstractInvoker.performInvocation(performInvocation.java:172) - mainly
because my debugger output at that position looks like this:
paramArray: Object[]  = {Object[2]@3320}
  0 = {LocalDate@3270} "2016-10-06"
  1 = {ArrayList@3295}  size = 1
    0 = {CommaSeparatedList@3294}  size = 3

I think what I'd actually need - to conform with the method signature
of my foo() method in my endpoint from above - is something that looks
a bit more like this (i.e. without the extra ArrayList layer):
paramArray: Object[]  = {Object[2]}
0 = {LocalDate}
1 =  {CommaSeparatedLis}  size = 3

Any idea how I can get that sorted, please?

Again, the goal is to convert ;f=a,b,c into List<Foo>[3] with elements
A, B, and C...

Kind regards,
Christian

On Thu, Oct 6, 2016 at 12:20 AM, Christian Balzer
<ch...@gmail.com> wrote:
> Hi all,
>
> We are using cxf with Spring at work, and I have a newbie question...
>
> This is my method signature:
>
> @GET
> @Path("foo")
> public Response foo(@MatrixParam("l") List<String> myList) {
>
> From that, I want to get a list back with the initial input String (to
> my matrix parameter l) being split into list elements at any comma,
> i.e. I want e.g. ";l=a,b,c" to turn into a list of three elements: a,
> b and c.
>
> My converter below is registered (a breakpoint in it is triggered for
> myList), but instead of passing rawType as List, I get rawType as
> String (so it doesn’t do anything).
>
> public class StringListHandler implements ParamConverterProvider {
>     @Override
>     public <T> ParamConverter<T> getConverter(final Class<T> rawType,
> Type genericType, Annotation[] annotations) {
>         if(rawType == List.class) {
>             return new ParamConverter<T>() {
>                 @Override
>                 public T fromString(String value) {
>                     return
> rawType.cast(Arrays.asList(value.split("\\s*,\\s*")));
>                 }
>
>                 @Override
>                 public String toString(T value) {
>                     return value.toString();
>                 }
>             };
>         }
>         return null;
>     }
> }
>
>
> Interestingly enough, I do get a list back. But instead of three
> elements a, b and c, it only seems to have one: a,b,c
>
> What piece of JAX-RS/cxf voodoo am I missing to make this work? ;-)
>
> Is it maybe because cxf comes with a default implementation for a
> List<String> converter so it can turn a URL like foo?l=a&l=b&l=c into
> a List<String> ? Does that also get called for foo;l=a;l=b;l=c ? (I
> thought that c would overwrite a in that situation?)
>
> Do I have to use a custom class with a List<String> property, like
> class MyContainer {
>   public List<List> l;
> }
>
> and change the method signature from above to
> public Response foo(@MatrixParam("l") MyContainer myContainer) {
>
> then check for MyContainer.class in ParamConverterProvider and change
> the fromString() method to
> public T fromString(String value) {
>   MyContainer mC = new MyContainer();
>   mc.l = Arrays.asList(value.split("\\s*,\\s*"));
>   return rawType.cast(mc);
> }
>
> Or should I create a custom argument annotation, say @CommaSeparated,
> and have ParamConverterProvider check for that (and
> rawType==String.class)?
> But if I do that, won't I get a List<List<String>> back?
>
> Any help much appreciated!
>
> Kind regards,
>
> Christian