<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Jason R Briggs &#187; REST</title>
	<atom:link href="http://www.briggs.net.nz/log/tag/rest/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.briggs.net.nz/log</link>
	<description>Techie stuff from the perspective of a Kiwi abroad</description>
	<lastBuildDate>Mon, 28 Jun 2010 06:45:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Units of Work</title>
		<link>http://www.briggs.net.nz/log/2009/03/20/units-of-work/</link>
		<comments>http://www.briggs.net.nz/log/2009/03/20/units-of-work/#comments</comments>
		<pubDate>Fri, 20 Mar 2009 22:59:26 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[commentary]]></category>
		<category><![CDATA[miscellaneous]]></category>
		<category><![CDATA[technical]]></category>
		<category><![CDATA[distributed]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[transactions]]></category>

		<guid isPermaLink="false">http://www.briggs.net.nz/log/?p=666</guid>
		<description><![CDATA[In regard to cross-resource transactions, I&#8217;m a member of the camp that wonders whether distributed transactions are strictly necessary in the REST/HTTP world; indeed I wonder whether they represent a design failure in modeling the granularity of your resources. That said, we don&#8217;t live in a perfect world &#8212; and even if I can&#8217;t envisage [...]]]></description>
			<content:encoded><![CDATA[<p>In regard to <a href="http://www.jboss.org/community/docs/DOC-13311">cross-resource transactions</a>, I&#8217;m a member of the <a href="http://www.innoq.com/blog/st/2009/03/restful_transactions.html">camp</a> that wonders whether distributed transactions are strictly necessary in the REST/HTTP world; indeed I wonder whether they represent a design failure in modeling the granularity of your <a href="http://rest.blueoxen.net/cgi-bin/wiki.pl?RestResources">resources</a>.</p>
<p>That said, we don&#8217;t live in a perfect world &#8212; and even if I can&#8217;t envisage why a properly designed RESTful application might require access to a distributed transaction, I can certainly envisage the environment where such an application might evolve.  I&#8217;ve worked in a few of them.  Places where interdepartmental barriers are as solid as the Great Wall; bastions of archaic technology where <em>&#8220;one might provide a pseudo-RESTful interface, but one certainly won&#8217;t be re-architecting one&#8217;s legacy system in the buzzword language of the day&#8221;</em>. </p>
<p>But, I think there&#8217;s a certain amount of smoke and mirrors in the JBossTS article &#8220;<a href="http://www.jboss.org/community/docs/DOC-13311">Transactional support for JAX RS based applications</a>&#8220;:</p>
<p><em>Certainly it is worth pointing out that if a system cannot be made reliable then it can be of only limited utility. That said it is a worthwhile exercise to show how a REST based system can be made reliable. </em></p>
<p>Lack of distributed transactions would hardly seem to make a REST based system &#8220;unreliable&#8221; and, as a consequence, of &#8220;only limited utility&#8221;.  Imagine a hotel booking facility &#8212; perhaps a <em>booking</em> resource, which internally might be constructed from a number of components, all governed (again internally) by transaction demarcation.  Does the fact that the booking resource is coarse-grained and does not require an external transaction make it less reliable than a number of fine-grained resources which do?  On the contrary. The latter sounds more like a WS-* api than a RESTful architecture, nothing to do with reliability.</p>
<p>So&#8230; hopefully it&#8217;s obvious I think it&#8217;s a bad idea.  But if I <strong>were</strong> to write such an API, I think the 8-year old spec mentioned in the article falls short of the mark.  Here&#8217;s my first-cut attempt at an alternative (which I still think falls a bit short of the mark, but is possibly an improvement):</p>
<table>
<tr>
<th colspan="5">Resource: tc</th>
</tr>
<tr>
<th>Method</th>
<th>URL</th>
<th>Content</th>
<th>Description</th>
<th>Statuses</th>
</tr>
<tr>
<td>GET</td>
<td>/tc</td>
<td></td>
<td>Returns HTML containing a summary of all transactions (status), plus an href to the transaction detail</td>
<td>200 &#8211; ok</td>
</tr>
<tr>
<td></td>
<td>/tc/{txid}</td>
<td></td>
<td>Returns HTML containing detail for the transaction with id {txid}, including the status and a list of hrefs to the each of the participants. For example:</p>
<pre><code>
&lt;html&gt;
&lt;body&gt;
	&lt;dl&gt;
		&lt;dt&gt;Transaction ID&lt;/dt&gt;
			&lt;dd id="transaction-id"&gt;12345a&lt;/dd&gt;
		&lt;dt&gt;Status&lt;/dt&gt;
			&lt;dd id="transaction-status"&gt;ACTIVE&lt;/dd&gt;
		&lt;dt&gt;Timeout&lt;/dt&gt;
			&lt;dd id="transaction-timeout"&gt;5000&lt;/dd&gt;
	&lt;/dl&gt;
	&lt;ul id="participants"&gt;
		&lt;li&gt;
			&lt;a href="/tc/12345a/participants/1"&gt;
				Participant 1
			&lt;/a&gt;
		&lt;/li&gt;
		&lt;li&gt;
			&lt;a href="/tc/12345a/participants/2"&gt;
				Order
			&lt;/a&gt;
		&lt;/li&gt;
	&lt;ul&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
</td>
<td>200 &#8211; ok<br/><br />
404 &#8211; if txid is not found<br />
409 &#8211; if the transaction has been deleted</td>
</tr>
<tr>
<td></td>
<td>/tc?status={status-type}</td>
<td></td>
<td>Return HTML containing a summary of transactions with a specific status, with href to the transaction detail<br />
For example: /tc?status=recovering or /tc?status=active</td>
<td>200 &#8211; ok</td>
</tr>
<tr>
<td></td>
<td>/tc/{txid}/participants</td>
<td></td>
<td>Return a list of participants in the transaction (list of hrefs)</td>
<td>200 &#8211; ok<br />
404 &#8211; if the transaction does not exist</td>
</tr>
<tr>
<td></td>
<td>/tc/{txid}/partipants/{rec-coord-id}</td>
<td></td>
<td>Return HTML containing the detail of a participant.  For example:</p>
<pre><code>
&lt;html&gt;
&lt;title&gt;Participant #2&lt;/title&gt;
&lt;body&gt;
&lt;dl&gt;
    &lt;dt&gt;ID&lt;/dt&gt;
        &lt;dd&gt;2&lt;/dd&gt;
    &lt;dt&gt;Name&lt;/dt&gt;
        &lt;dd&gt;Order&lt;/dd&gt;
    &lt;dt&gt;URL&lt;/dt&gt;
        &lt;dd&gt;
            &lt;a href="http://internal.mydomain.com/someresource/123"&gt;Order&lt;/a&gt;
        &lt;/dd&gt;
&lt;/dl&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
</td>
<td>200 &#8211; ok<br />
404 &#8211; if the transaction or participant does not exist</td>
</tr>
<tr>
<td>POST</td>
<td>/tc</td>
<td>[timeout={timeout}]</td>
<td>Start a transaction (with default timeout) returning the url /tc/{txid} &#8212; which is deleted after the timeout or after completion (any HTTP method relating to {txid} thereafter returns 404). Use timeout={timeout} to override the default timeout period.</td>
<td>201 &#8211; created</td>
</tr>
<tr>
<td>DELETE</td>
<td>/tc/{txid}</td>
<td></td>
<td>Rollback and stop a transaction</td>
<td>204 &#8211; ok<br />
404 &#8211; if the transaction does not exist</td>
</tr>
<tr>
<td></td>
<td>/tc/{txid}?commit</td>
<td></td>
<td>Commit and stop a transaction</td>
<td>204 &#8211; ok<br />
404 &#8211; if the transaction does not exist</td>
</tr>
<tr>
<td>POST</td>
<td>/tc/{txid}/participants</td>
<td>url={url}&#038;[name={name}]</td>
<td>Enlist {url} in the transaction, returning a unique resource for that participant of the form /tc/{txid}/participants/{rec-coord-id}. If name exists, record against the participant detail</td>
<td>201 &#8211; created<br />
404 &#8211; if the transaction does not exist</td>
</tr>
<tr>
<td>PUT</td>
<td>/tc/{txid}/participants/{rec-coord-id}</td>
<td>url={url}</td>
<td>Replace the participant url</td>
<td>200 &#8211; ok<br />
404 &#8211; if the transaction or participant does not exist</td>
</tr>
</table>
<p>The resource identified by a participant URL will have the following semantics:</p>
<table>
<tr>
<th>Method</th>
<th>URL</th>
<th>Content</th>
<th>Description</th>
<th>Statuses</th>
</tr>
<tr>
<td>PUT</td>
<td>URL/tx/{rec-coord-id}</td>
<td>action=prepare</td>
<td>The participant prepares any work done in the context of the transaction. The Warning header will contain additional info about the state of the prepare (either readonly, or notok).</td>
<td>200 &#8211; ok<br />
200 &#8211; ok (+ Warning: readonly)<br />
200 &#8211; ok (+ Warning: notok)<br />
404 &#8211; participant has rolled back</td>
</tr>
<tr>
<td></td>
<td></td>
<td>action=commit</td>
<td>The participant commits any work done in the context of the transaction.</td>
<td>200 &#8211; ok<br />
200 &#8211; ok (+ Warning: heuristic)<br />
404 &#8211; participant has rolled back</td>
</tr>
<tr>
<td></td>
<td></td>
<td>action=rollback</td>
<td>The participant commits any work done in the context of the transaction.</td>
<td>200 &#8211; ok<br />
200 &#8211; ok (+ Warning: heuristic)<br />
404 &#8211; participant has already rolled back</td>
</tr>
</table>
<p>Basic usage might look something like the following:</p>
<table>
<tr>
<td colspan="2">1. Create a new transaction resource</td>
</tr>
<tr>
<td>
<pre><code>POST /tc
Host: somedomain.com

timeout=5000
</code></pre>
</td>
<td>
<pre><code>HTTP/1.1 201 Created
Connection: close
Date: Thu, 19 Mar 2009 21:01:56 GMT
Location: http://somedomain.com/tc/10a23v991
X-Powered-By: TransactionServer/0.1
</code></pre>
</td>
</tr>
<tr>
<td colspan="2">2. Create a new resource of some kind (notify the resource that it will operate with a distributed transaction)</td>
</tr>
<tr>
<td>
<pre><code>POST /res1?tx
Host: internaldept1.somedomain.com

&lt;xml&gt;some xml describing the resource&lt;/xml&gt;
</code></pre>
</td>
<td>
<pre><code>HTTP1.1 201 Created
Connection: close
Date: Thu, 19 Mar 2009 21:01:56 GMT
Location: http://internaldept1.somedomain.com/res1/100
</code></pre>
</td>
</tr>
<tr>
<td colspan="2">3. Update another resource (again notify that it will be operating within a transaction)</td>
</tr>
<tr>
<td>
<pre><code>PUT /res2/somename?tx
Host: internaldept2.somedomain.com

&lt;xml&gt;some xml describing the update,
perhaps including a reference to the previously
created resource&lt;/xml&gt;
</code></pre>
</td>
<td>
<pre><code>HTTP1.1 200 Ok
Connection: close
Date: Thu, 19 Mar 2009 21:01:56 GMT
Location: http://internaldept2.somedomain.com/res2/somename
</code></pre>
</td>
</tr>
<tr>
<td colspan="2">4. Enlist the url for each resource in the transaction</td>
</tr>
<tr>
<td>
<pre><code>POST /tc/10a23v991/participants
Host: somedomain.com

name=resource1&amp;url=http://internaldept1.somedomain.com/res1/100
</code></pre>
</td>
<td>
<pre><code>HTTP/1.1 201 Created
Connection: close
Date: Thu, 19 Mar 2009 21:01:56 GMT
Location: http://somedomain.com/tc/10a23v991/participants/a01abgv21
X-Powered-By: TransactionServer/0.1
</code></pre>
</td>
</tr>
<tr>
<td>
<pre><code>POST /tc/10a23v991/participants
Host: somedomain.com

name=resource2&amp;url=http://internaldept2.somedomain.com/res2/somename
</code></pre>
</td>
<td>
<pre><code>HTTP/1.1 201 Created
Connection: close
Date: Thu, 19 Mar 2009 21:01:56 GMT
Location: http://somedomain.com/tc/10a23v991/participants/a01abgv22
X-Powered-By: TransactionServer/0.1
</code></pre>
</td>
</tr>
<tr>
<td colspan="2">5. Commit the transaction</td>
</tr>
<tr>
<td>
<pre><code>DELETE /tc/10a23v991?action=commit
Host: somedomain.com
</code></pre>
</td>
<td>
<pre><code>HTTP/1.1 204 Committed and deleted
Connection: close
Date: Thu, 19 Mar 2009 21:01:56 GMT
X-Powered-By: TransactionServer/0.1
</code></pre>
</td>
</tr>
<tr>
<td colspan="2">6. &#8216;Behind the scenes&#8217;, the commit results in the following (2-phase commit at this point)&#8230;</td>
</tr>
<tr>
<td>
<pre><code>PUT /res1/100/tx/a01abgv21
Host: internaldept1.somedomain.com

action=prepare
</code></pre>
<pre><code>PUT /res2/somename/tx/a01abgv22
Host: internaldept2.somedomain.com

action=prepare
</code></pre>
<p>and</p>
<pre><code>PUT /res1/100/tx/a01abgv21
Host: internaldept1.somedomain.com

action=commit
</code></pre>
<pre><code>PUT /res2/somename/tx/a01abgv22
Host: internaldept2.somedomain.com

action=commit
</code></pre>
</td>
<td></td>
</tr>
</table>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2009/03/20/units-of-work/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>when does REST become complex?</title>
		<link>http://www.briggs.net.nz/log/2007/02/17/when-does-rest-become-complex/</link>
		<comments>http://www.briggs.net.nz/log/2007/02/17/when-does-rest-become-complex/#comments</comments>
		<pubDate>Sat, 17 Feb 2007 08:32:59 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[commentary]]></category>
		<category><![CDATA[technical]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://www.briggs.net.nz/log/2007/02/17/when-does-rest-become-complex/</guid>
		<description><![CDATA[According to this: The great marketing coup that REST folks are trying to pull off is that WS-* is complex and REST is simple. That&#8217;s just nonsense &#8211; if you really try to build the type of systems that people build with WS-* using REST, then you&#8217;d end up just as complex. Who&#8217;s talking nonsense? [...]]]></description>
			<content:encoded><![CDATA[<p>According to <a href="http://www.infoq.com/articles/sanjiva-rest-myths">this</a>:</p>
<blockquote><p>The great marketing coup that REST folks are trying to pull off is that WS-* is complex and REST is simple. That&#8217;s just nonsense &#8211; if you really try to build the type of systems that people build with WS-* using REST, then you&#8217;d end up just as complex.</p></blockquote>
<p>Who&#8217;s talking nonsense?  So, you take something that is inherently complex and build a system with it &#8212; then you take something that is fundamentally simple and build a similar system with that &#8212; and the final results are the same in terms of complexity?</p>
<p>Mais non.  I don&#8217;t think so. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2007/02/17/when-does-rest-become-complex/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>simple REST testing tools</title>
		<link>http://www.briggs.net.nz/log/2006/08/06/simple-rest-testing-tools/</link>
		<comments>http://www.briggs.net.nz/log/2006/08/06/simple-rest-testing-tools/#comments</comments>
		<pubDate>Sun, 06 Aug 2006 20:55:11 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://www.briggs.net.nz/log/2006/08/06/simple-rest-testing-tools/</guid>
		<description><![CDATA[After a bit of googling, I have yet to find a command-line tool that simply handles HTTP requests. There&#8217;s wget for retrieving files, curl for PUT and POST, but what to use for DELETE (or other HTTP methods)? For that matter, I&#8217;d prefer to have a single tool, rather than 2 or 3. I wrote [...]]]></description>
			<content:encoded><![CDATA[<p>After a bit of googling, I have yet to find a command-line tool that <em><strong>simply</strong></em> handles HTTP requests.  There&#8217;s <a href="http://www.gnu.org/software/wget/">wget</a> for retrieving files, <a href="http://curl.haxx.se/">curl</a> for PUT and POST, but what to use for DELETE (or other HTTP methods)?  For that matter, I&#8217;d prefer to have a single tool, rather than 2 or 3.</p>
<p>I wrote a simple python script a while back &#8212; the last time I couldn&#8217;t find such a tool &#8212; and so now that I need to use it again, it seems like a good time to polish it up and release it (<a href="http://www.briggs.net.nz/log/products/wcall">here</a>).  Not before chopping out a bunch of specific hacks that really need tidying up before being included in released code, of course.</p>
<p>So if you need a basic tool for handling HTTP requests (GET, PUT, POST, DELETE) you may find it useful&#8230; rather than having to write the few lines of code yourself.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2006/08/06/simple-rest-testing-tools/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>apples and oranges</title>
		<link>http://www.briggs.net.nz/log/2006/07/01/apples-and-oranges/</link>
		<comments>http://www.briggs.net.nz/log/2006/07/01/apples-and-oranges/#comments</comments>
		<pubDate>Sat, 01 Jul 2006 16:35:58 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://www.briggs.net.nz/log/2006/07/01/apples-and-oranges/</guid>
		<description><![CDATA[My own experience suggests that REST and Soap/WS is an apple and oranges comparison. Actually that&#8217;s being unfair to oranges. It&#8217;s more like an apple and strange-looking-fruit-bearing-no-resemblance-to-anything-palatable comparison. When I&#8217;ve had the opportunity to design a system around REST principles (resource-oriented, POX, basic HTTP, etc), it has had a simplifying effect on the overall architecture [...]]]></description>
			<content:encoded><![CDATA[<p>My own experience suggests that REST and Soap/WS is an apple and oranges comparison. Actually that&#8217;s being unfair to oranges.  It&#8217;s more like an apple and strange-looking-fruit-bearing-no-resemblance-to-anything-palatable comparison.</p>
<p>When I&#8217;ve had the opportunity to design a system around REST principles (resource-oriented, <a href="http://en.wikipedia.org/wiki/POX">POX</a>, basic HTTP, etc), it has had a simplifying effect on the overall architecture &#8212; not dumbing things down, just a reduction in complexity. I&#8217;ve had to think differently about fundamental design issues &#8212; what are my resources, and how should they be interacted with&#8230; rather than RPC API details &#8212; in a way it&#8217;s a little like the difference between thinking OO terms and thinking procedurally.</p>
<p>The same can not be said for the SOAP/WS systems I&#8217;ve seen, and worked with, where complexity seems to be an accepted byproduct. BPEL, choreography, discovery, and the myriad other WS-* specifications are, to me, a good reason to sell expensive product, and justify business power lunches, but a bad way to design software. SOAP at its heart was a relatively simple idea, but there&#8217;s enough other crap bolted-on around it now to sink even the most bouyant of ships. There will be plenty of proponents who&#8217;ll argue the whole tooling angle &#8212; that their (obscenely expensive) tools will hide the implementation details from you. Well I&#8217;ve used some of their tools, and I&#8217;m definitely not sold. Just what the world needs: the equivalent of a Renault Alliance, with worse fuel economy than a Hummer, slower than a Simca 1000 running on 3 cylinders, and a Ferrari price tag to boot.<br />
Not to forget that hiding away implementation details is a good way to cause a mass of problems when the client asks you to do something that fall just outside the capabilities of the tool.</p>
<p>All of which is to say I believe that <a href="http://atmanes.blogspot.com/2006/06/flash-of-enlightenment-in-soap-vs-rest.html">positions</a> which appear to argue the differences between REST and SOAP down to mere implementation details, are less than convincing. Okay, to be absolutely honest, I have argued something vaguely similar in the <a href="http://www.onjava.com/pub/a/onjava/2006/02/15/jython-soap-interface-to-rest.html">past</a>&#8230; but I&#8217;m wavering on the cusp of thinking I should&#8217;ve explained myself a bit better. I still believe that a resource-oriented application could expose <em>some</em> SOAP-style services, but it certainly wouldn&#8217;t make sense for an entire application to expose dual interfacing. And I certainly <em>don&#8217;t</em> think that a WS-* design can then expose its methods as HTTP verbs and somehow magically satisfy the goals of a REST architecture (usage of HTTP verbs, alone, doth not a REST app make). A bit like an elephant trying to squeeze into a pair of hot-pants and then announcing to the world that it&#8217;s a prima ballerina.</p>
<p>Related footnote:  I hadn&#8217;t read <a href="http://naeblis.cx/rtomayko/2004/12/12/rest-to-my-wife">this</a> before, but it&#8217;s a brilliant read.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2006/07/01/apples-and-oranges/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>REST illustrations</title>
		<link>http://www.briggs.net.nz/log/2006/06/30/rest-illustrations/</link>
		<comments>http://www.briggs.net.nz/log/2006/06/30/rest-illustrations/#comments</comments>
		<pubDate>Fri, 30 Jun 2006 21:06:20 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://www.briggs.net.nz/log/2006/06/30/rest-illustrations/</guid>
		<description><![CDATA[Stefan has just posted a nice, simple illustration of the differences between service-oriented and resource-oriented (i.e. REST) design. Something to bookmark (which I&#8217;m doing right here&#8230; ) for the next time someone asks me what the hell REST is&#8230;]]></description>
			<content:encoded><![CDATA[<p>Stefan has just <a href="http://www.innoq.com/blog/st/2006/06/30/rest_vs_soap_oh_no_not_again.html">posted</a> a nice, simple illustration of the differences between service-oriented and resource-oriented (i.e. REST) design.  Something to bookmark (which I&#8217;m doing right here&#8230; <img src='http://www.briggs.net.nz/log/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />  ) for the next time someone asks me what the hell REST is&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2006/06/30/rest-illustrations/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>unconvinced by WADL</title>
		<link>http://www.briggs.net.nz/log/2006/05/03/unconvinced-by-wadl/</link>
		<comments>http://www.briggs.net.nz/log/2006/05/03/unconvinced-by-wadl/#comments</comments>
		<pubDate>Wed, 03 May 2006 13:40:00 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://localhost/log/2006/05/03/unconvinced-by-wadl/</guid>
		<description><![CDATA[I remain thoroughly unconvinced by WADL. I accept there&#8217;s a (current) corporate need to over-describe everything in XML, but quite frankly I&#8217;d rather describe my REST resources with HTTP OPTIONS plus extended http headers and/or, preferably, human-readable (and also machine parseable) xhtml, ala microformats. Interestingly, I find no mention of WADL on rest-discuss. Crappy yahoo [...]]]></description>
			<content:encoded><![CDATA[<p>I remain thoroughly unconvinced by <a href="http://research.sun.com/spotlight/2006/2006-04-24-TR153.html">WADL</a>.  I accept there&#8217;s a (current) corporate need to over-describe everything in XML, but quite frankly I&#8217;d rather describe my REST resources with HTTP OPTIONS plus extended http headers and/or, preferably, human-readable (and also machine parseable) xhtml, ala <a href="http://microformats.org/">microformats</a>.</p>
<p>Interestingly, I find no mention of WADL on rest-discuss.  Crappy yahoo search algorithm?  Or just &#8220;phasers set to ignore&#8221;?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2006/05/03/unconvinced-by-wadl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Resource versus Activity</title>
		<link>http://www.briggs.net.nz/log/2006/01/07/resource-versus-activity/</link>
		<comments>http://www.briggs.net.nz/log/2006/01/07/resource-versus-activity/#comments</comments>
		<pubDate>Sat, 07 Jan 2006 06:11:00 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[REST]]></category>

		<guid isPermaLink="false">http://localhost/log/2006/01/07/resource-versus-activity/</guid>
		<description><![CDATA[In an article on IBM&#8217;s developerworks: Proponents of the REST-based pattern often point to its architectural simplicity compared to the complexities inherent in the broad collection of SOAP-related Web service specifications as being the key to why REST is the superior approach. This argument has flaws in that, as I have illustrated, the two approaches [...]]]></description>
			<content:encoded><![CDATA[<p>In an <a href="http://www-128.ibm.com/developerworks/webservices/library/ws-restvsoap/">article</a> on IBM&#8217;s developerworks:</p>
<blockquote><p><em>Proponents of the REST-based pattern often point to its architectural simplicity compared to the complexities inherent in the broad collection of SOAP-related Web service specifications as being the key to why REST is the superior approach. This argument has flaws in that, as I have illustrated, the two approaches are looking to solve different types of problems. Web-based application services, such as Bloglines, Amazon, flikr, del.icio.us, and so forth, face a different set of issues than say, a hospital looking to automate their internal processes for order procedures and writing prescriptions and therefore demand a different architectural approach. That&#8217;s why design patterns exist &#8212; different approaches to solve different problems.</em></p></blockquote>
<p>I don&#8217;t believe he&#8217;s successfully illustrated any flaws at all, particularly not with those two examples.  Both order procedures and writing prescriptions can fit just as comfortably within a REST paradigm as they can SOAP.</p>
<p>For example, it&#8217;s easy to envisage POSTing to a prescription resource such as:</p>
<p><code>/health/services/prescription</code></p>
<p>which creates a new prescription identifier (for example, P1RA43) and adds the items in the prescription request.  The identifier is returned to the caller by way of the Location header:</p>
<p><code>Location: http://somehealthprovider.com/health/services/prescription/P1RA43</code></p>
<p>The prescription could then be viewed in a web browser by calling that URI and specifying a content type of text/html, or used by a pharmacy computer during the fulfillment stage, by requesting text/xml instead.  An HTTP DELETE against the URI would get rid of the prescription, a PUT would replace the contents, and subsequent POSTs add to the contents.</p>
<p>It seems to me this looks hellaciously (love that word) more elegant than the equivalent CreatePrescription, GetPrescription, AddToPrescription, ReplacePrescription, DeletePrescription, etc that I assume would be the SOAP equivalents.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2006/01/07/resource-versus-activity/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TDD, Jython &amp; REST (Part 3)</title>
		<link>http://www.briggs.net.nz/log/2005/10/02/tdd-jython-rest-part-3/</link>
		<comments>http://www.briggs.net.nz/log/2005/10/02/tdd-jython-rest-part-3/#comments</comments>
		<pubDate>Sun, 02 Oct 2005 15:27:00 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[jython]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://localhost/log/2005/10/02/tdd-jython-rest-part-3/</guid>
		<description><![CDATA[With the addition of xml validation and value checking, the testing scripts actually become somewhat useful: > PUT http://localhost:8080/ws/user '' 'username=testuser1&#38;email_address=testuser1@test.com' > GET http://localhost:8080/ws/user/testuser1 < assert status 200 < assert header Content-Type text/xml < assert xmlschema ../web/user.xsd < assert xmlbody /user/username=testuser1 < assert xmlbody /user/email_address=testuser1@test.com > DELETE http://localhost:8080/ws/user/testuser1 Depak Vohra&#8217;s article at ONJava, shows how [...]]]></description>
			<content:encoded><![CDATA[<p>With the addition of xml validation and value checking, the testing scripts actually become somewhat useful:</p>
<pre>
<code>
> PUT http://localhost:8080/ws/user ''
    'username=testuser1&amp;email_address=testuser1@test.com'
> GET http://localhost:8080/ws/user/testuser1
< assert status 200
< assert header Content-Type text/xml
< assert xmlschema ../web/user.xsd
< assert xmlbody /user/username=testuser1
< assert xmlbody /user/email_address=testuser1@test.com
> DELETE http://localhost:8080/ws/user/testuser1
</code>
</pre>
<p>Depak Vohra&#8217;s <a href="http://www.onjava.com/pub/a/onjava/2004/09/15/schema-validation.html">article</a> at ONJava, shows how to perform schema validation using xerces/jaxp.  Adapting his code to jython is straightforward, by adding the following imports to the test script from <a href="http://jasonrbriggs.blogspot.com/2005/09/tdd-jython-rest-part-2.html">part 2</a>:</p>
<pre>
<code>
from java.<span class="pyname">io</span> <span class="pykeyword">import</span> StringReader
from java.<span class="pyname">lang</span> <span class="pykeyword">import</span> System
from javax.<span class="pyname">xml</span>.<span class="pyname">parsers</span> <span class="pykeyword">import</span> DocumentBuilderFactory
from javax.<span class="pyname">xml</span>.<span class="pyname">parsers</span> <span class="pykeyword">import</span> DocumentBuilder
from org.<span class="pyname">xml</span>.<span class="pyname">sax</span>.<span class="pyname">helpers</span> <span class="pykeyword">import</span> DefaultHandler
from org.<span class="pyname">xml</span>.<span class="pyname">sax</span> <span class="pykeyword">import</span> InputSource

System.<span class="pyname">setProperty</span>(<span class="pystring">'javax.xml.parsers.DocumentBuilderFactory'</span>,
        <span class="pystring">'org.apache.xerces.jaxp.DocumentBuilderFactoryImpl'</span>)
</code>
</pre>
<p>Depak&#8217;s Validator becomes:</p>
<pre>
<code>
<span class="pykeyword">class</span> <span class="pyname">Validator</span>(DefaultHandler):
    <span class="pykeyword">def</span> <span class="pyname">__init__</span>(self):
        self.<span class="pyname">validationError</span> = <span class="pynumber">0</span>
        self.<span class="pyname">saxParseException</span> = None

    <span class="pykeyword">def</span> <span class="pyname">error</span>(self, exception):
        self.<span class="pyname">validationError</span> = <span class="pynumber">1</span>
        self.<span class="pyname">saxParseException</span> = exception

    <span class="pykeyword">def</span> <span class="pyname">fatalError</span>(self, exception):
        self.<span class="pyname">validationError</span> = <span class="pynumber">1</span>
        self.<span class="pyname">saxParseException</span> = exception      

    <span class="pykeyword">def</span> <span class="pyname">warning</span>(self, exception):
        pass
</code>
</pre>
<p>Finally, a new assertion method is added to the Test class:</p>
<pre>
<code>
    <span class="pycomment">#</span>
    <span class="pycomment"># assert that an xml document validates against an xml schema</span>
    <span class="pycomment">#</span>
    <span class="pykeyword">def</span> <span class="pyname">assert_xmlschema</span>(self, linearr, req, res):
        xml = res.<span class="pyname">getContent</span>()

        factory = DocumentBuilderFactory.<span class="pyname">newInstance</span>()
        factory.<span class="pyname">setNamespaceAware</span>(<span class="pynumber">1</span>)
        factory.<span class="pyname">setValidating</span>(<span class="pynumber">1</span>)
        factory.<span class="pyname">setAttribute</span>(<span class="pystring">'http://java.sun.com/xml/jaxp/properties/schemaLanguage'</span>,
                <span class="pystring">'http://www.w3.org/2001/XMLSchema'</span>)
        errhandler = Validator()
        factory.<span class="pyname">setAttribute</span>(<span class="pystring">'http://java.sun.com/xml/jaxp/properties/schemaSource'</span>,
            linearr[<span class="pynumber">0</span>])
        builder = factory.<span class="pyname">newDocumentBuilder</span>()
        builder.<span class="pyname">setErrorHandler</span>(errhandler)

        dom = builder.<span class="pyname">parse</span>(InputSource(StringReader(xml)))

        <span class="pykeyword">if</span> errhandler.<span class="pyname">validationError</span> == <span class="pynumber">1</span>:
            errmsg = <span class="pystring">'%s, %s'</span> % (<span class="pyfunction">str</span>(errhandler.<span class="pyname">validationError</span>),
                    errhandler.<span class="pyname">saxParseException</span>.<span class="pyname">getMessage</span>())
        <span class="pykeyword">else</span>:
            errmsg = <span class="pystring">''</span>

        assert errhandler.<span class="pyname">validationError</span> != <span class="pynumber">1</span>, errmsg
</code>
</pre>
<p>Value checking is accomplished with the following method:</p>
<pre>
<code>
    <span class="pycomment">#</span>
    <span class="pycomment"># assert that a particular xml element contains a particular value</span>
    <span class="pycomment">#</span>
    <span class="pykeyword">def</span> <span class="pyname">assert_xmlbody</span>(self, linearr, req, res):
        xml = res.<span class="pyname">getContent</span>()
        <span class="pykeyword">if</span> not self.<span class="pyname">xml</span> <span class="pykeyword">or</span> self.<span class="pyname">xml</span> != xml:
            factory = DocumentBuilderFactory.<span class="pyname">newInstance</span>()
            factory.<span class="pyname">setNamespaceAware</span>(<span class="pynumber">1</span>)
            builder = factory.<span class="pyname">newDocumentBuilder</span>()
            self.<span class="pyname">dom</span> = builder.<span class="pyname">parse</span>(InputSource(StringReader(xml)))
            self.<span class="pyname">xml</span> = xml

        <span class="pycomment"># get rid of the starting '/'</span>
        <span class="pycomment"># (could leave this out in the test, but it looks better in to me)</span>
        <span class="pykeyword">if</span> linearr[<span class="pynumber">0</span>].<span class="pyname">startswith</span>(<span class="pystring">'/'</span>):
            linearr[<span class="pynumber">0</span>] = linearr[<span class="pynumber">0</span>][<span class="pynumber">1</span>:]

        <span class="pycomment"># split into xml element list and a value based on the first '=' position</span>
        equalspos = linearr[<span class="pynumber">0</span>].<span class="pyname">find</span>(<span class="pystring">'='</span>)
        tmp = linearr[<span class="pynumber">0</span>][<span class="pynumber">0</span>:equalspos]
        value = linearr[<span class="pynumber">0</span>][equalspos+1:]
        elements = tmp.<span class="pyname">split</span>(<span class="pystring">'/'</span>)

        <span class="pycomment"># loop through the elements in the list and retrieve each named node</span>
        node = self.<span class="pyname">dom</span>
        <span class="pykeyword">for</span> x <span class="pykeyword">in</span> xrange(<span class="pynumber">0</span>, <span class="pyfunction">len</span>(elements)):
            nl = node.<span class="pyname">getElementsByTagName</span>(elements[x])
            <span class="pykeyword">if</span> not nl:
                raise AssertionError, <span class="pystring">'sub element "%s" is missing'</span> % elements[x]
            <span class="pykeyword">else</span>:
                node = nl.<span class="pyname">item</span>(<span class="pynumber">0</span>)
            <span class="pykeyword">if</span> x == <span class="pyfunction">len</span>(elements)-1:
                eval = node.<span class="pyname">getFirstChild</span>().<span class="pyname">getNodeValue</span>()
                assert eval == value, <span class="pystring">'element %s (value "%s") does not match expected %s'</span>
                            % (elements[x], eval, value)
                return
        raise AssertionError, <span class="pystring">'missing element %s'</span> % tmp
</code>
</pre>
<p>Which takes an assertion line such as &#8220;assert xmlbody /user/username=testuser1&#8243; and looks up elements in the dom, where &#8220;user&#8221; is the root node, and &#8220;username&#8221; is a child node of user.  At the moment this is very basic value checking which won&#8217;t work well for more complicated documents &#8212; but I&#8217;m trying to avoid complication as much as possible anyway, so it&#8217;s difficult to justify (to myself at least) going to greater lengths to make something more flexible.  The main enhancement I&#8217;m looking to make initially is the ability to specify the nth child of a node.  For example, something like the string &#8220;/list/user[2]/username&#8221; (in case it&#8217;s not painfully obvious, the second &#8220;user&#8221; element in &#8220;list&#8221;).</p>
<p>But for the moment, this will hopefully get me going.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2005/10/02/tdd-jython-rest-part-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TDD, Jython &amp; REST (Part 2)</title>
		<link>http://www.briggs.net.nz/log/2005/09/28/tdd-jython-rest-part-2/</link>
		<comments>http://www.briggs.net.nz/log/2005/09/28/tdd-jython-rest-part-2/#comments</comments>
		<pubDate>Wed, 28 Sep 2005 17:28:00 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[jython]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://localhost/log/2005/09/28/tdd-jython-rest-part-2/</guid>
		<description><![CDATA[In part one of this rumination, I discussed test-driven-development of a REST-style application written using Jython. An example of a test method would be something like: def test_getvaliduser(): req = MockHttpServletRequest('GET', 'user', 'http://localhost:8080/ws/user/testuser1', '', '') req.setContentType('text/html') res = MockHttpServletResponse() user.doGet(req, res) assert res.getStatus() == 200, 'incorrect status %s (%s)' % (res.getStatus(), res.getReason()) assert res.getContentType().startswith('text/html'), 'expected [...]]]></description>
			<content:encoded><![CDATA[<p>In part <a href="http://jasonrbriggs.blogspot.com/2005/09/tdd-jython-rest.html">one</a> of this rumination, I discussed test-driven-development of a REST-style application written using Jython. An example of a test method would be something like:</p>
<pre>
<code>
<span class="pykeyword">def</span> <span class="pyname">test_getvaliduser</span>():
    req = MockHttpServletRequest(<span class="pystring">'GET'</span>, <span class="pystring">'user'</span>, <span class="pystring">'http://localhost:8080/ws/user/testuser1'</span>, <span class="pystring">''</span>, <span class="pystring">''</span>)
    req.<span class="pyname">setContentType</span>(<span class="pystring">'text/html'</span>)

    res = MockHttpServletResponse()

    user.<span class="pyname">doGet</span>(req, res)

    assert res.<span class="pyname">getStatus</span>() == <span class="pynumber">200</span>, <span class="pystring">'incorrect status %s (%s)' % (res.getStatus(), res.getReason())</span>
    assert res.<span class="pyname">getContentType</span>().<span class="pyname">startswith</span>(<span class="pystring">'text/html'</span>), <span class="pystring">'expected text/html, got %s'</span> % res.<span class="pyname">getContentType</span>()
</code>
</pre>
<p>The main problem I have with this idea, is the amount of coding (read: effort) required for each test method, and therefore the amount of duplicated effort testing each resource (servlet). Testing other components in this manner, or testing non-standard functionality, is another matter, of course.</p>
<p>I&#8217;ve come up with what I believe is a more workable solution.  Reducing the previous example to something like the following:</p>
<pre>
<code>
== Get Valid User
> GET http://localhost:8080/ws/user/testuser1
< assert status 200
</code>
</pre>
<p>Which is a serious reduction in the amount of typing required for a test, particularly when you add the setup functions (missing from the 'old' test example):</p>
<pre>
<code>
== Get Valid User
> PUT http://localhost:8080/ws/user '' &#92;
    'username=testuser1&amp;email_address=testuser1@test.com&amp;level=1&amp;password=password'
> GET http://localhost:8080/ws/user/testuser1
< assert status 200
< assert header Content-Type text/xml
> DELETE http://localhost:8080/ws/user/testuser1
</code>
</pre>
<p>Hopefully the script is relatively straightforward to understand: '==' signifies a new test, '&gt;' is an http call, and 'here</a>.</p>
<p>The next step is to add handlers for checking the content in the response (particularly xml), both in form and content.</p>
<p>(If anyone is interested in a working example, let me know and I'll endeavour to post something useable)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2005/09/28/tdd-jython-rest-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TDD, Jython &amp; REST (Part 1)</title>
		<link>http://www.briggs.net.nz/log/2005/09/16/tdd-jython-rest-part-1/</link>
		<comments>http://www.briggs.net.nz/log/2005/09/16/tdd-jython-rest-part-1/#comments</comments>
		<pubDate>Fri, 16 Sep 2005 18:34:00 +0000</pubDate>
		<dc:creator>jrbriggs</dc:creator>
				<category><![CDATA[technical]]></category>
		<category><![CDATA[jython]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[TDD]]></category>

		<guid isPermaLink="false">http://localhost/log/2005/09/16/tdd-jython-rest-part-1/</guid>
		<description><![CDATA[I&#8217;ve been working on a personal project for a few months now (begun while living in Thailand for a few months), combining a number of different technologies I currently find interesting. I&#8217;ve chosen Jython as the programming language in recognition of how powerful I&#8217;ve found developing in Python &#8212; the reasons for not going completely [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working on a personal project for a few months now (begun while living in Thailand for a few months), combining a number of different technologies I currently find interesting. I&#8217;ve chosen Jython as the programming language in recognition of how powerful I&#8217;ve found developing in Python &#8212; the reasons for not going completely down the Python route (much as I would like to be using Py 2.4 modules) are mainly because of library issues. In particular I haven&#8217;t found a templating library as good as <a href="http://jakarta.apache.org/velocity">Velocity</a> in the Pythonic world.  Of course there is  <a href="http://www.cheetahtemplate.org/">Cheetah</a>, but while it is as close to Velocity as it&#8217;s possible to be without &#8216;sleeping in the same bed&#8217;, there is still something about Cheetah that rubs me the wrong way. I suspect it&#8217;s simply because, as a long time Velocity user, I have become so accustomed to the techniques we use with that library, that slight changes/quirks in the other become larger issues, completely out of proportion to their actual magnitude.</p>
<p><a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST</a> is less a technology than a way of thinking which I&#8217;m endeavouring to apply to both personal and professional projects. How successful that application is remains to be seen, but thinking about web services in terms of a resource with a simple set of verbs, has certainly resulted in a re-think about how one approaches component development. It&#8217;s difficult to justify a full J2EE deployment (EJBs and so on) when your operations are reduced to a basic four (GET, POST, PUT, DELETE) and the business logic fits comfortably within the framework provided by a servlet (in my case a Jython-Servlet).</p>
<p>Finally <a href="http://en.wikipedia.org/wiki/Test_driven_development">TDD</a> (which technically should&#8217;ve been mentioned first, considering where it falls in the development cycle), comes from the understanding that developing a project, which I hope to eventually turn into something commercially viable, requires more than just hacking together a bunch of parts in the hope that my frankenstein will live when electricity is applied to its extremities. I have to also consider the future of this system &#8212; have confidence in both my design and in any changes/refactoring I might make to it after it has a community of users.</p>
<p>TDD is at the crux of the code sample below.  This is just the beginnings of a testing system for my rest+jython servlets.</p>
<p>It may make more sense, to some, to reuse what&#8217;s already available (JUnit for example, or the unit-testing modules already included in Python/Jython), but this is as much a learning exercise, as serious development, since I haven&#8217;t yet had to opportunity to apply TDD in any professional projects.</p>
<p><span id="more-12"></span></p>
<pre>
<code>
<span class="pycomment">#! /usr/bin/env ../bin/jython</span>

<span class="pykeyword">import</span> base64
<span class="pykeyword">import</span> sys

<span class="pykeyword">def</span> <span class="pyname">authbasic</span>(username, password):
    return <span class="pystring">'Basic %s'</span> % base64.<span class="pyname">encodestring</span>(<span class="pystring">'%s:%s'</span> % (username, password))

global successtests
successtests = <span class="pynumber">0</span>
global failedtests
failedtests = <span class="pynumber">0</span>
global errortests
errortests = <span class="pynumber">0</span>

<span class="pykeyword">def</span> <span class="pyname">contains</span>(srclist, matchlist):
   <span class="pykeyword">for</span> i <span class="pykeyword">in</span> matchlist:
       <span class="pykeyword">if</span> i not <span class="pykeyword">in</span> srclist:
           return false
   return true

<span class="pycomment">#</span>
<span class="pycomment"># given a module name and a list of tests, run each test in the module</span>
<span class="pycomment">#</span>
<span class="pykeyword">def</span> <span class="pyname">runTests</span>(mod, testnames=None):
   global successtests
   global failedtests
   global errortests

   exec <span class="pystring">'import %s as testmod'</span> % mod
   <span class="pykeyword">if</span> hasattr(testmod, <span class="pystring">'setup'</span>):
       getattr(testmod, <span class="pystring">'setup'</span>)()

   <span class="pykeyword">for</span> name <span class="pykeyword">in</span> dir(testmod):
       <span class="pykeyword">if</span> name.<span class="pyname">startswith</span>(<span class="pystring">'test_'</span>):

           <span class="pykeyword">if</span> testnames:
               chkname = name[<span class="pynumber">5</span>:]
               <span class="pykeyword">if</span> chkname not <span class="pykeyword">in</span> testnames:
                   <span class="pykeyword">continue</span>

           try:
               try:
                   <span class="pycomment"># get the method from the module object</span>
                   testmethod = getattr(testmod, name)

                   <span class="pycomment"># run the test, and if successful increment our counter</span>
                   testmethod()
                   successtests = successtests + <span class="pynumber">1</span>
               except AssertionError, ae:
                   <span class="pycomment"># handle assertion failures</span>
                   failedtests = failedtests + <span class="pynumber">1</span>
                   print '[%s] failed because of "%s"' % (name, <span class="pyfunction">str</span>(ae))
           except Exception, e:
               <span class="pycomment"># handle errors that fall outside of the test boundaries</span>
               errortests = errortests + <span class="pynumber">1</span>
               print '[%s] failed because of "%s"' % (name, <span class="pyfunction">str</span>(e))
               (type, value, traceback) = sys.<span class="pyname">exc_info</span>()
               print traceback.<span class="pyname">dumpStack</span>()

   print <span class="pystring">'''%s tests succeeded</span>
%s tests failed assertions
%s tests failed because of errors''' % (successtests, failedtests, errortests)

<span class="pykeyword">if</span> __name__ == <span class="pystring">'__main__'</span>:
   <span class="pykeyword">if</span> <span class="pyfunction">len</span>(sys.<span class="pyname">argv</span>) == <span class="pynumber">2</span>:
       runTests(sys.<span class="pyname">argv</span>[<span class="pynumber">1</span>])
   <span class="pykeyword">elif</span> <span class="pyfunction">len</span>(sys.<span class="pyname">argv</span>) == <span class="pynumber">3</span>:
       runTests(sys.<span class="pyname">argv</span>[<span class="pynumber">1</span>], sys.<span class="pyname">argv</span>[<span class="pynumber">2</span>].<span class="pyname">split</span>(','))
</code>
</pre>
<p>NB:  I&#8217;ve hacked out some of the functionality required to set up that particular test (such as creating and deleting the user record), but you get the idea.</p>
<p>A sample scripting testing a valid user of a user management web service would look like this:</p>
<pre>
<code>
from wstesting <span class="pykeyword">import</span> MockServletContext, MockServletConfig, MockHttpServletRequest, MockHttpServletResponse

<span class="pykeyword">import</span> miscutils
<span class="pykeyword">import</span> test

<span class="pykeyword">import</span> props

global user
user = None

<span class="pykeyword">def</span> <span class="pyname">setup</span>():
   <span class="pycomment"># change the database connect string to use the test database</span>
   props.<span class="pyname">db_connect_string</span> = <span class="pystring">'jdbc:postgresql:testdb'</span></span>

   <span class="pycomment"># dynamically load the module</span>
   usermod = miscutils.<span class="pyname">loadmod</span>('.<span class="pyname">.</span>/web',<span class="pystring">'user.py'</span>)

   <span class="pycomment"># create a servlet context for testing with</span>
   ctx = MockServletContext.<span class="pyname">getInstance</span>().<span class="pyname">setRootPath</span>('.<span class="pyname">.</span>/web')

   <span class="pycomment"># load the servlet and then initialise with the config object</span>
   global user
   user = usermod.<span class="pyname">user</span>()
   user.<span class="pyname">init</span>(MockServletConfig(<span class="pystring">'user'</span>))

<span class="pykeyword">def</span> <span class="pyname">test_getvaliduser</span>():
   req = MockHttpServletRequest(<span class="pystring">'GET'</span>, <span class="pystring">'user'</span>, <span class="pystring">'http://localhost:8080/ws/user/testuser1'</span>, <span class="pystring">''</span>, <span class="pystring">''</span>)
   req.<span class="pyname">setContentType</span>(<span class="pystring">'text/html'</span>)

   res = MockHttpServletResponse()

   user.<span class="pyname">doGet</span>(req, res)

   assert res.<span class="pyname">getStatus</span>() == <span class="pynumber">200</span>, <span class="pystring">'incorrect status %s (%s)' % (res.getStatus(), res.getReason())</span>
   assert res.<span class="pyname">getContentType</span>().<span class="pyname">startswith</span>(<span class="pystring">'text/html'</span>), <span class="pystring">'expected text/html, got %s'</span> % res.<span class="pyname">getContentType</span>()
</code>
</pre>
<p>So that&#8217;s the basic, lots of typing approach to TDD for my REST app. Next step is to completely scrap that structure, and do it the right way (i.e. a method that doesn&#8217;t involve writing the similar code over and over again for each service). More on that later&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.briggs.net.nz/log/2005/09/16/tdd-jython-rest-part-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
