cxf-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Christian Balzer <christian.at....@gmail.com>
Subject Re: JAX-RS: Mapping @MatrixParam("l") ";l=a,b,c" to List<String>(3) using ParamConverterProvider doesn't work - how do I do it properly?
Date Thu, 06 Oct 2016 13:23:39 GMT
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 <sberyozkin@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
>> <christian.at.lhr@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/

Mime
View raw message