Hi
On Wed, May 25, 2011 at 10:29 PM, Carl-Erik Kopseng <[email protected]> wrote:
> Quick sum-up of the problem:
> I need to somehow configure a JAX-RS client implemented using CXF's
> proxy-based client API, to automatically convert parameters annotated
> with @FormParam to JSON. I cannot get this to work today.
> I pasted the example code on PasteBin for better viewing:
> http://www.pastebin.ca/2069097
>
> I have created some services in Apache CXF JAX-RS that takes several
> potentially complex objects as arguments. By implementing
> valueOf(String s) for complex beans + using @FormParam, I can
> pass several JSON encoded objects to the "method", not just one,
> as would normally be the case.
> This might not be the best way, or even a good way, to achieve the goal
> of being able to have several arbitrarily complex objects as parameters in
> a POST service, but it is the one I could think of ...
> Feel free to suggest a better way ;)
>
Can you clarify please, does the service need to handle form url
encoded submissions, or
have you decided to post name=value sequences and use FormParam
because you'd like to or may be need to have multiple parameters in a
resource method signature (such as jsonTest) ?
Also, do you need to use JSON or do you use it because Jackson offers
a useful Mapper class which can convert a JSON sequence into a complex
bean.
Just would like to understand your requirements better. CXF offers an
extension such as FormParam(""), so you can have
(@FormParam("myPrimitive") int myPrimitive, @FormParam("") MyObject myComplex)
but that will work only for plain name/value pairs, but won't when
values are actually JSON sequences.
If you don't need to handle form submissions then having a single
composite complex object as a parameter is what I'd suggest.
> An example implementation could be the following:
>
> @Service("receiverService")
> @Path("/receiver")
> public class ObjectReceiverService {
> @POST
> @Path("jsonTest")
> @Produces("application/json")
> public MyObject jsonTest(@FormParam("myPrimitive") int
> myPrimitive, @FormParam("myComplex") MyObject myComplex) {
> return myComplex;
> }
> ...//
> }
>
> //domain class with Mapper from Jackson
> public class MyObject {
> public String foo;
> public String bar;
> public String baz;
>
> public static MyObject valueOf(String s) throws Exception {
> return new ObjectMapper().readValue(s, MyObject.class);
> }
> ...//implementation of equals() etc.
> }
>
> This works quite nicely. So far I have tested it by resorting to
> low-level stuff; manually constructing query strings using Jackson's
> ObjectMapper etc. Example:
>
> //test class with lots of manual setup using WebClient
> public class ObjectReceiverServiceTest {
> @Before
> public void setup() {
> ProviderFactory.getSharedInstance().registerUserProvider(new
> JacksonJsonProvider());
> }
> @org.junit.Test
> public void jsonTest1_recievingSameObject() throws Exception {
> String url = "http://localhost:8080/";
> String path = "rest/receiver/jsonTest";
>
> MyObject myObjectToSend = new MyObject();
> MyObject result = new MyObject();
> myObjectToSend.foo = "foo";
> myObjectToSend.bar = "bar";
> myObjectToSend.baz = "baz";
>
> WebClient client = WebClient.create(url);
> client.path(path);
>
> client.accept("application/json").type(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
> Response r = client.post("myPrimitive=123&myComplex=" + new
> ObjectMapper().writeValueAsString(myObjectToSend));
>
> result = new ObjectMapper().readValue(new BufferedReader(new
> InputStreamReader((InputStream) r.getEntity())).readLine(),
> MyObject.class);
> assertEquals(myObjectToSend, result);
> }
>
> What I would like to do, is to use the proxy based api, as
> demonstrated in the CXF docs. This would be far cleaner and faster to
> implement. The previous test would be something like
>
> @org.junit.Test
> public void jsonTest2_recievingSameObject() throws Exception {
> String url = "http://localhost:8080/rest";
> ObjectReceiverService service = JAXRSClientFactory.create(url,
> ObjectReceiverService.class);
>
> MyObject myObjectToSend = new MyObject();
> myObjectToSend.foo = "foo2";
> myObjectToSend.bar = "bar2";
> myObjectToSend.baz = "baz2";
> MyObject result = service.jsonTest(123,myObjectToSend);
>
> assertEquals(myObjectToSend, result);
> }
>
> Today, this throws an WebApplicationException in
> org.apache.cxf.jaxrs.provider.FormEncodingProvider.writeTo(..), as it
> is probably just expecting primitives.
>
> I am not quite sure how to deal with this. Any ideas? I tried
> overriding toString() with { return new
> ObjectMapper.writeValueAsString(this); }, which is ok, but
> FormEncodingProvider.writeTo still fails when trying to convert
> non-string objects,
> like an Integer to String.
Yes, it does expect Strings only - I think the fix needs to be applied
at the client side, before FormEncodingProvider is invoked, because
JAX-RS requires the support for MultivaluedMap<String, String> - I'll
look into it
Cheers, Sergey
>
> Regards
> Carl-Erik
>