3 Replies Latest reply on Sep 26, 2014 3:01 PM by Greg Moulds

    Composite service + RESTEasy binding + JSON messages

    Greg Moulds Newbie

      Hi folks,

       

      I've been playing around with SwitchYard 2.0.0.Alpha2 and had some moderate success however I've run across an issue that I could use some guidance with.

       

      I'm trying to create a composite service with a RESTEasy binding that speaks JSON rather than XML. I've based my service on the OrderService of the rest-binding quickstart application.

       

      My very simple service looks like so:

       

      @Service(Greeting.class)
      public class GreetingBean implements Greeting {
      
        public String helloWorld() {
          return "Hello, world.";
        }
      
        public String generate(UserBio bio) {
          String msg = String.format("Greetings %s %s!", bio.getFirstName(), bio.getLastName());
          return msg;
        }
      }
      
      

       

      My model class looks like so:

       

      import javax.xml.bind.annotation.XmlRootElement;
      
      @XmlRootElement
      public class UserBio {
      
        private String firstName;
        private String lastName;
      
        public UserBio() {
        }
      
        public UserBio(String firstName, String lastName) {
          this.setFirstName(firstName);
          this.setLastName(lastName);
        }
      
        public String getFirstName() {
          return firstName;
        }
      
        public void setFirstName(String firstName) {
          this.firstName = firstName;
        }
      
        public String getLastName() {
          return lastName;
        }
      
        public void setLastName(String lastName) {
          this.lastName = lastName;
        }
      }
      
      

       

      My RESTEasy resource mapping looks like so:

       

      import javax.ws.rs.Consumes;
      import javax.ws.rs.GET;
      import javax.ws.rs.PUT;
      import javax.ws.rs.Path;
      
      @Path("/greeting")
      public interface GreetingResource {
      
        @GET
        @Path("/")
        public String helloWorld();
      
        @PUT
        @Path("/generate")
        @Consumes({ "application/json" })
        public String generate(UserBio bio);
      }
      
      

       

      Note that the generate() method has been annotated such that it is expecting to consume JSON rather than XML content.

       

      It's my understanding that RESTEasy JAX-RS has built-in support for JSON messages and that that is all I have to do get my service to speak JSON.

       

      That's probably wrong though because when I run a unit test like this:

       

      @Test
      public void greetingServiceRESTEndpoint() throws Exception {
      
          // Hello World
          String response = http.sendString(BASE_URL + "/greeting", "", HTTPMixIn.HTTP_GET);
          Assert.assertEquals(HELLO_WORLD, response);
      
          // Generate a greeting
          http.setContentType("application/json");
          ObjectMapper mapper = new ObjectMapper();
          String jsonString = mapper.writeValueAsString(userBio1);
      
          response = http.sendString(BASE_URL + "/greeting/generate", jsonString, HTTPMixIn.HTTP_PUT);
          Assert.assertEquals(SUCCESS, response);
      }
      
      

       

      The helloWorld response is successful however the 2nd http send fails with the following response from the server:

       

      HTTP/1.1 400 Could not find message body reader for type: class com.acme.bhn_test_service.UserBio of content type: application/json;charset="UTF-8" [Connection: keep-alive, Content-Length: 0]

       

      Any help from the hive mind is greatly appreciated.

      Thx.

       

      Edit: -- Forgot to mention that I will be deploying this on wildfly eventually however the unit test uses Netty as per the rest-binding sample... in case that's relevant...

        • 1. Re: Composite service + RESTEasy binding + JSON messages
          Jorge Morales Master

          Hi,

          I think you need to add a JSOn transformer. Even it will do it automatically, you have to register the transformer for it AFAIK.

          https://docs.jboss.org/author/display/SWITCHYARD/JSON+Transformer

           

          Cheers,

          • 2. Re: Re: Composite service + RESTEasy binding + JSON messages
            Greg Moulds Newbie

            Thanks Jorge.

             

            I finally had a chance to come back to this today. I don't think your suggestion about registering a transformer is correct because (and this is my understanding - I could certainly be wrong) the interfaces being exposed by my composite service and the related component service are both the same (java in my case) so a transformation is not needed -- see this post from Keith Babo.

             

            I did in fact find 2 solutions to my problem. The first is to use the BadgerFish annotation provided by the resteasy-jettison-provider. See here for an example. That's a less than ideal solution however because a) the json that is produced has some quirks about it (i.e. something like <foo>bar</foo> translates to { "foo": { "$": "bar" } }) and b) the xml is still being used/generated and then translated to json. Not great since I only want to deal with json.

             

            The 2nd (and better) solution is to modify the JAX-RS annotations slightly and verify that your http client is setup properly. The steps are documented here.

             

            Basically, my REST resource is now defined like so:

             

            import javax.ws.rs.Consumes;
            import javax.ws.rs.GET;
            import javax.ws.rs.POST;
            import javax.ws.rs.Path;
            import javax.ws.rs.Produces;
            
            /**
            * REST interface for GreetingService.
            */
            @Path("/greeting")
            public interface GreetingREST {
            
                @GET
                @Path("/")
                @Produces({ "application/xml", "application/json" })
                public Greeting helloWorld();
            
                @POST
                @Path("/generate")
                @Consumes({ "application/xml", "application/json" })
                @Produces({ "application/xml", "application/json" })
                public Greeting generate(UserBio bio);
            
            }
            

             

            Making sure that the content-type for my http client is set to "application/json" and that I set the http header parameter "Accept" to a value of "application/json" results in the following result when invoking the helloWorld GET:

            {"greeting":{"message":"Hello world!"}}

            and when I POST this content

            { "userBio": { "firstName": "Guy", "lastName": "Smiley" } }

            I receive the following response:

            {"greeting":{"message":"Greetings Guy Smiley!"}}

             

            Thanks for the response and I hope this helps others.

            • 3. Re: Re: Composite service + RESTEasy binding + JSON messages
              Greg Moulds Newbie

              Small update:

              Note that the json produced and consumed in my example corresponds to the Netty server I'm using in my unit tests. The json produced and consumed by wildfly differs slightly (i.e. wildfly will omit the "userBio" and "greeting" elements and just return the data contained inside them).