Tuesday Oct 30, 2007

Testing TLC

Last week i spent some time giving some needed TLC to Jersey's unit test infrastructure.

There was some really dreadful crufty code hacked together (the kind that you write in 5 minutes just to try something out and and you say i will change it later but somehow you never do and it gets used more and you spend silly amounts of time dealing with it) that was used to enable the unit testing of resource classes in-memory without depending on a HTTP container.

In addition there were unit tests using the light weight HTTP server, which used crufty HttpURLConnection code.

So there were two different sets of crufty code test infrastructure essentially doing the same thing, namely making HTTP-based client requests. Finally i got so fed up with this state of affairs i decided to do something about it.

The result is a simple RESTful client side API for making HTTP requests and processing HTTP responses that reuses classes and concepts from the JAX-RS API and Jersey. See here for a unit test that tests accept processing in memory, and see here for a unit test that tests matrix parameters using the Light Weight HTTP server. Notice that once a ResourceProxy has been obtained the code uses the same API.

Now that there is a common client-side API for making HTTP requests the next step is to further abstract out the configuration/deployment mechanism such that we can have one way of defining a unit test for testing one or more resource classes and be able to deploy them in any container (in-memory, LW HTTP server, or servlet).

Currently this client side API is part of the Jersey implementation at this location (and not the Jersey API). It was very instructive (and also very tedious!) to convert all the relevant unit tests over to using this API as it resulted in many little improvements and ideas to make further improvements (so it can be considered somewhat battle tested in the context of being used as a testing infrastructure API). Once those further improvements have been applied i plan to document it and move it over to the Jersey API for general use. But, if you are feeling brave you can still use it in the latest release. If you do let me know what you think.

While on the subject of testing TLC i watched a great interview on the Scoble show with ZFS inventors Jeff Bonwick and Bill Moore. At some point during the interview Jeff mentions that the testing code coverage of ZFS is over 90% (i cannot recall the exact number, it could be close to 99%) and it allowed them to make major changes to the ZFS code base without the fear of not knowing they had broken something. That point really resonated with me as i have found the Jersey unit tests have given me the confidence to make major internal changes (soon we will need to do some major refactoring of the URI dispatching). However, the code coverage of the Jersey unit tests is not at 99%. A Jersey release is built, using Hudson, every time the source code changes and that release is tested by running the unit tests. Emma code coverage is integrated and below is the trend graph generated by Hudson:

 

As you can see some more TLC is required to increase the code coverage. I have been told that the code coverage is not bad for a newish project, but still it makes me very nervous that at least 40% of methods are not being tested. Even if Jersey is early access we should strive for the highest quality possible for stable releases.

Friday Sep 21, 2007

Ohloh API

I noticed that Ohloh has REST-based API to GET representations in XML. Looks like a great start. The URI structure looks good and you are presented with URIs like the following:

http://www.ohloh.net/projects/{project_id}/factoids/{factoid_id}.xml 
http://www.ohloh.net/projects/{project_id}/factoids/{factoid_id}.xml
http://www.ohloh.net/projects/{project_id}/factoids.xml

But, from the perspective of a consumer, this API could be improved:

  1. The <response><status>...</status> fragment at the beginning of a representation is redundant and forces the client to unnecessarily process additional XML. Instead document the HTTP status codes. The HTTP status code 200 (OK) is sufficient for signaling success so rather than the root element being <response> it could be <projects> when getting a collection of projects and <project> when getting a project. The HTTP status code 400 (Bad Request) is sufficient for signaling a client error with an appropriate machine processable representation detailing the error.
  2. URIs are not present in the representations. This forces the client to construct URIs from identifiers in the representation, for example <project><id>1</id>...</project>, which increases the coupling between the client and server and makes it harder for the server to evolve the URIs (if need be) without breaking existing clients.

Monday Jul 16, 2007

BBC Web API (beta)

Recently i took a look a the BBC Web API as i was curious to see how "webby" and RESTful or not it was.

In the RESTful Web services book Leonard and Sam, in section 1.5, provide some descriptions of competing Web service architectures. One architecture described (in section 1.5.3) is the REST-RPC hybrid architecture and although the documentation for the API makes no claims whether it be RESTful or RCPful or not i think the API fits firmly into the REST-RPC hybrid category.

The following is the URI used to GET the information of the BBC One channel:

http://www0.rdthdo.bbc.co.uk/cgi-perl/api/query.pl?method=bbc.channel.getInfo&channel_id=BBCOne&format=simple

Note the scoping information in the URI, as the query parameter method, but also the method information in the URI too using the same query parameter:

method=bbc.channel.getInfo.

One way to find out that something is not quite as RESTful as it can be is lop off all the query parameters and perform a GET on the URI and see if it results in an error. If an error occurs it can be an indication of an RPC-based architecture that is focused on URIs as endpoints.

Since the BBC Web API is read only it can be considered RESTful. But, as soon one moves to a read/write API (for example the BBC could offer a way for a user to annotate programs or post/delete comments) then things can start to get problematic (like using GET for deletion).

Furthermore, IMHO, URIs for REST-RPC hybrid architecture are fairly ugly. They tend to become cleaner and more intuitive if the scoping information is placed in the URI path, and therefore cooler. In fact it is very easy to implement the BBC Web API using the JSR 311 API and turn it into a cooler more RESTful Web service.

Consider the channel package that has three methods:

  1. bbc.channel.getInfo
  2. bbc.channel.getLocations
  3. bbc.channel.list

Instead the following URI paths could be used (respectively):

  1. channel/{id} 
  2. channel/{id}/locations
  3. channel 

and an example URI could be:

http://www0.rdthdo.bbc.co.uk/api/channels/BBCOne?format=simple

Notice that the method and channel_id parameters are no longer required. In JSR 311 we could write the following resource to support channels:

@UriTemplate("channel")
class ChannelResource {
    @HttpMethod
    Channels getList(@QueryParam("format") String format) { ... }

    @UriTemplate("{id}")
    @HttpMethod
    Channel getInfo(@QueryParam("format") String format,
        @UriParam("id"} int channelId) { ... }

    @UriTemplate("{id}/locations")
    @HttpMethod
    Locations getLocations(@QueryParam("format") String format,
        @UriParam("id"} int channelId) { ... }
}

assuming that Channels/Channel/Locations types are serializable by the 311 runtime. The same pattern can apply for genre, group and programme packages. The resource could be further improved by removing the query parameter format and instead using content types:

@UriTemplate("channel")
class ChannelResource {
@ProduceMime("application/simpletv+xml")
    @HttpMethod
    Channels getList() { ... }

         @ProduceMime("application/tvanytime+xml")
@HttpMethod
Channels getList() { ... }

...

     }

The 311 expert group is also considering support for the following:

@UriTemplate("channel")
@ProduceMime(["application/simpletv+xml", 
    application/tvanytime+xml"])
class ChannelResource {
    @HttpMethod
    Channels getList() { ... }

...

     }

such that the serializer for Channels is capable of understanding how to serialize in both the simple and TV-Anytime representations, and thereby, reducing the duplication of HTTP methods. While on the topic of the representations... unfortunately the API is missing an important REST constraint, which is "hypermedia is the engine of application state" or more concisely "connectedness", but to get into that detail requires another blog entry.

About

sandoz

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today