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 <sberyoz...@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....@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/

Reply via email to