09:04 AM, Thursday, August 31, 2006

JAX-WS for RESTful Web Services?

This post is in response to Sameer Tyagi's article on using JAX-WS for RESTful web services posted at the Sun Developer Network. I find a few points stated in this article troubling if not inaccurate - (a) it presents REST in a limited scope thereby showing SOAP in better light, and (b) it misses some key differences between REST and SOAP based web services. Beyond this, the example cited in this article just shows why you SHOULD NOT use JAX-WS for REST style web services. In this post, I would like to discuss the whys behind the two statements I just made.

Let me first list each point mentioned about when to use REST and when to use SOAP, and comment on each. The statements in italics below are from this article.

  1. Use REST when the web services are completely statelesss.

    This article contents that REST is suited for stateless services. To the contrary, most people prefer keeping SOAP style web services stateless. I also find it easier to manage state over REST based services than SOAP based services. With REST based services, you can either manage state with cookies or with the state encoded in the URI. With SOAP based services, the only option is to encode the state within the envelope.

  2. Use REST when bandwidth is particularly important and needs to be limited.

    REST based web services may be less-bloated. That may or may not turn into better performance - it depends on the network and the latency.

  3. Use REST for web service delivery or aggregation into existing web sites ... using technologies such as Asynchronous JavaScript with XML (AJAX).

    This point is a stretch for two reasons. It is equally possible to invoke SOAP style services via XMLHttpRequest. There are pros and cons to each approach. Secondly, I would not use JAX-WS for driving Ajax responses as using JSON over XML has several advantages and JAX-WS is not meant for anything other than XML.

  4. Use REST when the service producer and service consumer have a mutual understanding of the context and content being passed along and use SOAP when a format contract mus be established to describe the interface that the web service offers.

    Both the statements are misleading. I think these statements are based on the assumption that WSDL and schemas provide such a contract for SOAP based web services. This is only half-true. WSDL and schemas can describe the structure of messages (data, headers, faults etc.), but you still need to describe the semantics of operations and messages outside the WSDL. Without a description of those semantics, it is not difficult for different people to come up with different interpretations. WSDL and schemas don't describe the semantic meaning.

  5. Use SOAP when The architecture must address complex nonfunctional requirements such as Transactions, Security, Addressing, Trust, Coordination, and so on that go beyond simple CRUD operations possible with REST.

    This article contends that real enterprise applications need more than the CRUD operations that are possible with HTTP. Actually, this article starts by saying how HTTP is related to SQL for CRUD capabilities. This is nothing but a mis-interpreation of HTTP. HTTP defines some standard verbs with defined semantics. There are two ways you can use HTTP for REST style services - by encoding the operations in the URI, and/or by defining new verbs. This gives a wide range of possibilities for surfacing services - definitely not limited to CRUD. In any case, how is this better than encoding the operations in the header/body of a SOAP message and send all messages via POST?

    Regarding the non-functional requirements such as Transactions, Security, Addressing, Trust, and Coordination, except for security, we are yet to see any one using the others seriously today. Who said security cannot be addressed with RESTful web services?

What this article forgets to mention is that RESTful services don't hide the fact that they are operating over HTTP, where as SOAP style services try to hide this and encourage protocol agnosticism. The result of this protocol agnosticism is API bloat with too many indirections. I have been developing web services for over three years, and I don't have problems with the lower level APIs (SAAJ, DOM etc.) - what frustrates me is the bloat added by the higher-level APIs (dispatchers, handlers, XML binding etc.) trying to hide the protocol.

JAX-WS for RESTful Services?

JAX-WS (like its dead-cousin JAX-RPC) is designed with protocol abstraction in mind - so they carry the bloat I just mentioned. The API goes on to great trouble in hiding the protocol with layer upon layer - may be the thought was if there are too many layers on top of the protocol, nobody would notice the protocol?

Yes, you can use JAX-WS for REST style services, but as I show below, using JAX-WS makes your code inherit some of that bloat. It adds more complexity without necessarily offering any advantages. Note that the example in this article uses JAXB in combination with JAX-WS, and my comments are limited to using JAX-WS.

First, let me briefly describe the example provided in this article.

  1. A purchase order service to get, create, update, or delete purchase orders. This service is implemented using the javax.xml.ws.Provider interface and some annotations of JAX-WS. The invoke() of this class method processes the request by first checking the request verb and the path of the URI used to make the request.
  2. A client that uses the javax.xml.ws.Service and javax.xml.ws.Dispatch interfaces of JAX-WS to talk to the purchase order service.

