1 2 Previous Next 16 Replies Latest reply on Jul 13, 2010 11:18 AM by jackalista

    JAX-RS client support

    jervisliu

      Hi, I recently had a chat with Stefano on WISE IRC regarding the possibilities of supporting JAX-RS client in WISE with a "near to zero-code approach".

      One possibility is through WISE GUI. Below is a typical scenario:

      User opens WISE GUI, selects the option of "Sending a request to JAX-RS services". On the GUI, user needs to fill in following information.

      1. The URI of JAX-RS service resource. Eg, http://localhost:80/bookservice/books/123
      2. Http method to use(GET|PUT|POST|DELETE)
      3. HTTP headers to use
      4. The content of the request: we will probably has a text box to allow users to input the request as plain text or be able to point to a local file then load the file as a stream.

      Once information above are collected, WISE will generate a request and sent it out to JAX-RS server. What underlying libraries to use are not relevant to the end users, as long as it is "code-free" from the end users' prospective.

      Another possibility is though a programmatic client side API. However design a decent client side API for JAX-RS is never an easy job, that's why JAX-RS spec does not have a plan to address client API in its 1.0 version at all. There are already many projects working on JAX-RS specific areas such as Jersey, Apache CXF, RESTEasy etc, I don't think WISE wants to join this competition which is not what WISE is about to focus on anyway, right? Of course the alternative is to leverage one of these projects then add values by using WISE. I had a quick look into RESTEasy[1], but I do not see how "zero-code" is going to be achieved. RESTEasy client framework requires one to hand write a JAVA interface like below:

      public interface SimpleClient
      {
      @GET
      @Path("basic")
      @ProduceMime("text/plain")
      String getBasic();

      @PUT
      @Path("basic")
      @ConsumeMime("text/plain")
      void putBasic(String body);

      @GET
      @Path("queryParam")
      @ProduceMime("text/plain")
      String getQueryParam(@QueryParam("param")String param);

      @GET
      @Path("matrixParam")
      @ProduceMime("text/plain")
      String getMatrixParam(@MatrixParam("param")String param);

      @GET
      @Path("uriParam/{param}")
      @ProduceMime("text/plain")
      int getUriParam(@PathParam("param")int param);
      }

      All the magic that WISE can do is based on the service contract(WSDL in the case of JAX-WS). From the service contract, the complexity can be hidden by doing the code generation on the fly. If there is no contract available (which is the case for most RESTful services, there is no formal service contract description language) or user has to write a contract manually (in the RESTEasy case, the Java interface serves as the contract of JAX-RS service, from which a client proxy can be generated), I do not see how WISE is going to play its role. Well, I could be wrong though, I am new to both RESTEasy and WISE, so please do correct me if I got it wrong.

      Comments are welcome.

      Thanks,
      Jervis

        • 1. Re: JAX-RS client support
          jervisliu

           

          4. The content of the request: we will probably has a text box to allow users to input the request as plain text or be able to point to a local file then load the file as a stream.


          Of course this part can be extended. For example:

          1. The request can be loaded from an Excel file. this will form a sequence of requests. For example, request one is from the column 1 row 1. Request two is from the column 1 row 2 and so on.

          2. We can also provide helps to help user to compose a request of a specific type on the GUI. The types I referred to are XML, HTML, JSON, AtomPub etc. Take XML as an example, if the actually content sending over the wire is XML document, we will have two editors to allow users to input their request. One is a plain text editor with which user can edit the XML request directly as plain text, another one is an XML editor. User has choices to decide which one to use. Same applies to JSON editor, AtomPub editor etc.

          • 2. Re: JAX-RS client support
            maeste

             

            "jervisliu" wrote:

            One possibility is through WISE GUI. Below is a typical scenario:
            ...CUT...

            Oki, we are agree here. Even if Web gui is going to be redesigned and will come out in JBoss.org project not before 1.1.

            Another possibility is though a programmatic client side API.

            That is the goal for Wise 1.0


            However design a decent client side API for JAX-RS is never an easy job, that's why JAX-RS spec does not have a plan to address client API in its 1.0 version at all. There are already many projects working on JAX-RS specific areas such as Jersey, Apache CXF, RESTEasy etc, I don't think WISE wants to join this competition which is not what WISE is about to focus on anyway, right? Of course the alternative is to leverage one of these projects then add values by using WISE.

            No we don't want to join JAX-RS competion for sure. But as you said JAX-RS take care of server side, not client side API.
            Maybe I'm totally wrong, but it seems to me we can provide API to call diagrammatically a JAX-RS URL more or less in the same manner we are providing JAX-WS. This is the use case I have in mind (that isn't far from the description you have just done for webgui). The developer using our API provide:

            1. The URI of JAX-RS service resource. Eg, http://localhost:80/bookservice/books/123
            2. Http method to use(GET|PUT|POST|DELETE)
            3. Wise generate message to be send. For this par we have more than one way, depending ont the situation:
            a. If there is an xsd users could provide it and we can use xjc on the fly to generate jaxb object and then standard wise's mapper if needed to map user's model to JAXB model
            b. Use a smooks mapper to map to XML (eventually checking DTD/XSD if there is one), or a smooks/freemarker template to generate JSON or Atom

            4. Wise call (standard HTTP client??) the JAX-RS service.



            I think point 3 could fit in "zero-code" approach giving users some good oadded value, even using Wise as API. Moreover thinking about Wise as ESB's action I think it culd be a great added value permitting users to define a "zero-code" action to call both JAX-WS and JAX-RS "endpoint" mapping on the fly from message content to service and back from service answer to their own model to be inserted in ESB message in response. These capability to work both with JAX-WS and JAX-RS would be good also when we will implement a Wise's smooks visitors (WISE-76).


            I had a quick look into RESTEasy[1], but I do not see how "zero-code" is going to be achieved. RESTEasy client framework requires one to hand write a JAVA interface like below:

            I'm not sure Wise could use this kind of interface, and/or if it would get some advantages from them. But maybe I'm loosing something...comments and hints here are welcome.


            All the magic that WISE can do is based on the service contract(WSDL in the case of JAX-WS). From the service contract, the complexity can be hidden by doing the code generation on the fly. If there is no contract available (which is the case for most RESTful services, there is no formal service contract description language) or user has to write a contract manually (in the RESTEasy case, the Java interface serves as the contract of JAX-RS service, from which a client proxy can be generated), I do not see how WISE is going to play its role. Well, I could be wrong though, I am new to both RESTEasy and WISE, so please do correct me if I got it wrong.

            I already said my opinion about. Let me remark that Wise (in particular as ESB action) could be interesting also because it is providing an out of the box integrationwith smooks. Of course for JAX-RS you could get same results using smooks and http client, but maybe a single framework to calls JAX-WS and JAX-RS services providing apowerfull mapping between user object model and service model/contract may be sexy.
            Perhaps I'm loving too much Wise and it's not so interesting.
            Comments from others and community are more than welcome.

            Thanks to have opened the discussion.

            • 3. Re: JAX-RS client support
              marklittle

              Have you looked at WADL?

              I think it should be possible to keep the Wise-RS approach for the client implementation neutral as far as JAX-RS implementations are concerned. But couldn't we provide a plugin? So for example, maybe out of the box Wise just calls the URIs directly using PUT, GET etc. But maybe there's a "generate RESTeasy client" button too. Of course this would require some meta-data (pseudo client-side language) for defining what the client wants to happen and then the plugin maps that down to the underlying implementation approach. But I'm not sure what that brings the Wise user, if anything.

              • 4. Re: JAX-RS client support
                maeste

                Hi Mark,

                Yep WADL is an opportunity.
                I had understood (but I may be wrong) it isn't widely used, so we are planning to support it in a second step.

                I agree with you, as I tried to explain in my post, Wise-RS client should be neutral as far as JAX_RS implementations are concernedm, and IMHO just calls URI wit PUT/GET/etc is the best approach (at least for the pure code implementation and possible integration in ESB)

                Thanks for the comment

                • 5. Re: JAX-RS client support
                  jervisliu

                   

                  Maybe I'm totally wrong, but it seems to me we can provide API to call diagrammatically a JAX-RS URL more or less in the same manner we are providing JAX-WS. This is the use case I have in mind (that isn't far from the description you have just done for webgui). The developer using our API provide:

                  1. The URI of JAX-RS service resource. Eg, http://localhost:80/bookservice/books/123
                  2. Http method to use(GET|PUT|POST|DELETE)
                  3. Wise generate message to be send. For this par we have more than one way, depending ont the situation:
                  a. If there is an xsd users could provide it and we can use xjc on the fly to generate jaxb object and then standard wise's mapper if needed to map user's model to JAXB model
                  b. Use a smooks mapper to map to XML (eventually checking DTD/XSD if there is one), or a smooks/freemarker template to generate JSON or Atom

                  4. Wise call (standard HTTP client??) the JAX-RS service.



                  I think point 3 could fit in "zero-code" approach giving users some good oadded value, even using Wise as API. Moreover thinking about Wise as ESB's action I think it culd be a great added value permitting users to define a "zero-code" action to call both JAX-WS and JAX-RS "endpoint" mapping on the fly from message content to service and back from service answer to their own model to be inserted in ESB message in response. These capability to work both with JAX-WS and JAX-RS would be good also when we will implement a Wise's smooks visitors (WISE-76).


                  Lets say user has to provide things as you mentioned above, for example:

                  1. URI = http://localhost:80/bookservice/books/123
                  2. HTTP Method = PUT
                  3. Input parameters. If the input parameters are not simple type, user needs to provides a jaxb object (if they want to use jaxb. users may want to use othere data format, such as JSON, AtomPub). Lets say the input parameter is a jaxb object called MyComplexTypeObject1 and the media type is application/xml.
                  4. no specific headers are required

                  The information above might be stored in a configuration file. If you think about this twice, actually it equals to the java interface below (RESTEasy)

                  public interface SimpleClient
                  {
                  
                   @PUT
                   @Path("bookservice/books")
                   @ConsumeMime("application/xml")
                   MyComplexTypeObject2 putBasic(MyComplexTypeObject1 para);
                  
                  }


                  The MyComplexTypeObject1 and MyComplexTypeObject2 are pre-generated Java type class maybe from an xsd. Or they can be replaced by an internal object used by wise client, so that we can go dynamic, by defining an extra smooks transformer to transform from an external object to the internal object. i.e., like this:

                  public interface SimpleClient
                  {
                  
                   @PUT
                   @Path("bookservice/books")
                   @ConsumeMime("application/xml")
                   InternalWISEObject putBasic(InternalWISEObject para);
                  
                  }
                  

                  so there is no difference between the approach you described above and the RESTEasy client approach.


                  • 6. Re: JAX-RS client support
                    maeste

                    Yep, there is no difference in what user are required to provide.
                    The difference is exactly the added value of Wise:
                    1. Dynamic generation of JAXB object
                    2. Out of the box smooks mapping (and please note it could be used also for different format like atom/json)
                    3. No code required, but just config in ESB (one of the most impotant point IMHO)
                    4. Smooks freemarker template could be used also for HTTP header if required
                    5. Last but not least similar API or even same ESB action to invoke both JAX-WS and JAX-RS.

                    I think these point merits some hour of code to see what we can get into Wise for JAX-RS. Am I wrong?

                    • 7. Re: JAX-RS client support
                      jervisliu

                       

                      3. No code required, but just config in ESB (one of the most impotant point IMHO)


                      The config you refer to is the java interface (or its equal part of a xml config file.) like below, right?

                      public interface SimpleClient
                      {
                      
                       @PUT
                       @Path("bookservice/books")
                       @ConsumeMime("application/xml")
                       MyComplexTypeObject2 putBasic(MyComplexTypeObject1 para);
                      
                      }


                      • 8. Re: JAX-RS client support
                        maeste

                         

                        "jervisliu" wrote:
                        3. No code required, but just config in ESB (one of the most impotant point IMHO)


                        The config you refer to is the java interface (or its equal part of a xml config file.) like below, right?

                        public interface SimpleClient
                        {
                        
                         @PUT
                         @Path("bookservice/books")
                         @ConsumeMime("application/xml")
                         MyComplexTypeObject2 putBasic(MyComplexTypeObject1 para);
                        
                        }

                        Yes, and it have also a reference to a smooks config/template for request and one for answer (we may decide to have also some config for HTTP header and/or authentication and/or logging as we have in JAX-WS). The idea (into esb) is the same of JAX-WS: take a message, map it to a call service and map back to a message the answer.


                        • 9. Re: JAX-RS client support
                          jervisliu

                          I was playing with some RESTEasy and WISE code yesterday, it appears to me that we do not need RESTEasy at all. Below is a pseudo code that shows how this is supposed to work:

                           WSJaxrsClient client = WSJaxrsClientFactory.getInstance().getClient();
                           WSMethod method = client.getMethod("http://localhost:80/bookservice/books/123", WSJaxrsClientFactory.HTTP_POST);
                           method.setRequestHeader("Content-Type", "application/xml");
                          
                           InternalObject internal = new InternalObject();
                           internal.setNumber(new Integer(1));
                           internal.setText("aa");
                           ExternalObject external = new ExternalObject();
                           external.setDate(new Date());
                           external.setInternal(internal);
                           // without smooks debug infos
                           InvocationResult result = method.invoke(external, new SmooksMapper("./smooks-config-XMLGregorianCalendar.xml"));
                           System.out.println(result.getMappedResult(new SmooksMapper("./smooks-response-config.xml"), null));


                          Essentially the WSJaxrsClient and WSMethod is just a wrapper over HTTP Client.

                          What I have not figured out yet is how the xsd is going to play its role in this scenario (if the request content type is application/xml and the xsd is provided). Can the SmooksMapper take the input object (the external object in the code snippet above), then turn this object into a xml stream directly according to the xsd? so that we do not need to go through JAXB at all.

                          • 10. Re: JAX-RS client support
                            maeste

                             

                            "jervisliu" wrote:
                            I was playing with some RESTEasy and WISE code yesterday, it appears to me that we do not need RESTEasy at all.


                            +1 on this

                            "jervisliu" wrote:

                            Below is a pseudo code that shows how this is supposed to work:
                            
                             WSJaxrsClient client = WSJaxrsClientFactory.getInstance().getClient();
                             WSMethod method = client.getMethod("http://localhost:80/bookservice/books/123", WSJaxrsClientFactory.HTTP_POST);
                             method.setRequestHeader("Content-Type", "application/xml");
                            
                             InternalObject internal = new InternalObject();
                             internal.setNumber(new Integer(1));
                             internal.setText("aa");
                             ExternalObject external = new ExternalObject();
                             external.setDate(new Date());
                             external.setInternal(internal);
                             // without smooks debug infos
                             InvocationResult result = method.invoke(external, new SmooksMapper("./smooks-config-XMLGregorianCalendar.xml"));
                             System.out.println(result.getMappedResult(new SmooksMapper("./smooks-response-config.xml"), null));


                            Essentially the WSJaxrsClient and WSMethod is just a wrapper over HTTP Client.


                            yep, great. I love your snippet, because I'd like very much to keep API as much similar as possible for both standards (JAX-WS and JAX-RS)

                            "jervisliu" wrote:

                            What I have not figured out yet is how the xsd is going to play its role in this scenario (if the request content type is application/xml and the xsd is provided). Can the SmooksMapper take the input object (the external object in the code snippet above), then turn this object into a xml stream directly according to the xsd? so that we do not need to go through JAXB at all.

                            Yep it can turn input object into XML stream writing a mapping file, and it is a possible use. But AFAIK smooks transformer doesn't validate output against an XSD. It would be the role of JAXB object (or even a post porcessing introduced by Wise without JAXB generation could be a choice).
                            Moreover we can use smooks mapper with XSL of freemarker template to support JSON and ATOM.


                            • 11. Re: JAX-RS client support
                              jervisliu

                              How about the code below. Does this example have any value added comparing to a standard JAX-RS client based on HTTPClient?

                              public static void main( String[] args ) {
                               try {
                              
                               //the request is a Java object
                               InternalObject internal = new InternalObject();
                               internal.setNumber(new Integer(1));
                               internal.setText("aa");
                               ExternalObject external = new ExternalObject();
                               external.setDate(new Date());
                               external.setInternal(internal);
                              
                               //JAVA to XML using Smooks
                               Smooks smooks = new Smooks("smooks-config.xml");
                               ExecutionContext executionContext = smooks.createExecutionContext();
                               StringWriter writer = new StringWriter();
                              
                               // Configure the execution context to generate a report...
                              
                               executionContext.setEventListener(new HtmlReportGenerator("target/report/report.html"));
                              
                               // Filter the message to the result writer, using the execution context...
                              
                               smooks.filter(new JavaSource(external), new StreamResult(writer), executionContext);
                              
                               String request = writer.toString();
                              
                              
                               //Using HTTPClient
                               String endpointAddress = "http://localhost:9080/repository/artifacts";
                              
                               PostMethod post = new PostMethod(endpointAddress);
                               post.setRequestEntity(new StringRequestEntity(request, "application/atom+xml", null));
                              
                               HttpClient httpclient = new HttpClient();
                              
                               try {
                               int result = httpclient.executeMethod(post);
                               String response = getStringFromInputStream(post.getResponseBodyAsStream());
                               System.out.print(response);
                              
                               } finally {
                               post.releaseConnection();
                               }
                              
                               } catch (Exception e) {
                               e.printStackTrace();
                               }
                               }



                              • 12. Re: JAX-RS client support
                                maeste

                                 

                                "jervisliu" wrote:
                                How about the code below. Does this example have any value added comparing to a standard JAX-RS client based on HTTPClient?


                                Not much at a first look. But my humble opinion is that Wise could be cool providing a single API to call both JAX-WS and JAX-RS.
                                Where could we provide more added values? using JAXB or XSD validation to ensure contract is respected when there is an XSD. Provide freemarker template to for JSON and ATOM to make simplier to write it (to be inverstigated a little)
                                And let me remark that Wise implementation would simplify integration of JAX-RS client into ESB.

                                Am I wrong?

                                • 13. Re: JAX-RS client support
                                  maeste

                                  I've just committed on "WISE-34" branch, changes to add APIs support for JAX-RS.
                                  The related issue to see related commit is WISE-82
                                  https://jira.jboss.org/jira/browse/WISE-82?page=com.atlassian.jira.ext.fisheye%3Afisheye-issuepanel

                                  I've also tried to write understandable javadoc to explain my intention in this APIs design.
                                  Please take a look and give me your feedback and/or get code and change it where I'm totally wrong.

                                  • 14. Re: JAX-RS client support
                                    jervisliu

                                    I looked into WISE-34 branch, most JAX-RS stuff are not there yet, but no worries, I can work this out. I will start to commit code to this branch as long as we agree with the following API:

                                    RSDynamicClient client = RSDynamicClientFactory.getInstance().getClient();
                                     RSMethod method = client.getMethod("http://localhost:80/bookservice/books/123", RSDynamicClientFactory.HTTP_POST);
                                     method.setRequestHeader("Content-Type", "application/xml");
                                    
                                     InternalObject internal = new InternalObject();
                                     internal.setNumber(new Integer(1));
                                     internal.setText("aa");
                                     ExternalObject external = new ExternalObject();
                                     external.setDate(new Date());
                                     external.setInternal(internal);
                                     // without smooks debug infos
                                     InvocationResult result = method.invoke(external, new SmooksMapper("./smooks-config-XMLGregorianCalendar.xml"));
                                     System.out.println(result.getMappedResult(new SmooksMapper("./smooks-response-config.xml"), null));


                                    And the underlying implementation will be sth equal to a HTTPClient based implementation like below:

                                    public static void main( String[] args ) {
                                     try {
                                    
                                     //the request is a Java object
                                     InternalObject internal = new InternalObject();
                                     internal.setNumber(new Integer(1));
                                     internal.setText("aa");
                                     ExternalObject external = new ExternalObject();
                                     external.setDate(new Date());
                                     external.setInternal(internal);
                                    
                                     //JAVA to XML using Smooks
                                     Smooks smooks = new Smooks("smooks-config.xml");
                                     ExecutionContext executionContext = smooks.createExecutionContext();
                                     StringWriter writer = new StringWriter();
                                    
                                     // Configure the execution context to generate a report...
                                    
                                     executionContext.setEventListener(new HtmlReportGenerator("target/report/report.html"));
                                    
                                     // Filter the message to the result writer, using the execution context...
                                    
                                     smooks.filter(new JavaSource(external), new StreamResult(writer), executionContext);
                                    
                                     String request = writer.toString();
                                    
                                    
                                     //Using HTTPClient
                                     String endpointAddress = "http://localhost:9080/repository/artifacts";
                                    
                                     PostMethod post = new PostMethod(endpointAddress);
                                     post.setRequestEntity(new StringRequestEntity(request, "application/atom+xml", null));
                                    
                                     HttpClient httpclient = new HttpClient();
                                    
                                     try {
                                     int result = httpclient.executeMethod(post);
                                     String response = getStringFromInputStream(post.getResponseBodyAsStream());
                                     System.out.print(response);
                                    
                                     } finally {
                                     post.releaseConnection();
                                     }
                                    
                                     } catch (Exception e) {
                                     e.printStackTrace();
                                     }
                                     }


                                    1 2 Previous Next