Ok
I made some progress and in the mean time I understood some of the
things you have been saying... I think the following descriptions
summarizes my status. I have also asked for help from resty-gwt forum. I
will let you know what comes up...
Thanks for the help so far. Much apprecieated...
https://groups.google.com/forum/#!topic/restygwt/T98XXEHiCl0
Hi everybody,
Here are some must do reading that I found...
http://stackoverflow.com/questions/3182033/pass-list-to-restful-webservice
https://github.com/resty-gwt/resty-gwt/issues/92
So what I am trying to do? Basically this.
@POST
@Path("existing-entities")
@Consumes("application/x-www-form-urlencoded")
public void getExistingEntities(
@FormParam("entities") List<Entity> entities,
MethodCallback<List<Entity>> callback);
This however transmits the entities as one jsonified array with [{x:"a",
y:"b"}, {x:"c", y:"d"}] if you know what I mean. CXF in the other
doesn't parse it as per JAX-RS spec probably but I am not sure. So what
are the options?
1) Use a list wrapper. Ugly and as I said I am porting existing services
and I don't feel like filling my sources with useless wrapper classes.
Never mind the changes to the clients of the services. This is my last
option. I will do that when all else fail.
2) Don't use @FormParam. This actually works
@POST
@Path("existing-entities")
public void getExistingEntities(
List<Entity> entities,
MethodCallback<List<Entity>> callback);
This puts the payload to MessageBody.
The disadvantage is that you can send only one list this way. As I said
previously I am porting... and there are cases that I need to transmit 2
or more lists as input parameters.
3) Third option and here I may need some help. If you actually post a
form with the same option name CXF (and probably Jersey) puts it in a
list. I have verified with curl
curl --data 'entities=`urlencode {x:"a",
y:"b"}`&entities=`urlencode {x:"c", y:"d"}`'
http://localhost/ws/rest/v1/ported/existing-entities -H 'Accept:
application/json'
and it works. So what I need is a way to teach Resty-GWT to do that kind
of form posting and not the entities: [{x:"a", y:"b"}, {x:"c", y:"d"}]
thingy when encounters
@POST
@Path("existing-entities")
@Consumes("application/x-www-form-urlencoded")
public void getExistingEntities(
@FormParam("entities") List<Entity> entities,
MethodCallback<List<Entity>> callback);
I suppose I have to use a custom MessageBodyWriter in the client side.
Is this possible in resty-gwt? I don't mind mucking with resty-gwt
source but I don't know from where to start. Any ideas?
Thanks in advance.
On 08/12/2014 10:23 PM, Sergey Beryozkin wrote:
Hi,
On 12/08/14 19:44, [email protected] wrote:
Hi Sergey,
First of all thanks for your prompt reply as always. Your help is
appreciated.
Secondly, I am sorry I didn't explain the situation more clearly. I am a
bit frustrated with this... as always when things don't work as we
expect them too. It is quite probable that I lack the necessary
understanding to make things work.
Now to the problem.
I have GWT and Resty-GWT into the client. RestyGWT requires jackson and
so I have to use jackson and not jettinson in the server part where I
have CXF.
In the client I jave something like this
@POST
@Path("existing-entities")
@Consumes("application/x-www-form-urlencoded")
public void getExistingEntities(
@FormParam("entities") List<Entity> entities,
MethodCallback<List<Entity>> callback);
In the server I have
@POST
@Path("existing-entities")
@Consumes("application/x-www-form-urlencoded")
public List<Entity> getExistingEntities(
@FormParam("entities") List<Entity> entities) throws
SQLException;
When I issue the call from the client with two entities: I get in the
form contents. This is done by resty-gwt:
entities: [{"name":"cca1", "type":"GENE"},{"name":"cca2", "type":"GENE"}]
which is a JSONArray payload.
What cxf does is to take the full json string and trying to shovel it
down the throat of my poor ParamConverter as entity and not as
List<Entity>.
as I explained earlier with the example of a query containing multipal
parameters of the same name, ParamConverterProvider deals with the
individual parameters such as Entity in your case
I am not getting it. I think CXF either should break the array and call
the converter for each object or it should leave the converter to
convert the array. Now it is not breaking it but it doesn't give me the
option to handle the array,
How exactly would CXF or indeed any other generic framework break this
JSON Array ? It won't. It has no idea about the semantics associated
with a given parameter.
You were right earlier on, just introduce a wrapper and forget about
mapping it to the explicit collection :-). Explicit collections just
make things more complicated unless there's an obvious/straightforward
mapping as in the example I provided earlier on.
Thanks, Sergey
INFO
com.biovista.ws.impl.jaxrs.JacksonJsonParamConverterProvider.getConverter(JacksonJsonParamConverterProvider.java:41):
**** called: arg0: class com.biovista.lib.datatype.Entity arg1: class
com.biovista.lib.datatype.Entity arg2:
[Ljava.lang.annotation.Annotation;@7f65ae66 providers:
org.apache.cxf.jaxrs.impl.tl.ThreadLocalProviders@63aea6a8
INFO
com.biovista.ws.impl.jaxrs.JacksonJsonParamConverterProvider.getConverter(JacksonJsonParamConverterProvider.java:44):
Annotation: @javax.ws.rs.FormParam(value=entities)
INFO
com.biovista.ws.impl.jaxrs.JacksonJsonParamConverterProvider$1.fromString(JacksonJsonParamConverterProvider.java:71):
Called for [{"name":"cca1", "type":"
GENE"},{"name":"cca2", "type":"GENE"}]
WARN 2014-08-12 17:47:46,722 http-bio-8081-exec-197:
org.apache.cxf.common.logging.LogUtils.doLog(LogUtils.java:452):
Interceptor for {http://impl.ws.biovista.com/}VizitService has thrown
exception, unwinding now javax.ws.rs.ProcessingException:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize
instance of com.biovista.lib.datatype.Entity out of START_ARRAY token
Now what do you think I should do in order to send complex types and
collections to the server from the client?
About Jersey - I don't know I have never used it. I am not sure how I
should research it. In theory everything works. In practice you get to
see the weird corner cases.
I am not 100% sure but I believe the opposite way (cxf to send
List<Entity>) works as expected. I will test and report tomorrow.
Again thanks for the patience.
On 2014-08-12 12:17, Sergey Beryozkin wrote:
On 12/08/14 17:13, Sergey Beryozkin wrote:
Sorry, do not understand what you are trying to do.
Why do you use a single parameter (query, header, or may be form) to
send a JSON array ? This is typically sent as a message body, in which
case JAX-RS MessageBodyReader takes care of it.
Imagine this case:
?a=1-2-3&a=4-5-6
here we have 2 'a' query parameters, one with value "1-2-3" and another
- with "4-5-6"
and we have this signature:
Response get(@QueryParam("a") List<Integer> list);
In this case we convert each parameter to Integer (the converter would
remove '-'), the runtime takes case of creating a List.
If we expected the converter to convert a String to List<Integer> then
what would the runtime pass to the converter, "a=1-2-3&a=4-5-6" and
expect it to parse it manually ? Not sure it makes sense.
And given that ParamConverterProvider is expected to be reusable, what
would we do with
and a sequence like
/path;a=1-2-3;a=4-5-6
Response get(@MatrixParam("a") List<Integer> list);
the converter would have to parse both
"a=1-2-3&a=4-5-6" and "a=1-2-3;a=4-5-6"
IMHO it is not how it should work, but please do not hesitate to
investigate it further with Jersey
Cheers, Sergey
Can you investigate what Jersey does ? I'm pretty sure it was
discussed,
specifically that the runtime should take care of dealing with the list
itself, but they might deal with it differently
Cheers, Sergey
On 12/08/14 16:45, Vassilis Virvilis wrote:
Aah,
So in order to make this work through CXF is to define an
EntitiesListWrapper class. Right? Then the ParamConverter will be
called
with EntitiesListWrapper as argument and jackson will deserialize.
Right?
Is there any other way to break the input of
final String entities_json = "[{\"name\":\"cca1\",
\"type\":\"GENE\"},{\"name\":\"cca2\", \"type\":\"GENE\"}]";
so the paramconverter will be called with the correct substring each
time?
This looks weird.
Vassilis
On 08/12/2014 06:22 PM, Sergey Beryozkin wrote:
Hi
AFAIK ParamConverterProvider is expected to be called once per every
entity in the list.
Thanks, Sergey
On 12/08/14 16:12, Vassilis Virvilis wrote:
Hi,
Some time before I have started a thread with jackson and form
parameters in cxf
http://mail-archives.apache.org/mod_mbox/cxf-users/201406.mbox/%[email protected]%3E
Sergey suggested that I should use ParamConverterProvider.
I tried it today and it worked. Great.
Now I am trying the next step which is to pass List<Entity>
instead of
Entity in the input. The problem is that my
ParamConverterProvider is
not called with the actual input class as specified in the interface
(List<Entity>) but with plain Entity as you can see from the
logs. It
then fails because due to
*** Can not deserialize instance of
com.biovista.lib.datatype.Entity out of START_ARRAY token ****
INFO
com.biovista.ws.impl.jaxrs.JacksonJsonParamConverterProvider.getConverter(JacksonJsonParamConverterProvider.java:41):
**** called: arg0: class com.biovista.lib.datatype.Entity arg1:
class
com.biovista.lib.datatype.Entity arg2:
[Ljava.lang.annotation.Annotation;@7f65ae66 providers:
org.apache.cxf.jaxrs.impl.tl.ThreadLocalProviders@63aea6a8
INFO
com.biovista.ws.impl.jaxrs.JacksonJsonParamConverterProvider.getConverter(JacksonJsonParamConverterProvider.java:44):
Annotation: @javax.ws.rs.FormParam(value=entities)
Here is my ParamConverterProvider
package com.biovista.ws.impl.jaxrs;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.ext.MessageContext;
import org.codehaus.jackson.map.ObjectMapper;
@Provider
public class JacksonJsonParamConverterProvider implements
ParamConverterProvider {
private final Log log = LogFactory.getLog(getClass());
@Override
public <T> ParamConverter<T> getConverter(final Class<T>
rawType,
final Type genericType, final Annotation[]
annotations) {
log.info("**** called: arg0: " + rawType + " arg1: " +
genericType
+ " arg2: " + annotations + " providers: " +
providers);
for (final Annotation annotation : annotations) {
log.info("Annotation: " + annotation);
}
return new ParamConverter<T>() {
@Override
public T fromString(final String value) {
try {
log.info("Called for " + value);
return mapper.reader(rawType).readValue(value);
} catch(IOException e) {
throw new ProcessingException(e);
}
}
@Override
public String toString(final T value) {
try {
return
mapper.writer().writeValueAsString(value);
} catch(Exception e) {
throw new ProcessingException(e);
}
}
};
}
}
How can I register different ParamConverterProvider for different
input
types? Or is that List is handled specially? By jackson or CXF? In
jackson I just need to do
final List<Entity> entities =
mapper.readValue(entities_json,
new TypeReference<List<Entity>>() {
});
but I don't know how to do this from inside CXF. I am using CXF 3.0
Thanks
--
__________________________________
Vassilis Virvilis Ph.D.
Head of IT
Biovista Inc.
US Offices
2421 Ivy Road
Charlottesville, VA 22903
USA
T: +1.434.971.1141
F: +1.434.971.1144
European Offices
34 Rodopoleos Street
Ellinikon, Athens 16777
GREECE
T: +30.210.9629848
F: +30.210.9647606
www.biovista.com
Biovista is a privately held biotechnology company that finds novel uses
for existing drugs, and profiles their side effects using their
mechanism of action. Biovista develops its own pipeline of drugs in CNS,
oncology, auto-immune and rare diseases. Biovista is collaborating with
biopharmaceutical companies on indication expansion and de-risking of
their portfolios and with the FDA on adverse event prediction.