The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








I heart FeedBurner

Latest Diggs - Programming

Computers Blogs - Blog Top Sites

Site Hits

Total: 4,868,426
since: 19 Jan 2005

Know thine Enemy: RESTful Web Services in ... Java!

posted Sun 11 Jan 09

Any good debater knows that you can't be prepared to defend your own argument unless you know the pros and cons of your opponent's argument. Likewise, as an architect, you can't realistically recommend one platform over another without having learned everything you can both about the platform you selected and the platform you rejected. One of my pet peeves involves people who kneejerk respond to subjective (and naive) questions like, "Which platform is better?" with their favorite platform, followed by a littany of reasons why the other platform sucks. The problem is, their reasons are usually full of crap and you end up listening to someone who sounds like a preschooler talking about why "Girls have cooties". Replace "Girls" with "Microsoft" and you get the crowd of MS-bashers. Replace "girls" with "Java" and you get a large number of C# progammers who are zealous in their belief that C# is the best language and .NET is the best platform on the planet for all applications and all requirements.

The truth is more fuzzy. Certain platforms are better at some things than others. XAML makes declarative UI building easier than Interface Builder, but IB makes loosely coupling the components in an MVC style app much easier than WPF. Can you actually say "XX is better" ? No, it has to be qualified.

So I was looking back at my RESTful samples the other day and I realized that I'd done some samples on WCF-based services, both app-hosted and IIS-hosted. I'd also done a sample on how to turn an MVC controller into a RESTful service and it's view into a declarative POX rendering. I noticed something missing - no Java!

So here it is folks, with or without popular demand: a quick cliff notes tour through how to do a RESTful Web Service in Java, from the perspective of an open-minded .NET addict.

The first thing that confused me in this endeavor was the amount of choice. In .NET, for nearly every task you can imagine, you can either use the built-in Microsoft frameworks, or there is an extremely short list of stand-out implementations and everyone seems to know what the defacto standards are. Not so with Java. Apparently there are at least a hojillion stacks for doing Web Services... so I went looking for the one that had the simplest-looking syntax and was the most familiar to me as a WCF developer. I ended up finding JAX-WS... which I guess is part of the standard Java stack as of SE 1.6, I didn't need to go to some open source site, download the source code, then spend the next 4 hours figuring out how to compile it before I even get to play with it. IMHO, that practice is like triple-wrapping Christmas presents in duct tape: If you want people to use your stuff, at least make it easy to unwrap dammit!

So here I am with my decision to use JAX-WS. First thing I needed to do was define the service contract. I realize that I'm using REST, but the way in which data is serialized onto the wire in XML is part of the contract. There may be no WSDL, but even with RESTful POX you need to agree on what your payloads look like. So here's a sample Java class that uses some annotations to inform JAXB how it gets serialized as XML (yes, I am modeling a hamburger service).

package com.dotnetaddict.blog;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;

@XmlRootElement
public class Hamburger {

@XmlAttribute
    public int calories; 

@XmlAttribute
    public String burgerName;
}

Now that I've got my individual burger type defined, I needed to define the burger collection type, which is basically just a wrapper so that I can define the XML element name for the root output of the service:

package com.dotnetaddict.blog;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;

@XmlRootElement(name="Burgers")
public class BurgerList {
    @XmlElement(name="Burger")
    public final List<Burger> burgerList = new ArrayList<Burger>();
}

If you've ever used the .NET attributes for providing hints to the XML serializer, then you'll notice that these attributes are nearly identical in form, function, and even in name.

Now we need to create an implementation of a provider for the endpoint. This is basically a class that has methods on it that respond to HTTP messages. You write your own quick dispatcher based on the method (GET, PUT, POST, DELETE) and then inside each method handler, you examine the path (e.g. "/allburgers" or "/lowfatburgers" etc) and perform the appropriate action. This is where the pattern deviates a little from the .NET world. With WCF, you simply provide a UriTemplate annotation, er, attribute, on the top of each method in your service. This attribute tells WCF what URL pattern is handled by the method below, and whether it handles GET, PUT, POST, or DELETE. This attribute also automatically chunks parameters out of the URL path and converts them into method parameters. So, in this manner, the main difference between JAX-WS and WCF is that with JAX-WS, you are writing your own dispatching and URL parsing logic. For a standard URL without much complexity, this means the difference of about 15-20 lines of code.

So, here's a stubbed out endpoint implementation:

package com.dotnetaddict.blog;

// imports go here...

@WebServiceProvider
@ServiceMode(value=Service.Mode.MESSAGE)
public class BurgerEndpoint implements Provider<Source> {
    @Resource
    private WebServiceContext wsContext;
   
    private BurgerList allBurgers = new BurgerList();
    private JAXBContext jc;
   
    public BurgerEndpoint()
    {
        try {
            jc = JAXBContext.newInstance(BurgerList.class);
        } catch (JAXBException je)
        {
            throw new WebServiceException("Cannot create JAXBContext", je);
        }
// somehow get list of burgers.. could be from database, or here it's just fake.
        Burger b = new Burger();
        b.calories = 1000;
        b.burgerName = "Teh Woppa";
        burgerList.Add(b);
    }
   
    public Source invoke(Source source)
    {
        MessageContext mc = wsContext.getMessageContext();
        String path = (String)mc.get(MessageContext.PATH_INFO);
        String method = (String)mc.get(MessageContext.HTTP_REQUEST_METHOD);
        System.out.println("[Burgers] HTTP " + method + " request for " + path);
        try {
            if (method.equals("GET"))
                return get(source,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);
        } catch (JAXBException je)
        {
            throw new WebServiceException(je);
        }
        throw new WebServiceException("Unsupported HTTP Method "+method);
    }
   