This example is simple, yet demonstrates the bloat very well. Let's see the bloat.

  1. The servlet API is best suited for providing an entry point for REST-style services. The API has the right abstractions for serving services over HTTP. But since JAX-WS wants to stay protocol agnostic, there is no way you can bind JAX-WS provider classes to servlets. But still JAX-WS leaks some protocol details via the javax.xml.ws.handler.MessageContext (which IMO, makes JAX-WS more usable than JAXRPC). So, this example ends up using the leaks to get details of the request API.

        public Source invoke(Source source) {
            try{
                MessageContext mc = wsContext.getMessageContext();
                String path = (String)mc.get(MessageContext.PATH_INFO);
                String method = (String)mc.get(MessageContext.HTTP_REQUEST_METHOD);
                System.out.println("Got HTTP "+method+" request for "+path);
                if (method.equals("GET")) 
                    return get(mc);
                if (method.equals("POST"))
                    return post(source, mc);
                if (method.equals("PUT"))
                    return put(source, mc);
                if (method.equals("DELETE"))
                    return delete(source, mc);
                throw new WebServiceException("Unsupported method:" +method);
            } catch(JAXBException je) {
                throw new WebServiceException(je);
            }
        }
    

    The above code from this article makes a good case why you should servlet API and not JAX-WS. Since this service is bound to HTTP, this code had to obtain the request info directly via the leaked abstractions. Here is how you could rewrite this using the servlet API.

    // Nothing

    Except for message parsing, there is no need to write code to detect the verb. The good old javax.servlet.http.HttpServlet does this job, and would delegate to methods like doGet(), doPost() etc.

  2. The purchase order service writes back fault messages and response error codes. Here again, we see the SOAP concepts leaking into the REST-style purchase order service.

  3. The client side code is similarly more complex like the service implementation. The client first creates a Service, adds a Port, then creates a Dispatcher, creates a RequestContext, and then invokes the service.

        private  void retrieveOrder() {
            service = Service.create(qname);
            service.addPort(qname, HTTPBinding.HTTP_BINDING, url + "ABC-123");
            Dispatch dispatcher = service.createDispatch(qname, Source.class, Service.Mode.MESSAGE);
            Map requestContext = dispatcher.getRequestContext();
            requestContext.put(MessageContext.HTTP_REQUEST_METHOD, "GET");
            Source result = dispatcher.invoke(null);
            printSource(result);
       }
    

    There are too many abstractions in this code snippet. It uses APIs designed for SOAP and WSDL to implement REST-style invocations. But why not use HttpURLConnection for the same task?

        // pseudo code
        URL url = new URL(...);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();	
        connection.setDoInput(true);
        connection.setRequestMethod("GET");
        // ...
        InputStream is = connection.getInputStream();
        // Read and bind to a JAXB object
    

    This code is not trivial either, but you are atleast dealing with HTTP directly - no SOAPy abstractions in the way of doing REST.

Due to the fact that JAX-WS hides HTTP, you will also notice that neither the client nor the service can easily deal with things like headers and cookies. This article makes a good point that REST style services can take advatange of HTTP caching, but unfortunately, JAX-WS won't let you do it easily.

My Conclusion

In summary, this articles makes an attempt to justify that JAX-WS can be used for programming REST-style web services. It should also have taken the time to justify why you must use JAX-WS as opposed to, let's say, servlets and a HTTP client?

If the JAX-WS API is really intent on supporting REST-style programming, here are some the things that it needs to do

  1. Provide an easier way to process XML and JSON messages on the server side without hiding HTTP, preferrably by extending the servlet API.
  2. Provide an wasier way to write HTTP client by creating some light-weight abstractions on the top of HttpURLConnection so that writing XML to, and reading XML from HTTP connections is simplified.

As of today, I would not recommend using JAX-WS for REST-style web services.

Update (09/03/2006): I just came across Mark Baker's interview at http://www.infoq.com/articles/mark-baker-REST. In response to a question on combining REST with more conventional web services approaches, he says it's technically possible, but from what I've seen of Web services toolkits (even those that claim to "support REST"), there's a very good chance that it'll be more trouble than it's worth. That says it all.

Comments

Sameer Tyagi said:

Hi.

Thanks for taking the time to read the article and post your review. I just happened to come across this link today so didn't have a chance to respond earlier. Would have been cool if you had posted a link to the above in the comments section on my blog.

Anyhow,

1. The intention was not to show either SOAP or REST in "better light" but to present developers with options. I m not really sure how you gathered that. The summary also states that clearly.

2. On your comment about REST+state. You can also do state management with cookies in SOAP based services. There are also specs like WS-Secure Conversation (see wsit.dev.java.net)
that combines security and state. You can also build stateful SOAP based web services using JAX-WS directly with the @Stateful annotation. See Kohsuke's blog [1] for details

3.On your comment about REST+performance. There are two kinds of performance, actual and perceived. Also when you use web services from resource constrained devices like
like smart phones, bandwidth becomes increasingly important.

4. On your comment about SOAP+XMLHttpRequest - It's possible but not easy. Also a-priori knowledge and parsing of the WSDL may be required.

5.On your comment about semantics - The WSDL and schemas do serve as a contract. In real world scenarios and large enterprise applications, it is extremely unlikely that a service consumer will not
know the semantics of the operations. Also most commercial web services management implementations that are used to encore SLA's and policies at the periphery use the wsdl
and schemas as the contract.

6. On your comment about crud - If I were to read your explanation, I walk away with the understanding that every possible problem in the web services space
can be solved by REST. This is nothing but untrue. There are actual viable use cases where the WS-* technologies and implementations like WSIT [2] for
interoperability across platforms play an important part.

7. On your comment about JAX-WS vs Servlets. You are right in that RESTful web services can be built with servlets, but what about the case for polymorphic processors.


8. On your comment about what Jax-WS needs to do for REST.
They are all very good suggestions. Could you possibly file RFE's for these on the jax-ws project page [3] so that the team can start looking at them more closely.


/s

[1]http://weblogs.java.net/blog/kohsuke/archive/2006/10/stateful_web_se.html
[2]http://wsit.dev.java.net
[3]http://jax-ws.dev.java.net

Leave a comment