    private Source get(Source source, MessageContext mc) throws JAXBException
    {
        String path = (String)mc.get(MessageContext.PATH_INFO);
   
        if (path != null && path.equals("/all"))
            return getAllBurgers();
        if (path != null && path.equals("/lowfat"))
            return getLowfatBurgers();
      
        return null;
    }
   
    private synchronized Source getAllBurgers() throws JAXBException {
        return new JAXBSource(jc, burgerList);
    }
   
    private synchronized Source getLowfatBurgers() throws JAXBException {
        return new JAXBSource(jc, burgerList); // fool the public!       
    }
    // put, delete, post implementations
}

So now we have our serialization object placeholders and we've got our endpoint implementation. The next thing we need to do is publish the service. We can do that by either publishing to Tomcat, Glassfish, or some other container... or we can just self-host it in a manner that is VERY (some would say uncannily, perhaps eerily) similar to the way WCF does it.

package com.dotnetaddict.blog;

import javax.xml.ws.*;
import javax.xml.ws.http.*;

public class ServicePublisher {

    public static void main(String[] args) throws Exception {
        String burgerAddress = "http://localhost:6666/burgers";
       
        Endpoint burgerEndpoint = Endpoint.create(HTTPBinding.HTTP_BINDING, new BurgerEndpoint());
        burgerEndpoint.publish(burgerAddress);
       
//        This is just a hack so we don't stop hosting. I took this from some sample code I found somewhere...
//        I'm not a Java guy so I don't know what would be better to put here..
        Thread.sleep(10000000L);
    }
}

And so that's that. A RESTful (however trivial) Web Service implemented in Java. The big take-aways for me were:

  • It's just about as easy to make a RESTful Web Service in Java (if you pick the RIGHT stack... there are others that seem overly complex or are not as intuitive as JAX-WS or Axis 2) as it is in WCF
  • JAX-WS RESTful services require the developer to parse their own RESTful URLs. For simple URLs this can feel like a burden, but for more complex URL patterns that don't conform to the way WCF's UriTemplate works, this actually could be a bonus.
  • You can self-host a RESTful service (JAX-WS) in Java just as easily as in WCF
  • Deploying to a container is brainless and simple... potentially easier and less error-prone than hitting the VS 2008 "Publish" button.
  • I can write all of this on my Mac and expect it to work on a production server that is Windows, Linux, or whatever. That tickles my geek bone. 
  • Anybody who immediately dismisses competing technology without first learning about that technology is a zealot, not an architect.

tags:                    

links: digg this    del.icio.us    technorati    reddit

AddThis Social Bookmark Button




1. leeg left...
Sun 11 Jan 09 8:48 am :: http://iamleeg.blogspot.com

"One of my pet peeves involves people who kneejerk respond to subjective (and naive) questions like, "Which platform is better?" with their favorite platform, followed by a littany of reasons why the other platform sucks." ... "It's just about as easy to make a RESTful Web Service in Java (if you pick the RIGHT stack... there are others that suck)"

:-)


2. Kevin Hoffman left...
Sun 11 Jan 09 8:55 am

Was wondering if anyone was going to catch that one. By "others that suck" I am referring to the relative complexity of code required to produce the same result. I looked at Axis 2.0 and it seemed like a perfectly decent stack, but I found the code for a standard RESTful web service to be less easy to read and much less easy to infer URL format. I'll re-word the blog post so that instead of "suck" it mentions the one objective point about relative code complexity and size.


3. Craig left...
Sun 11 Jan 09 2:16 pm :: http://www.typemismatch.com/

Want to do it one more time in Rails? :) Would seem pretty straight forward too.


Tag Related Posts

Building RESTful Java Web Services with JAX-RS

Mon 23 Feb 09 8:42 P GMT-05
tags:                    

Upgrading your Leopard install to Java SE 6 64-Bit

Mon 12 Jan 09 1:38 P GMT-05
tags:            

Know thine Enemy: RESTful Web Services in ... Java!

Sun 11 Jan 09 2:08 P GMT-05
tags:                    

Enterprise Web Services Manifesto - Wire Formats

Tue 02 Sep 08 1:43 P GMT-05
tags:                

CLINQ v1 Demo - Network Message Filtering

Wed 09 Jan 08 7:47 P GMT-05
tags:        

LINQ to REST - A much better name for Astoria

Tue 11 Dec 07 1:23 P GMT-05
tags:        

Orcas' Hidden Gem - The managed PNRP stack

Fri 11 May 07 6:45 P GMT-05
tags:        

On MUDs

Thu 08 Mar 07 5:00 A GMT-05
tags:                    

What I think is a bug in WCF POX messaging

Thu 04 Jan 07 4:58 P GMT-05
tags:      

Ulysses Agenda Makes Redmond Developer News

Wed 29 Nov 06 7:10 P GMT-05
tags:                

Ulysses Agenda - Network Engine Test 1

Mon 09 Oct 06 2:26 A GMT-05
tags:              

Ulysses Agenda : First Cut Networking Design

Thu 14 Sep 06 12:46 A GMT-05
tags:                  

First Impressions of Windows Vista RC1

Thu 07 Sep 06 1:30 P GMT-05
tags:                      

.NET Framework 3.0 June CTP is out!

Fri 23 Jun 06 6:23 P GMT-05
tags:                

Tech-Ed 2006 - Session Reviews

Tue 13 Jun 06 6:22 P GMT-05
tags:                    

SOA Manifesto

Thu 26 Jan 06 10:38 P GMT-05
tags: