<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8355257</id><updated>2012-01-25T00:41:43.110-06:00</updated><category term='media types'/><category term='JUG'/><category term='documentation'/><category term='Wall of Erasure'/><category term='bug'/><category term='annotations'/><category term='JAX-RS'/><category term='Windows'/><category term='open source'/><category term='syntax'/><category term='www'/><category term='conflicts'/><category term='BGGA'/><category term='DSL'/><category term='resources'/><category term='web service'/><category term='Mac OS X'/><category term='No Fluff Just Stuff'/><category term='user interface design'/><category term='HATEOAS'/><category term='API discovery'/><category term='closures'/><category term='Jersey'/><category term='anti-patterns'/><category term='POST'/><category term='word wide web'/><category term='rfc 2616'/><category term='RESTful services'/><category term='mistakes'/><category term='example'/><category term='Content-Type'/><category term='XML'/><category term='adapters'/><category term='iterators'/><category term='Accept'/><category term='RESTful'/><category term='interface chaining'/><category term='unit testing'/><category term='GET'/><category term='architecture'/><category term='prime minister'/><category term='GWT'/><category term='support'/><category term='safe methods'/><category term='architecture of the web'/><category term='client'/><category term='XP'/><category term='Xcode'/><category term='RemoteIterator'/><category term='apple'/><category term='303'/><category term='representations'/><category term='allocation'/><category term='URI'/><category term='risk'/><category term='curl'/><category term='http'/><category term='SOA'/><category term='Ajax'/><category term='C++'/><category term='design pattern'/><category term='virtual memory'/><category term='WSDL'/><category term='project planning'/><category term='response'/><category term='frameworks'/><category term='JSR-311'/><category term='bad design'/><category term='grizzly servlet'/><category term='JAX-WS'/><category term='code coupling'/><category term='JSON'/><category term='BufferedReader'/><category term='Guice'/><category term='WS-*'/><category term='URI fragment'/><category term='JNI'/><category term='dependency injection'/><category term='Iterable'/><category term='REST'/><category term='http redirect'/><category term='programming'/><category term='good service design'/><category term='assertions'/><category term='philanthropy'/><category term='entity provider'/><category term='software design'/><category term='web services'/><category term='Java'/><category term='distributed computing'/><category term='danger'/><category term='good documentation'/><category term='human factors'/><category term='versioning'/><category term='SOAP'/><category term='blog-tag'/><category term='Gateway Software Symposium'/><category term='hello world'/><category term='St. Louis Java User&apos;s Group'/><category term='SmartGWT'/><category term='iterator'/><category term='container'/><category term='WAX'/><category term='Maven'/><category term='curves'/><category term='generics'/><category term='ownership'/><category term='twitter'/><category term='project management'/><category term='estimation'/><title type='text'>View from the Fringe</title><subtitle type='html'>Observations and commentary from the edge of normal.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>56</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8355257.post-8518770840207395222</id><published>2011-03-10T09:18:00.000-06:00</published><updated>2011-03-10T09:18:40.747-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='mistakes'/><category scheme='http://www.blogger.com/atom/ns#' term='prime minister'/><title type='text'>Pardon me, but I believe there's been a mistake...</title><content type='html'>I logged into my Twitter account this morning after having not looked at it for quite some time. Imagine my surprise when I looked at the list of people following me and I see this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://lh5.googleusercontent.com/-rZFawdjoGk4/TXjrLyE50pI/AAAAAAAAAIA/C7gIIY10e7o/s1600/UNPrimeMinisterFollower.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="https://lh5.googleusercontent.com/-rZFawdjoGk4/TXjrLyE50pI/AAAAAAAAAIA/C7gIIY10e7o/s1600/UNPrimeMinisterFollower.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;While it's flattering to entertain the idea that the UK Prime Minister cares about my tweets, somehow I don't think so.&lt;br /&gt;&lt;br /&gt;I've gotten my unintended humor for the day and the day has barely begun...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8518770840207395222?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8518770840207395222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8518770840207395222' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8518770840207395222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8518770840207395222'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2011/03/pardon-me-but-i-believe-theres-been.html' title='Pardon me, but I believe there&apos;s been a mistake...'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='https://lh5.googleusercontent.com/-rZFawdjoGk4/TXjrLyE50pI/AAAAAAAAAIA/C7gIIY10e7o/s72-c/UNPrimeMinisterFollower.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1975315756846682378</id><published>2011-03-01T11:53:00.000-06:00</published><updated>2011-03-01T11:53:20.779-06:00</updated><title type='text'>Jargon, REST, and Reuse</title><content type='html'>Benjamin Carlyle has &lt;a href="http://soundadvice.id.au/blog/2011/02/09/"&gt;a great post&lt;/a&gt; about jargon in REST and how it relates to media types and reuse. RESTs uniform API is great but he succinctly makes a compelling case for why we also need to focus on reusing media types whenever possible to really enable reuse across services and over time.&lt;br /&gt;&lt;br /&gt;It's well worth your time. Go &lt;a href="http://soundadvice.id.au/blog/2011/02/09/"&gt;read it now&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1975315756846682378?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1975315756846682378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1975315756846682378' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1975315756846682378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1975315756846682378'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2011/03/jargon-rest-and-reuse.html' title='Jargon, REST, and Reuse'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-4861527105252430280</id><published>2010-06-15T21:22:00.000-05:00</published><updated>2010-06-15T21:22:29.608-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RESTful services'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='Content-Type'/><category scheme='http://www.blogger.com/atom/ns#' term='conflicts'/><category scheme='http://www.blogger.com/atom/ns#' term='rfc 2616'/><category scheme='http://www.blogger.com/atom/ns#' term='media types'/><category scheme='http://www.blogger.com/atom/ns#' term='Accept'/><title type='text'>Handling conflicts in media type headers</title><content type='html'>If you read my &lt;a href="http://viewfromthefringe.blogspot.com/2010/06/versioning-rest-services.html"&gt;previous entry discussing versioning of RESTful services&lt;/a&gt;, I pointed out how we can leverage media types to support incompatible versioning of RESTful services. I think this is an excellent solution, but it can lead to an issue for service implementors&lt;br /&gt;&lt;br /&gt;Imagine you have a service which has more than one version. Let's say it's a hotel reservation system, just to pick something familiar. We've picked media types for our versions of the service, and adopted the convention that in the absence of any media types we will use "Version 1" of the service. Assume our media types are "application/hotel-json" and "application/hotel-json-v2".&lt;br /&gt;&lt;br /&gt;If you support operations for updating a reservation using PUT, you can run into a situation where you have both a Content-Type header (specifying the content of the entity body being used to update the reservation) and an Accept header specifying the kind or kinds of entities the client can accept in the response. This might be just fine, if the two versions of the service can be intermixed between the sent entity and the returned entity. But the two versions of the service might not mix. For example, an update sent using version one of the updating entity might not contain a value required for version two of the service.&lt;br /&gt;&lt;br /&gt;In this situation what the client did is an error, so we clearly need to return a status code in the 400 range. But which is the most appropriate code? A quick look at the 4XX codes shows two promising candidates:&lt;br /&gt;&lt;br /&gt;406 - Not Acceptable&lt;br /&gt;415 - Unsupported Media Type&lt;br /&gt;&lt;br /&gt;But when we read the description of 415 in &lt;a href="http://www.ietf.org/rfc/rfc2616.txt"&gt;RFC 2616&lt;/a&gt; we see it isn't appropriate:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The server is refusing to service the request because the entity of&amp;nbsp;the request is in a format not supported by the requested resource&amp;nbsp;for the requested method.&lt;/blockquote&gt;The Accept header is specifying a media type the service understands for the resource, it's just the combination of provided entity media type and requested return media type that is a problem. Happily, status code 406 fits perfectly:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The resource identified by the request is only capable of generating&amp;nbsp;response entities which have content characteristics not acceptable&amp;nbsp;according to the accept headers sent in the request.&lt;/blockquote&gt;Interestingly, the RFC makes a comment about this very situation, and suggests that returning an entity type different than those specified in the Accept header might be preferable to a 406:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Note: HTTP/1.1 servers are allowed to return responses which are&amp;nbsp;not acceptable according to the accept headers sent in the&amp;nbsp;request. In some cases, this may even be preferable to sending a&amp;nbsp;406 response. User agents are encouraged to inspect the headers of&amp;nbsp;an incoming response to determine if it is acceptable.&lt;/blockquote&gt;In this particular scenario (where a required value isn't provided), one way to return a result would be to "downgrade" to a version 1 response in hopes that works for the client. There are potential issues with this, if the semantics of applying a version 1 update differ from those that would have occurred with a version 2 update. And if the client can't interpret the returned version 1 entity, they may end up in an inconsistent state. But it's a good idea to keep in mind and make work whenever possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-4861527105252430280?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/4861527105252430280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=4861527105252430280' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4861527105252430280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4861527105252430280'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/06/handling-conflicts-in-media-type.html' title='Handling conflicts in media type headers'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-6687752607012184601</id><published>2010-06-15T10:04:00.001-05:00</published><updated>2010-06-15T11:27:06.487-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RESTful'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='web services'/><category scheme='http://www.blogger.com/atom/ns#' term='versioning'/><category scheme='http://www.blogger.com/atom/ns#' term='media types'/><title type='text'>Versioning REST services</title><content type='html'>I read a &lt;a href="http://wisdomofganesh.blogspot.com/2010/06/does-rest-need-versioning.html"&gt;blog entry&lt;/a&gt; yesterday from &lt;a href="http://wisdomofganesh.blogspot.com/"&gt;Ganesh&lt;/a&gt; musing on whether RESTful web services need versioning. In his posting, Ganesh suggests putting some kind of version number into the body of a request. He suggests that this be done even for a GET or DELETE.&lt;br /&gt;&lt;br /&gt;While I suppose this could work, it feels awkward. Putting a version number into an entity body for a GET, which would typically not have an entity body, is awkward and a fair bit of work. Things get even more complicated in the case of a PUT or POST where the entity body is some binary format like an image format. In this case, the Content-Type can't just be image/jpeg (for example) since your putting some kind of version information before the image data.&lt;br /&gt;&lt;br /&gt;Even in the case of textual data such as XML, putting version information into the entity body requires that our XML format support a version number somewhere (probably as an attribute of the top-level element in the entity body?). This may be feasible, but it may not be feasible if we are trying to use a data format that is already defined. We could start hacking and just throw a line at the beginning of the entity body that indicates the version with the understanding that the 'normal' payload starts after this line. But this is hackish, and makes our Content-Type delcarations wrong (we aren't adhering to the content type definition since we've thrown that version information at the front).&lt;br /&gt;&lt;br /&gt;After reading Ganesh's entry, I thought about versioning for a while and it seemed to me that the right way to handle versioning is via media types (using, for example, the Accept and Content-Type headers). This works well because the first version of our service can be delivered when no media-type headers are specified. Then, if a client wants to take advantage of a newer, incompatible version of our service, they simply specify this using media types in the Accept and Content-Type headers.&lt;br /&gt;&lt;br /&gt;I could spend a fair bit of time describing this in more detail, but I did some googling and found a &lt;a href="http://barelyenough.org/blog/2008/05/versioning-rest-web-services/"&gt;great blog entry&lt;/a&gt;&amp;nbsp;(written back in May of 2008) by &lt;a href="http://barelyenough.org/blog/"&gt;Peter Williams&lt;/a&gt; where he describes things concisely and clearly. It's short and sweet and &lt;a href="http://barelyenough.org/blog/2008/05/versioning-rest-web-services/"&gt;well worth a read&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Back from reading his posting? There are two issues I thought of which Peter didn't directly address. One is specific to versioning, and one is a more general issue that can arise. The versioning related issue is this:&lt;br /&gt;&lt;br /&gt;What if your service model changes to much that the set of resources you expose has to remove one of the resources in your original version of the service?&lt;br /&gt;&lt;br /&gt;In this case, you make operations to that URI path with an Accept header and/or Content-Type header for later versions of your service return 404 (or similar error code) since that type of resource doesn't exist with that version of the service. While Peter didn't talk specifically about this, this approach meshes well with his description and I suspect it is the same answer he would give if asked.&lt;br /&gt;&lt;br /&gt;A related but more general problem can occur if your service receives a request with both an Accept and a Content-Type header and the values are incompatible. I'll address that in my next entry.&lt;br /&gt;&lt;br /&gt;[Updated 2010-06-15 11:25 a.m.]&lt;br /&gt;&lt;br /&gt;I should mention that you shouldn't create new media types unless you have to. If your service deals with ATOM or RSS, use the version number for those to distinguish different versions of your service if at all possible. In his thesis, Roy Fielding states that creating new media types is not RESTful. And that is true in the sense that custom media types reduce the likelihood that clients will know how to interpret your representations. But there is a tension here between accurately representing incompatible versions of a service and using a pre-existing media type. There's no perfect answer, just a continuum with different trade-offs for different kinds of applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-6687752607012184601?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/6687752607012184601/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=6687752607012184601' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6687752607012184601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6687752607012184601'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/06/versioning-rest-services.html' title='Versioning REST services'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-6207096546001374454</id><published>2010-05-31T18:51:00.001-05:00</published><updated>2010-05-31T18:52:35.556-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='API discovery'/><category scheme='http://www.blogger.com/atom/ns#' term='HATEOAS'/><title type='text'></title><content type='html'>I spoke at the Gateway Software Symposium (NFJS St. Louis) last weekend. After my &lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17649"&gt;RESTful Web Services with JAX-RS talk&lt;/a&gt;, one of the attendees asked me how to discover the proper URLs when starting to interact with a RESTful service. The impetus for the question came from my statement that many RESTful services don't have large specifications (such as the WSDL files for "Big" web services).&lt;br /&gt;&lt;br /&gt;I explained that well-designed RESTful services are "well-connected". By this, I mean they adhere to the idea of "hypermedia as the engine of application state", or &lt;a href="http://en.wikipedia.org/wiki/HATEOAS"&gt;HATEOAS&lt;/a&gt;. If your service adheres to HATEOAS, your service is accessible and navigable given just a single URL. How can this be?&lt;br /&gt;&lt;br /&gt;Recall that in RESTful services, we use URIs/URLs to connect various related resources. To be well-connected (I'll use this term in place of to HATEOAS mostly because the acronym looks odd to some people) and be RESTful, representations of resources should contain URIs/URLs to related resources. To make this clearer, let's look at a specific example. As we look at this example, we'll make note of the aspects of the API which couldn't be discovered by examining the results of searches or resource retrieval.&lt;br /&gt;&lt;br /&gt;In my talks on JAX-RS, I use a simple music service. In this service, each artist has a name and some number of music albums they have published. Each album as a name and some number of tracks on that album. Here is a simple UML diagram of the objects:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_nyDSKzPKawU/S_6yFQ-_FWI/AAAAAAAAAEo/sFihUtj-05s/s1600/MusicServiceUML.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_nyDSKzPKawU/S_6yFQ-_FWI/AAAAAAAAAEo/sFihUtj-05s/s320/MusicServiceUML.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;We now come to the first piece of information we can't discover for ourselves. We need to know a starting URL. With the example service, it's simply a host name and a port, as in: "http://localhost:3131".&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;When we run the music service and hit it with a browser, we see a spartan web page:&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_nyDSKzPKawU/TAQ4Eq9Iz_I/AAAAAAAAAEw/2Q8__Qu1Sxo/s1600/Service+Entry+Form.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="230" src="http://1.bp.blogspot.com/_nyDSKzPKawU/TAQ4Eq9Iz_I/AAAAAAAAAEw/2Q8__Qu1Sxo/s400/Service+Entry+Form.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;While this certainly allows us to do some (mildly) interesting things, how does it help us discover the API for this RESTful service? The answer lies in examining the underlying HTML. When we do that, we see this:&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/_nyDSKzPKawU/TARHKk1R5HI/AAAAAAAAAFY/xun98ZsK4yQ/s1600/HTML+for+service+entry+page.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="640" src="http://2.bp.blogspot.com/_nyDSKzPKawU/TARHKk1R5HI/AAAAAAAAAFY/xun98ZsK4yQ/s640/HTML+for+service+entry+page.png" width="569" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;At first, there might not seem to be much here. But let's look at the form for searching for artists:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_nyDSKzPKawU/TARHYgHRsDI/AAAAAAAAAFg/bkAIbY7cr3s/s1600/Search+For+Artists+HTML+Form.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="168" src="http://2.bp.blogspot.com/_nyDSKzPKawU/TARHYgHRsDI/AAAAAAAAAFg/bkAIbY7cr3s/s640/Search+For+Artists+HTML+Form.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;From this we can see that we can search for artists whose name "is" (IS), "starts with" (STARTS), "contains" (CONTAINS), or "ends with" (ENDS) the text specified by 'searchString'. If you don't live and work with HTML forms and their resulting queries every day, this still might not be obvious. So we can always go ahead and do a search. Let's search for artists whose name begins with 'F':&lt;br /&gt;&lt;blockquote&gt;&lt;a href="http://2.bp.blogspot.com/_nyDSKzPKawU/TAQ5ZLnXLWI/AAAAAAAAAE4/W_xmI6t-nmw/s1600/Search+For+Artists+Starting+with+F.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;span class="Apple-style-span" style="color: black;"&gt;&lt;span class="Apple-style-span" style="text-decoration: none;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_nyDSKzPKawU/TAQ5ZLnXLWI/AAAAAAAAAE4/W_xmI6t-nmw/s320/Search+For+Artists+Starting+with+F.png" /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/blockquote&gt;Clicking the 'Search' button does two things for us:&lt;br /&gt;&lt;br /&gt;1) It performs the search and gives us the results:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;a href="http://1.bp.blogspot.com/_nyDSKzPKawU/TAQ6N7RjozI/AAAAAAAAAFA/Z_rmoXnjBjs/s1600/Artists+Whose+Name+Starts+with+F.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="217" src="http://1.bp.blogspot.com/_nyDSKzPKawU/TAQ6N7RjozI/AAAAAAAAAFA/Z_rmoXnjBjs/s400/Artists+Whose+Name+Starts+with+F.png" width="400" /&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;2) It shows us the proper format for executing queries against the service (in the browser's address text box):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_nyDSKzPKawU/TAQ6rx0s7UI/AAAAAAAAAFI/1F83rN6sdwM/s1600/Query+String+for+Artists+Whose+Name+Starts+with+F.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="32" src="http://2.bp.blogspot.com/_nyDSKzPKawU/TAQ6rx0s7UI/AAAAAAAAAFI/1F83rN6sdwM/s640/Query+String+for+Artists+Whose+Name+Starts+with+F.png" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Looking at this, we know that we can query for artists by specifying a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;searchType&lt;/span&gt; and a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;searchString&lt;/span&gt; if we search under &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/artists&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;My example music service supports more than HTML for representations. It also supports an XML format and a JSON format. In order to use the XML format, we need to specify that we accept a content type of "application/music-xml" (a content type I made up for this example). This is the second piece of information the service itself did not tell us. However, we could simply query the service and get back an HTML representation of the search results so it's not strictly necessary. The XML format is a more compact representation than the HTML, but the same information is contained in the HTML version.&lt;br /&gt;&lt;br /&gt;If we use the curl command line utility to query:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;curl --header "Accept: application/music-xml" "http://localhost:3131/artists?searchType=STARTS&amp;amp;searchString=F"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We get XML results:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_nyDSKzPKawU/TARLT4ioq-I/AAAAAAAAAGA/iT66rGlCNz8/s1600/XML+for+Artists+starting+with+F.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="196" src="http://2.bp.blogspot.com/_nyDSKzPKawU/TARLT4ioq-I/AAAAAAAAAGA/iT66rGlCNz8/s640/XML+for+Artists+starting+with+F.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Because this service is designed with API discovery in mind, we see that it reminds us of the search that was performed:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt; artists uri="/artists?" searchString="F" searchType="STARTS"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In these results we also see an example of how to build well-connected services. Notice that each search result lists not only the name of the artist found, but also the URI of the resource we would need to retrieve in order to get a representation for that artist. For example, the artist "Fish" lists a URI of&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/artists/id/149&lt;/span&gt;. If we go to the music service in the browser and query for this URI (a URL of &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;http://localhost:3131/artists/id/149&lt;/span&gt;), we get information about the artist named Fish:&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: Times, Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-family: Times;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Times, Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-family: Times;"&gt;&lt;blockquote&gt;&lt;span class="Apple-style-span" style="color: black;"&gt;&lt;span class="Apple-style-span" style="text-decoration: none;"&gt;&lt;a href="http://3.bp.blogspot.com/_nyDSKzPKawU/TARCEYi7lnI/AAAAAAAAAFQ/PQA9tYhvzR0/s1600/Information+about+the+Artist+named+Fish.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="212" src="http://3.bp.blogspot.com/_nyDSKzPKawU/TARCEYi7lnI/AAAAAAAAAFQ/PQA9tYhvzR0/s400/Information+about+the+Artist+named+Fish.png" width="400" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;Looking at the HTML, we see:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_nyDSKzPKawU/TARKjJChb7I/AAAAAAAAAFo/6ZoF_BqyC_I/s1600/HTML+for+Artist+named+Fish.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="452" src="http://4.bp.blogspot.com/_nyDSKzPKawU/TARKjJChb7I/AAAAAAAAAFo/6ZoF_BqyC_I/s640/HTML+for+Artist+named+Fish.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;And if we query for the XML representation using curl:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;curl --header "Accept: application/music-xml" "http://localhost:3131/artists/id/149"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We see:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_nyDSKzPKawU/TARLN3Lv6DI/AAAAAAAAAF4/3UHpJV6FJLs/s1600/XML+for+Artist+named+Fish.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="124" src="http://1.bp.blogspot.com/_nyDSKzPKawU/TARLN3Lv6DI/AAAAAAAAAF4/3UHpJV6FJLs/s640/XML+for+Artist+named+Fish.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Notice that the information about each album contains a URI we can use to retrieve a representation of that album, just like the search results listed a URI for each artist that was found. These are examples of how we build a well-connected RESTful service. By making sure that each HTML page (or XML result) contains URIs for related resources, users of our services can discover the proper URIs/URLs to use when invoking our service without a large specification.&lt;br /&gt;&lt;br /&gt;In part two, I'll explore this notion further, looking at how our self-describing service allows us to discover how to create and delete resources as well as search for and retrieve them.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-6207096546001374454?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/6207096546001374454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=6207096546001374454' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6207096546001374454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6207096546001374454'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/05/i-spoke-at-gateway-software-symposium.html' title=''/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_nyDSKzPKawU/S_6yFQ-_FWI/AAAAAAAAAEo/sFihUtj-05s/s72-c/MusicServiceUML.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-8193142063208125069</id><published>2010-05-22T14:51:00.001-05:00</published><updated>2010-05-22T14:57:01.382-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='No Fluff Just Stuff'/><category scheme='http://www.blogger.com/atom/ns#' term='Gateway Software Symposium'/><title type='text'>Gateway Software Symposium 2010 (NFJS St. Louis)</title><content type='html'>I'm attending (and speaking at) the &lt;a href="http://www.nofluffjuststuff.com/home/main"&gt;NFJS&lt;/a&gt; conference here in &lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/home"&gt;St. Louis this weekend&lt;/a&gt;. The quality of the talks is excellent, and the speakers clearly know their stuff.&lt;br /&gt;&lt;br /&gt;The two talks I'll be delivering on Sunday are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17669"&gt;Open Source Java Performance Tuning&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17649"&gt;RESTful Web Services with JAX-RS&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I hope if you're attending the conference you come to the talks to learn and participate.&lt;br /&gt;&lt;br /&gt;Some of my fellow OCI-ers are also presenting:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17685"&gt;Using Test Doubles with Mockito&lt;/a&gt; - &lt;a href="http://blog.james-carr.org/"&gt;James Carr&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17683"&gt;First-Class Builds with Gradle&lt;/a&gt; - &lt;a href="http://codetojoy.blogspot.com/"&gt;Michael Easter&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=18611"&gt;Clojure - Functional Programming for the JVM&lt;/a&gt; - &lt;a href="http://java.ociweb.com/mark/"&gt;Mark Volkmann&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Sessions I've attended include:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17610"&gt;Open Source Debugging Tools for java&lt;/a&gt;&amp;nbsp;- &lt;a href="http://ambientideas.com/"&gt;Matthew McCullough&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17608"&gt;The Busy Java Developer's Guide to Advanced Collections&lt;/a&gt;&amp;nbsp;- &lt;a href="http://www.tedneward.com/"&gt;Ted Neward&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17565"&gt;Software Architecture for the Cloud&lt;/a&gt; -&amp;nbsp;&lt;a href="http://www.michaelnygard.com/"&gt;Michael Nygard&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17571"&gt;Programming Scala&lt;/a&gt; -&amp;nbsp;&lt;a href="http://www.agiledeveloper.com/"&gt;Venkat Subramaniam&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17618"&gt;Emergent Design&lt;/a&gt; - &lt;a href="http://www.nealford.com/"&gt;Neal Ford&lt;/a&gt;&lt;/li&gt;&lt;li&gt;The Busy Java Developer's Guide to Concurrency &lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17626"&gt;Part 1&lt;/a&gt; and &lt;a href="http://www.nofluffjuststuff.com/conference/st_louis/2010/05/session?id=17627"&gt;Part 2&lt;/a&gt;&amp;nbsp;-&amp;nbsp;&lt;a href="http://www.tedneward.com/"&gt;Ted Neward&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;All good stuff.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8193142063208125069?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8193142063208125069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8193142063208125069' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8193142063208125069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8193142063208125069'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/05/gateway-software-symposium-2010-nfjs-st.html' title='Gateway Software Symposium 2010 (NFJS St. Louis)'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-8981127664498163169</id><published>2010-03-28T09:56:00.000-05:00</published><updated>2010-03-28T09:56:19.078-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='architecture of the web'/><category scheme='http://www.blogger.com/atom/ns#' term='URI fragment'/><category scheme='http://www.blogger.com/atom/ns#' term='URI'/><title type='text'>URI Fragments</title><content type='html'>I learned something interesting when reading the Architecture of the World Wide Web, Volume One. It turns out that URI fragments (the part of a URI after a '#' character) are not interpreted as part of a URI:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Note that the HTML implementation in Emma's browser did not need to understand the syntax or semantics of the SVG fragment (nor does the SVG implementation have to understand HTML, WebCGM, RDF ... fragment syntax or semantics; it merely had to recognize the # delimiter from the URI syntax [URI] and remove the fragment when accessing the resource). This orthogonality (§5.1) is an important feature of Web architecture; it is what enabled Emma's browser to provide a useful service without requiring an upgrade.&lt;br /&gt;&lt;br /&gt;The semantics of a fragment identifier are defined by the set of representations that might result from a retrieval action on the primary resource. The fragment's format and resolution are therefore dependent on the type of a potentially retrieved representation, even though such a retrieval is only performed if the URI is dereferenced. If no such representation exists, then the semantics of the fragment are considered unknown and, effectively, unconstrained. Fragment identifier semantics are orthogonal to URI schemes and thus cannot be redefined by URI scheme specifications.&lt;br /&gt;&lt;br /&gt;Interpretation of the fragment identifier is performed solely by the agent that dereferences a URI; the fragment identifier is not passed to other systems during the process of retrieval. This means that some intermediaries in Web architecture (such as proxies) have no interaction with fragment identifiers and that redirection (in HTTP [RFC2616], for example) does not account for fragments.&lt;/blockquote&gt;&lt;br /&gt;While I had an intuitive understanding of how browsers work with URI fragments in HTML documents (retrieve the document, find the fragment, display the document starting at the fragment), I hadn't considered the semantic split, nor the implications of URI fragments being applied to other kinds of representations.&lt;br /&gt;&lt;br /&gt;I find the handling of fragments interesting in a number of ways. For one thing, it means that as new content types become part of the web, the creators of those content types are free to map URI fragments into that content type. So in HTML the format of URI fragments is generally a textual name that appears in the html. But in a 3D modeling format, it might take the form of [position,orientation,scale] to define a location from which the model is being viewed, the direction the camera is facing, and the scaling factor. That's nice because it allows URI fragments to be structured in a manner most appropriate for the kind of representation being retrieved.&lt;br /&gt;&lt;br /&gt;One possibly surprising consequence of this split is that URI fragments are not considered in URI resolution activities such as interacting with proxies or redirection. It also means that you shouldn't try to use URI's with fragments as if they represented actual resources, since the web isn't allowed to cache individual fragments and no semantic interpretation is allowed from the '#' character onward.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8981127664498163169?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8981127664498163169/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8981127664498163169' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8981127664498163169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8981127664498163169'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/03/uri-fragments.html' title='URI Fragments'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-3931168758604277490</id><published>2010-03-24T23:42:00.000-05:00</published><updated>2010-03-24T23:42:45.201-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='http'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture of the web'/><category scheme='http://www.blogger.com/atom/ns#' term='safe methods'/><category scheme='http://www.blogger.com/atom/ns#' term='rfc 2616'/><category scheme='http://www.blogger.com/atom/ns#' term='good service design'/><title type='text'>Why you should care about adhering to the Architecture of the World Wide Web</title><content type='html'>I was reading more of the &lt;a href="http://www.w3.org/TR/webarch/"&gt;Architecture of the World Wide Web, Volume One&lt;/a&gt;", and&amp;nbsp;&lt;a href="http://www.ietf.org/rfc/rfc2616.txt"&gt;RFC 2616&lt;/a&gt; and the definition of "safe" methods:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;9.1.1 Safe Methods&lt;/blockquote&gt;&lt;blockquote&gt;Implementors should be aware that the software represents the user in&amp;nbsp;their interactions over the Internet, and should be careful to allow&amp;nbsp;the user to be aware of any actions they might take which may have an&amp;nbsp;unexpected significance to themselves or others.&lt;/blockquote&gt;&lt;blockquote&gt;In particular, the convention has been established that the GET and&amp;nbsp;HEAD methods SHOULD NOT have the significance of taking an action&amp;nbsp;other than retrieval. These methods ought to be considered "safe".&amp;nbsp;This allows user agents to represent other methods, such as POST, PUT&amp;nbsp;and DELETE, in a special way, so that the user is made aware of the&amp;nbsp;fact that a possibly unsafe action is being requested.&lt;/blockquote&gt;&lt;blockquote&gt;Naturally, it is not possible to ensure that the server does not&amp;nbsp;generate side-effects as a result of performing a GET request; in&amp;nbsp;fact, some dynamic resources consider that a feature. The important&amp;nbsp;distinction here is that the user did not request the side-effects,&amp;nbsp;so therefore cannot be held accountable for them.&lt;/blockquote&gt;&lt;br /&gt;This matches quite nicely with my intuition about how the web works (at least the normal web). &amp;nbsp;That might seem like something trivial, but in fact it is something profound.&lt;br /&gt;&lt;br /&gt;Unfortunately, many, many web services violate the principle of safe operations. For example, they frequently have all interactions occur via GET, and use query parameters or headers or other conventions to stipulate whether the result is a resource retrieval, creation, update, or delete. This is (unfortunately) only one example of aberrant service design.&lt;br /&gt;&lt;br /&gt;At first, this might not seem like such a problem. But as you start to think about consuming such services or providing support for services you've built, you realize that the conventions of HTTP and the architecture of the web represent a sort of "lingua franca". Rather than making your services do more, you are making your services easier for (potential) consumers of your service to understand and use.&amp;nbsp;If you adhere to the architecture of the web, your services don't do any more than they would have done otherwise. Rather (and this is crucial) you have implemented your services in such a way that users of your services can very easily and rapidly understand their features, capabilities, and limitations. That is a huge advantage in today's fast-moving technology world.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-3931168758604277490?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/3931168758604277490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=3931168758604277490' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/3931168758604277490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/3931168758604277490'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/03/why-you-should-care-about-adhering-to.html' title='Why you should care about adhering to the Architecture of the World Wide Web'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-6858568975788768035</id><published>2010-03-08T23:10:00.004-06:00</published><updated>2010-03-08T23:44:43.247-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ownership'/><category scheme='http://www.blogger.com/atom/ns#' term='www'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture of the web'/><category scheme='http://www.blogger.com/atom/ns#' term='URI'/><category scheme='http://www.blogger.com/atom/ns#' term='allocation'/><title type='text'>Parsing "The Architecture of the World Wide Web, Volume 1"; URI allocation</title><content type='html'>As I continue to read &lt;a href="http://www.w3.org/TR/webarch/"&gt;Architecture of the World Wide Web, Volume I&lt;/a&gt;, I keep running into material that is completely outside of what I would have expected, yet valuable.&lt;br /&gt;&lt;br /&gt;For example, &lt;a href="http://www.w3.org/TR/webarch/#uri-assignment"&gt;Section 2.2.2&lt;/a&gt; talks about URI allocation. Since URIs are supposed to identify a single resource, it becomes important to make sure that the social organizations which allocate and assign URIs are organized so that they don't allocate the same URI to refer to more than one resource. In other words, we want to make sure that we give organizations authority to assign URIs that don't overlap, so that different organizations don't assign the same URI to different resources (sort of like giving the same Social Security number or driver's license number or bank account number to two different people).&lt;br /&gt;&lt;br /&gt;This sort of material may sound obvious when we read it. But it is frequently &lt;span style="font-weight:bold;"&gt;not&lt;/span&gt; obvious to everyone involved in building, deploying, managing, and evolving software systems. In fact, I think failure to make these sorts of issues clear at the architectural and administrative levels is quite possibly the single greatest cause of problems in managing software systems in the real world.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-6858568975788768035?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/6858568975788768035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=6858568975788768035' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6858568975788768035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6858568975788768035'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/03/parsing-architecture-of-world-wide-web.html' title='Parsing &quot;The Architecture of the World Wide Web, Volume 1&quot;; URI allocation'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-6221476707491157300</id><published>2010-03-02T23:15:00.000-06:00</published><updated>2010-03-02T23:15:42.496-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='www'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture of the web'/><category scheme='http://www.blogger.com/atom/ns#' term='URI'/><title type='text'>Principals of the Web</title><content type='html'>As I noted in a &lt;a href="http://viewfromthefringe.blogspot.com/2010/03/architecture-of-world-wide-web-volume-1.html"&gt;previous entry&lt;/a&gt;, I've been reading &lt;a href="http://www.w3.org/TR/webarch/"&gt;Architecture of the World Wide Web, Volume One&lt;/a&gt; and am finding it a great read. For example, take this little gem:&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;Constraint: URIs Identify a Single Resource&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;    Assign distinct URIs to distinct resources.&lt;/blockquote&gt;&lt;br /&gt;In a nutshell the authors have made it clear that a URI should refer to a particular resource. And just a bit further on they point out that URI's can be aliases for a single resource:&lt;br /&gt;&lt;blockquote&gt;Just as one might wish to refer to a person by different names (by full name, first name only, sports nickname, romantic nickname, and so forth), Web architecture allows the association of more than one URI with a resource. URIs that identify the same resource are called &lt;strong&gt;URI aliases&lt;/strong&gt;. The section on URI aliases (§2.3.1) discusses some of the potential costs of creating multiple URIs for the same resource.&lt;/blockquote&gt;&lt;br /&gt;They even offer thoughts on the performance consequences of aliases.&lt;br /&gt;&lt;br /&gt;Ya gotta love it... :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-6221476707491157300?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/6221476707491157300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=6221476707491157300' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6221476707491157300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6221476707491157300'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/03/principals-of-web.html' title='Principals of the Web'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1404552199099748756</id><published>2010-03-01T00:18:00.003-06:00</published><updated>2010-03-01T00:25:10.147-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='www'/><category scheme='http://www.blogger.com/atom/ns#' term='word wide web'/><category scheme='http://www.blogger.com/atom/ns#' term='good documentation'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>The Architecture of the World Wide Web - Volume 1</title><content type='html'>As part of my foray into RESTful services, I've been reading &lt;a href="http://www.w3.org/TR/webarch/"&gt;The Architecture of the World Wide Web - Volume 1&lt;/a&gt; and find it refreshingly informative. For example:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The choice of syntax for global identifiers is somewhat arbitrary; it is their global scope that is important. The Uniform Resource Identifier, [URI], has been successfully deployed since the creation of the Web. There are substantial benefits to participating in the existing network of URIs, including linking, bookmarking, caching, and indexing by search engines, and there are substantial costs to creating a new identification system that has the same properties as URIs.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;And:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;A resource should have an associated URI if another party might reasonably want to create a hypertext link to it, make or refute assertions about it, retrieve or cache a representation of it, include all or part of it by reference into another representation, annotate it, or perform other operations on it. Software developers should expect that sharing URIs across applications will be useful, even if that utility is not initially evident.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;There is so much packed into each of these brief statements, and they are in equivalent of the first 10 pages of the document.&lt;br /&gt;&lt;br /&gt;I find it both amazing and sad that this document was published in 2004 yet I've found very few references to it in the six years since it's publication. Maybe I just haven't been looking in the right places?&lt;br /&gt;&lt;br /&gt;I will share additional passages that I find enlightening in the days (and weeks?) to come.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1404552199099748756?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1404552199099748756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1404552199099748756' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1404552199099748756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1404552199099748756'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/03/architecture-of-world-wide-web-volume-1.html' title='The Architecture of the World Wide Web - Volume 1'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1445715675742485400</id><published>2010-02-27T15:30:00.003-06:00</published><updated>2010-02-27T19:18:16.864-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='GWT'/><category scheme='http://www.blogger.com/atom/ns#' term='support'/><category scheme='http://www.blogger.com/atom/ns#' term='philanthropy'/><category scheme='http://www.blogger.com/atom/ns#' term='documentation'/><category scheme='http://www.blogger.com/atom/ns#' term='SmartGWT'/><title type='text'>GWT and SmartGWT</title><content type='html'>I'm doing some work with &lt;a href="http://code.google.com/webtoolkit/"&gt;GWT 2.0&lt;/a&gt; and also with &lt;a href="http://code.google.com/p/smartgwt/"&gt;SmartGWT&lt;/a&gt;. I like both toolkits, but I'm in the midst of a very steep learning curve (meaning I'm learning a lot quickly). While I'm making good progress in becoming proficient, I'm finding that SmartGWT suffers from the same problem that many open source products with commercial support options suffer from: weak documentation.&lt;br /&gt;&lt;br /&gt;It's completely understandable and I don't blame the developer(s) of SmartGWT. When you are working on an open source project and also trying to provide commercial support as a means of revenue, it's hard to find the time to produce good documentation. And the truth is that if you provide a good product with documentation good enough that developers don't need your services, you have just put yourself out of business.&lt;br /&gt;&lt;br /&gt;Such is the nature of open source projects that have commercial support models as their primary financing model. There's nothing that can be done about it (except find a philanthropist who will fund the project; and philanthropists of any sort are a rare breed these days, not to mention those interested in the the obscurity of open source software).&lt;br /&gt;&lt;br /&gt;So I'll keep climbing the learning curve with the documentation as is, and take good notes for the future when I've stopped working with these technologies and come back to them.&lt;br /&gt;&lt;br /&gt;Oh, and if you know any philanthropists looking to fund open source projects, I've got a project or two of my own I can suggest...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1445715675742485400?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1445715675742485400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1445715675742485400' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1445715675742485400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1445715675742485400'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/02/gwt-and-smartgwt.html' title='GWT and SmartGWT'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-9204629069112070924</id><published>2010-02-10T17:38:00.006-06:00</published><updated>2010-02-10T17:43:30.103-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Guice'/><category scheme='http://www.blogger.com/atom/ns#' term='dependency injection'/><title type='text'>Guice 2.0 - tasty</title><content type='html'>I'm finally getting a chance to do some work with &lt;a href="http://code.google.com/p/google-guice/"&gt;Guice 2.0&lt;/a&gt;. I don't know if I just couldn't wrap my head around Guice in the past and I've finally "gotten" it, or if Guice 2.0 provides a more approachable API. Either way, I'm finding it great to use.&lt;br /&gt;&lt;br /&gt;I've been mildly whiny about Guice in the past, stating that it wasn't completely statically typed (which is true, since it's possible you'll ask for a resource at runtime that isn't available because it wasn't configured). But even with that small limitation, I'm finding Guice 2.0 to be far better than Spring for dependency injection. The code is much smaller, much more type-safe, not XML (a big plus), and much more strongly type-checked.&lt;br /&gt;&lt;br /&gt;If you haven't checked out Guice, or if you tried Guice 1.0 but haven't tried out 2.0, you should give 2.0 a serious look.&lt;br /&gt;&lt;br /&gt;Now I need to integrate Guice with &lt;a href="https://jersey.dev.java.net/"&gt;Jersey&lt;/a&gt; (the JAX-RS reference implementation)...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-9204629069112070924?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/9204629069112070924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=9204629069112070924' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/9204629069112070924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/9204629069112070924'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/02/guice-20-tasty.html' title='Guice 2.0 - tasty'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-2596587269855112108</id><published>2010-02-08T14:21:00.007-06:00</published><updated>2010-02-08T14:41:05.045-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WS-*'/><category scheme='http://www.blogger.com/atom/ns#' term='SOAP'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='SOA'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-WS'/><category scheme='http://www.blogger.com/atom/ns#' term='anti-patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='WSDL'/><title type='text'>JAX-WS tarpit</title><content type='html'>I'm currently doing some work with an open source framework (to be left unnamed) built upon JAX-WS (using SOAP, SAML, and HTTPS). I'm making headway working with it, but talk about a tar pit. You go into the code and it's almost impossible to get out. Every time you gain a bit of understanding, some other anti-pattern slaps you until you see starts and you're back to global searches with google and looking through the code to make further progress.&lt;br /&gt;&lt;br /&gt;Violations of the &lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt; principle are rampant. The generated code has magic constants sprinkled liberally throughout. Generated classes have default constructors and public getters and setters for all fields despite the fact that some fields are actually required; no hashCode; no equals; no ability to determine if one of these data objects is valid or not. Turning the WSDL into code generates vast numbers of classes (reminding me of the terrible mapping from CORBA IDL to Java). The generated code has almost no helpful comments (despite the fact that generating at least some reasonable back-references in generated code is easy precisely because you are generating code). And we've only delved a bit into the SAML aspects of things; I expect that to be another can of worms entirely.&lt;br /&gt;&lt;br /&gt;I've heard that the SOAP/WS-* specifications were co-opted by certain large companies and made so complex it's nearly impossible to work with them except with the very big IDEs-with-god-complexes those vendors sell. Based on these experiences and previous ones working with some SOA frameworks, I can believe it. If that isn't the reason for their incredible opaqueness and anti-patterns, I'm afraid to find out who thought all of this was a good idea.&lt;br /&gt;&lt;br /&gt;So while I'm making progress and getting things done, the entire experience makes me want to go wash my hands and update my resume.&lt;br /&gt;&lt;br /&gt;If you know of good ways to work with this stuff short of plunking down vast quantities of money for some IDE that will sort-of/mostly/kind-of hide all the complexity (until the moment things break and you really need to understand what is going on), I'd love to hear about them...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-2596587269855112108?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/2596587269855112108/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=2596587269855112108' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2596587269855112108'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2596587269855112108'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/02/jax-ws-tarpit.html' title='JAX-WS tarpit'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-8189024736795919422</id><published>2010-02-02T11:35:00.001-06:00</published><updated>2010-02-02T11:35:43.073-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='frameworks'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='danger'/><category scheme='http://www.blogger.com/atom/ns#' term='annotations'/><title type='text'>Java Annotations have Become Pixie Dust</title><content type='html'>I was giving a talk about RESTful services using JAX-RS and Jersey recently and was asked why I had used Mark Volkmann's &lt;a href="http://java.ociweb.com/mark/programming/wax.html"&gt;WAX&lt;/a&gt; for generating HTML and XML. The person asking the question pointed out that Jersey has integration with JAXB.&lt;br /&gt;&lt;br /&gt;There were two answers to that question. One answer is that I am leery of anything which automatically converts my Java objects into a serialized format (bitten by Java's object serialization in the past). Incompatible object changes can be difficult or impossible to reconcile in a backward-compatible manner.&lt;br /&gt;&lt;br /&gt;But the main answer I gave got some chuckles and further questions. I explained I was trying to avoid too much "pixie dust". In the example code, I was already using the Java Persistence API (JPA) and JAX-RS and their associated annotations. If I had not been careful, there would have been annotations for Spring and JAXB as well. All of these annotations are small in the code but have very large effects. Those innocent looking annotations subject my poor unsuspecting code to some very big (and some would argue bad) frameworks. Understanding how these frameworks interact is not only hard, but those interactions change as the frameworks change (possibly resulting in the system breaking with no code changes).&lt;br /&gt;&lt;br /&gt;I have real misgivings about the number of annotation-based technologies that should be applied to any one project. Each annotation you use represents some amount of code you don't have to write. And that is, of course, a good thing from a development perspective. But every annotation you use represents 'pixie dust', behavior which is hidden from you and generally performed at runtime. That means that when things go wrong, there isn't any code to look at. It also means that small differences between configurations can produce large changes in behavior. That's a very bad thing for testing, production deployment, production maintenance, etc.&lt;br /&gt;&lt;br /&gt;I've been thinking about this issue for some time*, so I was pleasantly surprised to find Stephen Schmidt's post admonishing us to &lt;a href="http://codemonkeyism.com/beware-magical-code"&gt;Be careful with magical code&lt;/a&gt;. His post is not specific to annotations (he calls out aspect-oriented programming, for example - I agree that AOP is another kind of pixie dust). And he points out some examples of the "pixie dust" phenomenon. While I don't agree with his 'magic containment' example, it's a good post. You should read it.&lt;br /&gt;&lt;br /&gt;As a rule of thumb, I think two kinds of pixie dust is the maximum to sprinkle on a project. So think hard and choose wisely when picking which ones to use: the more kinds of pixie dust you sprinkle, the harder it will be for you and others to understand and troubleshoot things, now and especially in the future.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;*Thanks to &lt;a href="http://codetojoy.blogspot.com/"&gt;Mike Easter&lt;/a&gt; for planting the idea of talking about the state of annotations in Java&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8189024736795919422?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8189024736795919422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8189024736795919422' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8189024736795919422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8189024736795919422'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/02/java-annotations-have-become-pixie-dust.html' title='Java Annotations have Become Pixie Dust'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-2600550687420546533</id><published>2010-01-08T17:15:00.007-06:00</published><updated>2010-01-20T19:36:23.243-06:00</updated><title type='text'>Taxonomy of Technical Blog Posts</title><content type='html'>I categorize technical blog postings into a taxonomy:&lt;br/&gt;&lt;br /&gt;&lt;div&gt;Type I: Describing how to use some kind of technology, your own or someone else's&lt;/div&gt;&lt;div&gt;Type II: Describing how to overcome some limitation, bug, or quirk of technology&lt;/div&gt;&lt;div&gt;Type III: Whining about failures to get one or more technologies to work (together)&lt;/div&gt;&lt;div&gt;Type IV: Crowing about getting one or more technologies to work (together) - often a follow-up to a Type III posting&lt;/div&gt;&lt;div&gt;Type V: Indulging in a post that really doesn't belong in a technical blog.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For a classic example of a Type II posting, see:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://codetojoy.blogspot.com/2010/01/grails-tip-for-internet-connections.html"&gt;http://codetojoy.blogspot.com/2010/01/grails-tip-for-internet-connections.htm&lt;/a&gt;l&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-2600550687420546533?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/2600550687420546533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=2600550687420546533' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2600550687420546533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2600550687420546533'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2010/01/i-categorize-technical-blog-postings.html' title='Taxonomy of Technical Blog Posts'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-8630694098824095787</id><published>2010-01-05T22:24:00.001-06:00</published><updated>2010-01-05T23:07:58.828-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='example'/><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><title type='text'>A RESTful web service testbed</title><content type='html'>It's time to build a more complete RESTful service example. In this web service, we'll aim to perform all the common activities of a real service. This is often referred to by the acronym CRUD, which stands for &lt;a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;Create, Read, Update, and Delete&lt;/a&gt; for all of the common operations associated with persistent data. Since we're dealing with a RESTful web service, we'll also throw in multiple representations for a resource, and connectedness to make it easy for clients to navigate through the service resources.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, what should our service do? My friend and colleague, &lt;a href="http://java.ociweb.com/mark/"&gt;Mark Volkmann&lt;/a&gt;, introduced the eample of a database of music information. In this example, the service service contains information about several kinds of resources:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Music artists (Artists)&lt;/li&gt;&lt;li&gt;Albums&lt;/li&gt;&lt;li&gt;Songs&lt;/li&gt;&lt;/ul&gt;This is a nice example because it is rich enough to expose many of the problems that need to be solved by RESTful web service without becoming so large that it's unwieldy to explore. The domain model is immediately familiar, so we can focus on the technologies and not the model.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;With this service we can explore not only traditional browser-based HTML, but XML and even an Ajax client using JSON. We can also use it as testbed for things like caching, security, clustering/failover, and composition of services.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8630694098824095787?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8630694098824095787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8630694098824095787' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8630694098824095787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8630694098824095787'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/07/restful-web-service-testbed.html' title='A RESTful web service testbed'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-4680822815826310513</id><published>2009-12-07T23:10:00.003-06:00</published><updated>2009-12-07T23:13:52.784-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JUG'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='St. Louis Java User&apos;s Group'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><category scheme='http://www.blogger.com/atom/ns#' term='JSR-311'/><title type='text'>JAX-RS and Jersey talk materials</title><content type='html'>I recently gave a &lt;a href="http://java.ociweb.com/javasig/knowledgebase/2009-11/index.html"&gt;talk&lt;/a&gt; on JAX-RS, JSR 311, and Jersey at the St. Louis Java User's Group website. You can find the &lt;a href="http://java.ociweb.com/javasig/knowledgebase/2009-11/JAX-RS%20and%20Jersey.pdf"&gt;presentation&lt;/a&gt; and the &lt;a href="http://java.ociweb.com/javasig/knowledgebase/2009-11/JAX-RS-JUG-Nov-09.jar"&gt;sample code&lt;/a&gt; on their &lt;a href="http://java.ociweb.com/javasig/"&gt;website&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-4680822815826310513?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/4680822815826310513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=4680822815826310513' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4680822815826310513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4680822815826310513'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/12/jax-rs-and-jersey-talk-materials.html' title='JAX-RS and Jersey talk materials'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-2458662373784370837</id><published>2009-11-13T10:25:00.015-06:00</published><updated>2009-11-13T11:11:44.019-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='curl'/><category scheme='http://www.blogger.com/atom/ns#' term='POST'/><category scheme='http://www.blogger.com/atom/ns#' term='GET'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='303'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac OS X'/><category scheme='http://www.blogger.com/atom/ns#' term='http redirect'/><title type='text'>Curl has a bug with redirects?</title><content type='html'>&lt;div style="font-size:medium;"&gt;I gave a presentation at the JUG last night about JAX-RS and Jersey. The talk went well, and it was a great crowd with a lot of participation (I'll post more once I have the materials cleaned up and they are posted to the JUG website).&lt;br /&gt;&lt;br /&gt;But one thing that didn't go right last night was when I was using the 'curl' command line tool to create new artists in the RESTful music database service I was using as an example.&lt;br /&gt;&lt;br /&gt;The curl man page on my MacBook Pro says:&lt;/div&gt;&lt;br /&gt;&lt;div style="font-family:'Times New Roman';font-size: medium;"&gt;&lt;i&gt;When  curl follows a redirect and the request is not a plain GET (for example POST or PUT), it will do the following request with a GET if the HTTP response was 301, 302, or 303. If the response code was any other 3xx code, curl  will  re-send  the  following request using the same unmodified method.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Yet, when I was using it, it wasn't doing that. For example when trying to create artist 'Jane':&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font-size:small; font-family:'courier new';"&gt;&lt;br /&gt;brian@Widget: curl -v -H "Accept: text/html" -X POST -L "http://localhost:3131/artists/Jane"&lt;br /&gt;* About to connect() to localhost port 3131 (#0)&lt;br /&gt;*   Trying ::1... Connection refused&lt;br /&gt;*   Trying fe80::1... Connection refused&lt;br /&gt;*   Trying 127.0.0.1... connected&lt;br /&gt;* Connected to localhost (127.0.0.1) port 3131 (#0)&lt;br /&gt;&gt; POST /artists/Jane HTTP/1.1&lt;br /&gt;&gt; User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3&lt;br /&gt;&gt; Host: localhost:3131&lt;br /&gt;&gt; Accept: text/html&lt;br /&gt;&gt; &lt;br /&gt;&lt; HTTP/1.1 303 See Other&lt;br /&gt;&lt; server: grizzly/1.9.10&lt;br /&gt;&lt; Location: http://localhost:3131/artists/id/313&lt;br /&gt;&lt; Content-Type: text/plain; charset=iso-8859-1&lt;br /&gt;&lt; Content-Length: 0&lt;br /&gt;&lt; Date: Fri, 13 Nov 2009 05:24:12 GMT&lt;br /&gt;&lt; &lt;br /&gt;* Connection #0 to host localhost left intact&lt;br /&gt;* Issue another request to this URL: 'http://localhost:3131/artists/id/313'&lt;br /&gt;* Re-using existing connection! (#0) with host localhost&lt;br /&gt;* Connected to localhost (127.0.0.1) port 3131 (#0)&lt;br /&gt;&gt; POST /artists/id/313 HTTP/1.1&lt;br /&gt;&gt; User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3&lt;br /&gt;&gt; Host: localhost:3131&lt;br /&gt;&gt; Accept: text/html&lt;br /&gt;&gt; &lt;br /&gt;&lt; HTTP/1.1 405 Method Not Allowed&lt;br /&gt;&lt; server: grizzly/1.9.10&lt;br /&gt;&lt; Allow: DELETE,OPTIONS,HEAD,GET&lt;br /&gt;&lt; Content-Type: text/plain; charset=iso-8859-1&lt;br /&gt;&lt; Content-Length: 0&lt;br /&gt;&lt; Date: Fri, 13 Nov 2009 05:24:12 GMT&lt;br /&gt;&lt; &lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font-size:medium;"&gt;When it followed the redirect and issued the follow-up request, it went to the right place:&lt;/div&gt;&lt;br /&gt;&lt;div style="font-size:small; font-family:'courier new';"&gt;&lt;br /&gt;...&lt;br /&gt;&lt; HTTP/1.1 &lt;strong&gt;303 See Other&lt;/strong&gt;&lt;br /&gt;&lt; server: grizzly/1.9.10&lt;br /&gt;&lt; Location: &lt;strong&gt;&lt;span style="color= #5555CC;"&gt;http://localhost:3131/artists/id/313&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;...&lt;br /&gt;* Connection #0 to host localhost left intact&lt;br /&gt;* Issue another request to this URL: '&lt;strong&gt;&lt;span style="color = #6600CC;"&gt;http://localhost:3131/artists/id/313&lt;/span&gt;&lt;/strong&gt;'&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font-size:medium;"&gt;But it didn't switch to GET:&lt;br /&gt;&lt;/div&gt;&lt;div style="font-size:small; font-family:'courier new';"&gt;&lt;br /&gt;&gt; &lt;strong&gt;&lt;span style="color:#FF0000;"&gt;POST&lt;/span&gt;&lt;/strong&gt; /artists/id/313 HTTP/1.1&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font-size:medium;"&gt;As a result, I got a 405 error code because curl tried to do a POST to the redirected URL rather than doing a GET as is documented.&lt;br /&gt;&lt;br /&gt;I did some further experiments last evening after the talk and confirmed this behavior. I even tried switching the return code to a 302 just to see if it would make a difference and it doesn't.&lt;br /&gt;&lt;br /&gt;How does your curl behave? Does it switch from POST to GET when following a redirect using the -L option?&lt;br /&gt;&lt;br /&gt;I'd appreciate it if anyone who knows anything about this could comment. Is curl doing the wrong thing, or is the man page wrong and curl is no longer supposed to do this?&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-2458662373784370837?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/2458662373784370837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=2458662373784370837' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2458662373784370837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2458662373784370837'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/11/curl-has-bug-with-redirects.html' title='Curl has a bug with redirects?'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1733437357564833496</id><published>2009-07-21T22:00:00.023-05:00</published><updated>2009-07-21T23:53:40.735-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RESTful'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='web services'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><category scheme='http://www.blogger.com/atom/ns#' term='client'/><title type='text'>RESTful clients with the Jersey Client API</title><content type='html'>&lt;div&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;[Note: This is one in a &lt;/span&gt;&lt;/span&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/06/jax-rs-and-jersey-posts.html"&gt;&lt;span style="color:#55178d;"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;series of posts&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt; about JAX-RS. You may wish to start at &lt;/span&gt;&lt;/span&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/05/exploring-restful-web-services-with-jax.html"&gt;&lt;span style="color:#55178d;"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;the beginning&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia; min-height: 16.0px"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="color:#55198c;"&gt;&lt;a href="https://jersey.dev.java.net/"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Jersey&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;, the reference implementation of the JAX-RS specification, is a framework that makes it very easy to implement RESTful web services. But there is more to Jersey than just the server side. In this post I will spend a little time exploring the Jersey client library.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia; min-height: 16.0px"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Depending upon how you design your RESTful service, you may or may not want to have separate URLs for separate representations of the same resource. This presents a problem when trying to test with a browser. There is no way to tell popular browsers that you want a text/html representation or an image/jpeg representation. The browser has a list of preferred media types, but none that I'm aware of allow you to customize this (either in general or for a particular request). Even more importantly, we need to be able to build solid unit tests for our services. The Jersey client framework provides a good solution to this problem. It is designed to allow developers of RESTful web services to write good unit tests, but is more general purpose than that. It can also be used to write RESTful client applications.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Times New Roman; min-height: 16.0px"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;There is an &lt;/span&gt;&lt;/span&gt;&lt;a href="https://www.sun.com/offers/details/Java_Jersey_Client.xml"&gt;&lt;span style="letter-spacing: 0.0px ;color:#00009d;"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;excellent tutorial&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt; on the Jersey client API, which you should download and read if you plan to use it. But I will give you a taste of the API here.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia; min-height: 16.0px"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;The Jersey client API is very clean, and requires a minimum of fuss to use. As an example, let's write a unit test which tests our web service serving up information about fighter planes. First, we write the code to set up and tear down our service implementation. This code is the same as that in our Main class before, just split up between the setup and tear-down methods.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;public class Test3b {&lt;br /&gt;&lt;br /&gt;private SelectorThread selector;&lt;br /&gt;&lt;br /&gt;@org.junit.Before&lt;br /&gt;public void createService() throws IOException {&lt;br /&gt;   Map&lt;string, string=""&gt; initParams = new HashMap&lt;string, string=""&gt;();&lt;br /&gt;   initParams.put("com.sun.jersey.config.property.packages", "net.gilstraps.server");&lt;br /&gt;   selector = GrizzlyWebContainerFactory.create("http://localhost:9998/", initParams);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@org.junit.After&lt;br /&gt;public void DestroyService() {&lt;br /&gt;   selector.stopEndpoint();&lt;br /&gt;   selector = null;&lt;br /&gt;}&lt;br /&gt;&lt;/string,&gt;&lt;/string,&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;The only difference in this case is we don't read from standard in to shut down the service, since we always want to shut it down at the end of the unit test.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Times New Roman; min-height: 16.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Now, we can test that the text/html representation returned by invoking the service is what we expect. First, we do a bit of hoop jumping to read in a copy of the HTML we expect to receive:&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;@org.junit.Test&lt;br /&gt;public void testF22Html() {&lt;br /&gt;try {&lt;br /&gt;   File expected = new File("f22.html");&lt;br /&gt;   long fileSize = expected.length();&lt;br /&gt;   if (fileSize &gt; Integer.MAX_VALUE) {&lt;br /&gt;       throw new IllegalArgumentException("File it larger than a StringWriter can hold");&lt;br /&gt;   }&lt;br /&gt;   int size = (int) fileSize;&lt;br /&gt;   BufferedReader r = new BufferedReader(new FileReader(expected), size);&lt;br /&gt;   char[] chars = new char[size];&lt;br /&gt;   int readChars = r.read(chars);&lt;br /&gt;   if (readChars != size) {&lt;br /&gt;       throw new RuntimeException("Failed to read all chars of the expected result html file");&lt;br /&gt;   }&lt;br /&gt;   final String expectedText = new String(chars);&lt;br /&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;At this point, the variable expectedText contains what we should receive back from our request. Making the request is quite straightforward. First, we create a JAX-RS client:&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;Client client = new Client();&lt;br /&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;These clients are 'heavyweight' objects (taking time to create and using significant resources). As such, in a production client we would want to create this client once and use it many times. For the sake of independent unit tests, we will go ahead and create a Client object for each test.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Times New Roman; min-height: 16.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Next, we specify the resource we want to retrieve using a WebResource object:&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;WebResource f22 = client.resource("http://localhost:9998/planes/f22/f22.html");&lt;br /&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;And then we ask the client to retrieve the resource for us, specifying that we want a text/html representation (MediaType.TEXT_HTML_TYPE) and specifying that we want to get back a ClientResponse object:&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia; min-height: 16.0px"&gt;&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;ClientResponse response =&lt;br /&gt;f22.accept(MediaType.TEXT_HTML_TYPE).get(ClientResponse.class);&lt;br /&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;This code is using the builder pattern, where we build up our request through a chain of method calls. In this case, the chain is only two calls long. First, we call the accept method to specify the media types we will accept in the response (&lt;/span&gt;&lt;i&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;text/html&lt;/span&gt;&lt;/i&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;), then we call the get method to actually retrieve the resource. It is possible to chain together more calls to specify other characteristics of either the request or the expected response (for more information, see the whitepaper on the Jersey client API).&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Times New Roman; min-height: 16.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Now that we have gotten a representation of the resource in the form of an HTTP response, we can then retrieve the HTML entity contained within that response as a string:&lt;/span&gt;&lt;/span&gt;&lt;/p&gt; &lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Times New Roman; min-height: 16.0px"&gt;&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;String returnedHTML = response.getEntity(String.class);&lt;br /&gt;&lt;pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;And finally, since this is a unit test, we make sure that what we got back matches what we expected:&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;assertEquals("Expected and actual HTML don't match"&lt;br /&gt;expectedText, returnedHTML);&lt;br /&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span class="Apple-style-span" style="white-space: pre-wrap; "&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;The ClientResponse object is useful if you want to look at other characteristics of the response, such as the returned headers. In this case, we could just as well have asked for the string from the WebResource directly. To do so, we would replace these two lines:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;pre&gt;ClientResponse response =&lt;br /&gt;f22.accept(MediaType.TEXT_HTML_TYPE).get(ClientResponse.class);&lt;br /&gt;String returnedHTML = response.getEntity(String.class);&lt;br /&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Georgia"&gt;&lt;span style="letter-spacing: 0.0px"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;With this one:&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:monospace, fantasy;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;String returnedHTML = asHTML.accept(MediaType.TEXT_HTML_TYPE).get(String.class);&lt;br /&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 0.0px 0.0px; text-align: justify; font: 14.0px Georgia"&gt;&lt;/p&gt;&lt;p  style="margin: 0.0px 0.0px 0.0px 0.0px; text-align: justify; font: 14.0px Georgia; color:#333333;"&gt;&lt;span class="Apple-style-span"    style="font-family:monospace, -webkit-fantasy;font-size:100%;color:#000000;"&gt;&lt;span class="Apple-style-span"  style="font-size:13px;"&gt;&lt;span class="Apple-style-span"   style="font-family:Georgia, -webkit-fantasy;font-size:130%;"&gt;&lt;span class="Apple-style-span"  style="font-size:14px;"&gt;&lt;span class="Apple-style-span"   style="  white-space: normal; font-family:Georgia, fantasy;font-size:16px;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;span class="Apple-style-span"    style="font-family:monospace, -webkit-fantasy;font-size:100%;color:#000000;"&gt;&lt;span class="Apple-style-span"   style="font-family:Georgia, -webkit-fantasy;font-size:130%;"&gt;&lt;pre&gt;&lt;pre&gt;&lt;p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font: normal normal normal 14px/normal Georgia; "&gt;&lt;span class="Apple-style-span" style="white-space: pre-wrap; "&gt;&lt;span class="Apple-style-span"  style="font-family:georgia;"&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Testing for retrieval of the JPEG representation of an image is almost identical. The only differences are that we read in the image file as an array of bytes, ask for the response entity as an array of bytes, and compare the two as arrays of bytes. Here is the entire test method:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p  style="margin: 0.0px 0.0px 0.0px 0.0px; text-align: justify; font: 14.0px Georgia; color:#333333;"&gt;&lt;span class="Apple-style-span"   style="color: rgb(0, 0, 0);   font-family:monospace, fantasy;font-size:13px;"&gt;&lt;span class="Apple-style-span"   style="  ;font-family:Georgia, fantasy;font-size:14px;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;@org.junit.Test&lt;br /&gt;public void testF22JPEG() {&lt;br /&gt;  try {&lt;br /&gt;      // Read in our expected result&lt;br /&gt;      File imageFile = new File("f22.jpg");&lt;br /&gt;      long fileSize = imageFile.length();&lt;br /&gt;      if (fileSize &gt; Integer.MAX_VALUE) {&lt;br /&gt;          throw new IllegalArgumentException("File it larger than a StringWriter can hold");&lt;br /&gt;      }&lt;br /&gt;      int size = (int) fileSize;&lt;br /&gt;      byte[] expectedBytes = new byte[size];&lt;br /&gt;      BufferedInputStream bis = new BufferedInputStream(new FileInputStream(imageFile), size);&lt;br /&gt;      int bytesRead = bis.read(expectedBytes);&lt;br /&gt;      assertEquals(size, bytesRead);&lt;br /&gt;&lt;br /&gt;      // Request the representation&lt;br /&gt;      Client client = new Client();&lt;br /&gt;      WebResource f22 = client.resource("http://localhost:9998/planes/f22/f22.jpg");&lt;br /&gt;      ClientResponse response = f22.accept(MediaType.WILDCARD).get(ClientResponse.class);&lt;br /&gt;      MultivaluedMap&lt;string,string&gt; headers = response.getHeaders();&lt;br /&gt;      for ( String key : headers.keySet() ) {&lt;br /&gt;          System.out.println( key + "=" + headers.get(key) );&lt;br /&gt;      }&lt;br /&gt;      byte[] returnedBytes = new byte[0];&lt;br /&gt;      returnedBytes = response.getEntity(returnedBytes.getClass());&lt;br /&gt;&lt;br /&gt;      // Compare the two sets of bytes to make sure they match&lt;br /&gt;      assertEquals(size, returnedBytes.length);&lt;br /&gt;      assertTrue(Arrays.equals(expectedBytes,returnedBytes));&lt;br /&gt;  }&lt;br /&gt;  catch (FileNotFoundException e) {&lt;br /&gt;      AssertionError ae = new AssertionError("File containing expected HTML not found!");&lt;br /&gt;      ae.initCause(e);&lt;br /&gt;      throw ae;&lt;br /&gt;  }&lt;br /&gt;  catch (IOException e) {&lt;br /&gt;      AssertionError ae = new AssertionError("Problems reading expected text!");&lt;br /&gt;      ae.initCause(e);&lt;br /&gt;      throw ae;&lt;br /&gt;  }&lt;br /&gt;}&lt;/string,string&gt;&lt;/pre&gt;&lt;p style="margin: 0.0px 0.0px 16.0px 0.0px; font: 16.0px Georgia; min-height: 19.0px"&gt;&lt;span class="Apple-style-span" style="white-space: normal; "&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;&lt;pre&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:georgia, fantasy;"&gt;&lt;span class="Apple-style-span" style="white-space: pre-wrap; "&gt;&lt;span class="Apple-style-span"  style="font-size:medium;"&gt;Just like the core of Jersey, the client library enables clear, short code that clearly expresses what the developer is trying to do. This is a marked departure from many other web frameworks and APIs. It is refreshing to be able to write web services clients in such a terse, yet clear style.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:georgia, -webkit-fantasy;font-size:130%;"&gt;&lt;span class="Apple-style-span" style="font-size: 16px; white-space: pre-wrap;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:georgia, -webkit-fantasy;font-size:130%;"&gt;&lt;span class="Apple-style-span" style="font-size: 16px; white-space: pre-wrap;"&gt;[Note: I plan to continue working with Jersey and continue posting about it. But I'm going to take a few weeks to look into some other topics first. I'll make sure to update the &lt;a href="http://viewfromthefringe.blogspot.com/2009/06/jax-rs-and-jersey-posts.html"&gt;list of JAX-RS and Jersey posts&lt;/a&gt; as I add more]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;&lt;pre&gt;&lt;p&gt;&lt;/p&gt;&lt;/pre&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1733437357564833496?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1733437357564833496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1733437357564833496' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1733437357564833496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1733437357564833496'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/07/restful-clients-with-jersey-client-api.html' title='RESTful clients with the Jersey Client API'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1045561786901373837</id><published>2009-07-09T22:12:00.001-05:00</published><updated>2009-07-21T23:52:15.458-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='entity provider'/><category scheme='http://www.blogger.com/atom/ns#' term='RESTful'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='response'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><title type='text'>JAX-RS Response Entity Providers</title><content type='html'>&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_nyDSKzPKawU/SlY9vluXLxI/AAAAAAAAAEc/Lpk7yyTZKLA/s1600-h/EntityConversions.jpg"&gt;&lt;/a&gt;[Note: This is one in a &lt;a href="http://viewfromthefringe.blogspot.com/2009/06/jax-rs-and-jersey-posts.html"&gt;series of posts&lt;/a&gt; about JAX-RS. You may wish to start at &lt;a href="http://viewfromthefringe.blogspot.com/2009/05/exploring-restful-web-services-with-jax.html"&gt;the beginning&lt;/a&gt;]&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you read my &lt;a href="http://viewfromthefringe.blogspot.com/2009/07/multiple-representations-of-rest.html"&gt;previous entry about returning more than one representation of a resource&lt;/a&gt;, you saw this sample code for a RESTful F-22 fighter plane resource:&lt;/div&gt;&lt;div&gt;&lt;pre&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;@Path("/planes/f22/")&lt;/div&gt;&lt;div&gt;public class F22 {&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;@GET @Produces("text/html") @Path( "f22.html" )&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;public File getHTMLRepresentation() throws IOException {&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;return new File( "f22.html");&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;@GET @Produces("image/jpeg") @Path( "f22.jpg" )&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;public File getImageRepresentation() {&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;return new File( "f22.jpg");&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;}&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;These methods might seem a bit odd at first sight. The &lt;code&gt;getHTMLRepresentation&lt;/code&gt; method has a &lt;code&gt;@Produces&lt;/code&gt; annotation which indicates the method produces a MIME type of &lt;i&gt;"text/html"&lt;/i&gt;, but the return type of the Java method is &lt;code&gt;java.io.File&lt;/code&gt;. Similarly, the &lt;code&gt;getImageRepresentation&lt;/code&gt; method produces &lt;i&gt;"image/jpeg"&lt;/i&gt; but the Java method also returns a &lt;code&gt;File&lt;/code&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This apparent disconnect is resolved by a JAX-RS feature called entity providers. The specification states that entity providers "supply mapping services between representations and their associated Java types". The job of entity providers is to take HTTP request entities and turn them into Java objects on the request side, and take Java objects and turn them into response entities on the result side. Entity providers which convert request entities to Java types implement the &lt;code&gt;MessageBodyReader&lt;/code&gt; interface and must be marked with the &lt;code&gt;@Provider&lt;/code&gt; annotation. Similarly, entity providers which convert Java return types to response entities implement the &lt;code&gt;MessageBodyWriter&lt;/code&gt; interface and are marked with the &lt;code&gt;@Provider&lt;/code&gt; annotation (see Figure 1).&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://2.bp.blogspot.com/_nyDSKzPKawU/SlY9vluXLxI/AAAAAAAAAEc/Lpk7yyTZKLA/s320/EntityConversions.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5356536694593498898" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 269px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Figure 1: Conversion between HTTP entities and Java objects&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;JAX-RS mandates a set of built-in entity providers covering common entity types such as strings, input streams, java.io.Reader objects, java.io.File objects, and a number of other sophisticated types of conversions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In this case, Jersey takes the returned &lt;code&gt;File&lt;/code&gt; object and uses it’s contents as the response entity which it marks as &lt;i&gt;“text/html”&lt;/i&gt;. This mapping from Java types to HTTP entities can be quite powerful, as you don’t have to write code to read the contents of files and return them as byte arrays, Strings, etc. It also offers a JAX-RS implementation the opportunity to implement handling of some types much more efficiently. For example, a JAX-RS implementation might choose to use Java’s NIO facilities to read the contents of a file for performance reasons.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This notion of entity providers is a crucial aid to the POJO (Plain Old Java Objects) style of JAX-RS applications, and avoids what would otherwise be a great deal of boilerplate code in the application performing these conversions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[Next: &lt;a href="http://viewfromthefringe.blogspot.com/2009/07/restful-clients-with-jersey-client-api.html"&gt;RESTful clients with the Jersey Client API&lt;/a&gt;]&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1045561786901373837?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1045561786901373837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1045561786901373837' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1045561786901373837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1045561786901373837'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/07/jax-rs-response-entity-providers.html' title='JAX-RS Response Entity Providers'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_nyDSKzPKawU/SlY9vluXLxI/AAAAAAAAAEc/Lpk7yyTZKLA/s72-c/EntityConversions.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-9148439202244963912</id><published>2009-07-08T14:56:00.008-05:00</published><updated>2009-07-21T23:51:30.066-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='resources'/><category scheme='http://www.blogger.com/atom/ns#' term='representations'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><title type='text'>Multiple representations of a REST Resource using JAX-RS</title><content type='html'>[Note: I have a &lt;a href="http://viewfromthefringe.blogspot.com/2009/06/jax-rs-and-jersey-posts.html"&gt;post&lt;/a&gt; that provides an in-order list of all the JAX-RS and Jersey-related posts I've written. You may want to start at the beginning if you haven't already read the previous posts]&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One of the principles of RESTful web services is that a client makes a request for a representation of a resource. The resource itself is an abstract notion, just as an object-oriented model of some real world object is an abstraction. But in the case of a RESTful web service, it becomes possible to return different kinds of information based upon what the client requests.&lt;br /&gt;&lt;br /&gt;As a simple example, imagine we are aficionados of military planes and decide to create a web service which provides non-secret information (and perhaps speculation) about various military aircraft (thanks to my friend Bruce for this idea). In the REST world, our resource might be "F-22". In this case the resource is the United States F-22 fighter plane. But there are many possible representations of an F-22. We might provide a summary description of the plane, or an image of the plane, or a CAD schematic of the plane, or any of a number of other representations. This basic notion that a single resource may have more than representation can be demonstrated with a simple object that can provide either an HTML text description of the F-22 or a simple graphic drawing of the plane.&lt;br /&gt;&lt;br /&gt;Perhaps the simplest way to provide more than one representation for a given resource is to provide two different methods in a Java class with different MIME types in their @Produces annotations. In our example, assume we have a class named F22 which provides representations of an F-22. It can have a method that returns HTML and a different method that returns a JPEG image. This is very easy to do with JAX-RS:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@Path("/planes/f22")&lt;br /&gt;public class F22 {&lt;br /&gt;@GET @Produces("text/html")&lt;br /&gt;public String getHTMLRepresentation() throws IOException {&lt;br /&gt;    File f = new File( "F22.html");&lt;br /&gt;    BufferedReader br = new BufferedReader(new FileReader(f));&lt;br /&gt;    StringWriter sw = new StringWriter();&lt;br /&gt;    for( String s = br.readLine(); s != null ; s = br.readLine() ) {&lt;br /&gt;        sw.write( s );&lt;br /&gt;        sw.write( '\n' );&lt;br /&gt;    }&lt;br /&gt;    return sw.toString();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@GET @Produces("image/jpeg")&lt;br /&gt;public File getImageRepresentation() {&lt;br /&gt;    return new File( "F22.jpg");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We now run into a problem using a web browser for a testing client. The browser is going  to specify in it’s HTTP request the formats it prefers for a response. The browser doesn’t allow us to specify the type of representation we want. So, do we get back the HTML representation or the JPEG image? The answer depends upon the particular web browser you are using. Firefox 3.5 on a Mac and Windows and Safari on a Mac return the HTML form:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_nyDSKzPKawU/SlT6_ss-HRI/AAAAAAAAAEM/f3Bni9Ag1GY/s1600-h/safari-html.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 268px;" src="http://3.bp.blogspot.com/_nyDSKzPKawU/SlT6_ss-HRI/AAAAAAAAAEM/f3Bni9Ag1GY/s320/safari-html.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5356181829088845074" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;IE 6 on Windows and IE 8 on Windows return the JPEG image:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_nyDSKzPKawU/SlT8tyBw49I/AAAAAAAAAEU/-KGsSmD4qx4/s1600-h/IE-image.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 257px;" src="http://2.bp.blogspot.com/_nyDSKzPKawU/SlT8tyBw49I/AAAAAAAAAEU/-KGsSmD4qx4/s320/IE-image.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5356183720303846354" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There are other ways to return more than on representation. For example, can have a single method which returns a JAX-RS Response object. That Response can have different contents depending upon the requested representation. I may explore these in future posts.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[Next: &lt;a href="http://viewfromthefringe.blogspot.com/2009/07/jax-rs-response-entity-providers.html"&gt;JAX-RS Response Entity Providers&lt;/a&gt;]&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-9148439202244963912?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/9148439202244963912/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=9148439202244963912' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/9148439202244963912'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/9148439202244963912'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/07/multiple-representations-of-rest.html' title='Multiple representations of a REST Resource using JAX-RS'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_nyDSKzPKawU/SlT6_ss-HRI/AAAAAAAAAEM/f3Bni9Ag1GY/s72-c/safari-html.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-8507952372749115696</id><published>2009-06-12T21:14:00.007-05:00</published><updated>2009-07-21T23:53:53.075-05:00</updated><title type='text'>JAX-RS and Jersey posts</title><content type='html'>&lt;span class="Apple-style-span" style="font-size: medium;"&gt;I'm writing a series of posts on JAX-RS and Jersey (the reference implementation for JAX-RS). I'll be updating this post to provide links to all the others. This post gives you "one stop shopping" for all the related posts.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/05/exploring-restful-web-services-with-jax.html"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;Introduction&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/06/setting-up-jersey-for-use.html"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;Setting up Jersey for Use&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/06/jax-rs-hello-world.html"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;Hello World using JAX-RS and Jersey&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="color: rgb(204, 102, 0); line-height: 25px; "&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/07/multiple-representations-of-rest.html"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;Multiple representations of a REST Resource using JAX-RS&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style=" color: rgb(51, 51, 51); "&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/07/jax-rs-response-entity-providers.html"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;JAX-RS Response Entity Providers&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="color: rgb(204, 102, 0); "&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/07/restful-clients-with-jersey-client-api.html"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;RESTful clients with the Jersey Client API&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span class="Apple-style-span" style="color: rgb(204, 102, 0); line-height: 25px; "&gt;&lt;p  style="margin: 0.0px 0.0px 0.0px 0.0px; line-height: 25.0px; font: 18.0px Georgia; color:#333333;"&gt;&lt;span class="Apple-style-span"  style=" ;font-size:13px;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3 class="post-title entry-title" style="margin-top: 0.25em; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 4px; padding-left: 0px; font-size: 18px; font-weight: normal; line-height: 1.4em; color: rgb(204, 102, 0); "&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/07/restful-clients-with-jersey-client-api.html" style="color: rgb(51, 51, 51); text-decoration: none; display: block; font-weight: normal; "&gt;&lt;/a&gt;&lt;/h3&gt;&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8507952372749115696?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8507952372749115696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8507952372749115696' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8507952372749115696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8507952372749115696'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/06/jax-rs-and-jersey-posts.html' title='JAX-RS and Jersey posts'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-910793106040087686</id><published>2009-06-08T22:24:00.013-05:00</published><updated>2009-07-21T23:50:20.611-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RESTful'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='hello world'/><category scheme='http://www.blogger.com/atom/ns#' term='web services'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><title type='text'>JAX-RS Hello World</title><content type='html'>[NOTE: All of my examples will use standalone services (not housed inside a container). The integration between Jersey and the Grizzly HTTP server makes this a snap]&lt;br /&gt;&lt;br /&gt;So, what is the simplest, smallest hello world we can implement with JAX-RS? The Jersey site provides an example and it's surprisingly small. We will start with an example almost identical to the hello world example from the Jersey documentation and grow from there.&lt;br /&gt;&lt;br /&gt;If we ignore the package statements and imports, there are two files of about ten lines each. The first one is our main class that starts up the service and generally gets things going. Later examples will show why this is it's own class separate from the rest of a service's implementation. Here's the class in its entirety:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;package net.gilstraps.server;&lt;br /&gt;&lt;br /&gt;import com.sun.grizzly.http.SelectorThread;&lt;br /&gt;import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.util.HashMap;&lt;br /&gt;import java.util.Map;&lt;br /&gt;&lt;br /&gt;public class Main {&lt;br /&gt;&lt;br /&gt;   public static void main(String[] args) throws IOException {&lt;br /&gt;       Map&amp;lt;String,String&amp;gt; initParams = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt;       initParams.put( "com.sun.jersey.config.property.packages", "net.gilstraps.server" );&lt;br /&gt;       SelectorThread selector = GrizzlyWebContainerFactory.create( "http://localhost:9998/", initParams );&lt;br /&gt;       //noinspection ResultOfMethodCallIgnored&lt;br /&gt;       System.in.read();&lt;br /&gt;       threadSelector.stopEndpoint();&lt;br /&gt;       System.exit(0);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The code here is pretty easy to understand, so let's look at it. These two lines are a crucial part of configuring Jersey:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;       Map&amp;lt;String,String&amp;gt; initParams = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt;       initParams.put( "com.sun.jersey.config.property.packages", "net.gilstraps.server" );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With these lines, we tell Jersey which java packages should be examined at runtime to find classes which implement RESTful resources. We've specified that Jersey should look at the "net.gilstraps.server" package and examine the classes in that package to find ones which have JAX-RS annotations on them. Those annotations tell a JAX-RS implementation what resources each class serves up to clients and what URI's those resources have. There are also annotations for specifying which HTTP methods match up to which Java methods. We'll look at these shortly.&lt;br /&gt;&lt;br /&gt;The next line actually instantiates the Grizzly HTTP server, telling it the base URI for all the URL's it should support and providing it with initialization parameters:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;       SelectorThread selector = GrizzlyWebContainerFactory.create( "http://localhost:9998/", initParams );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The call returns us a SelectorThread which represents the control object for the HTTP server. With the string "http://localhost:9998/", we are telling the service to use port 9998 of the host named 'localhost'. Grizzly has built-in knowledge of Jersey and our inclusion of the "com.sun.jersey.config.property.packages" parameter causes Jersey to used to provide the web service.&lt;br /&gt;&lt;br /&gt;The remaining lines provide a simple means to shut down the service gracefully. The program reads input from System.in and then shuts things down cleanly. So if you run the program on a command line you can get it to shut down without killing it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;       System.in.read();&lt;br /&gt;       threadSelector.stopEndpoint();&lt;br /&gt;       System.exit(0);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, where is our cliched "hello world" resource? There is one more class used to implement our service, also quite small. Here it is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;package net.gilstraps.server;&lt;br /&gt;&lt;br /&gt;import javax.ws.rs.Produces;&lt;br /&gt;import javax.ws.rs.GET;&lt;br /&gt;import javax.ws.rs.Path;&lt;br /&gt;import javax.swing.text.html.HTML;&lt;br /&gt;&lt;br /&gt;@Path("/helloworld")&lt;br /&gt;public class HelloWorldResource {&lt;br /&gt;   @GET @Produces("text/plain")&lt;br /&gt;   public String getClichedMessage() {&lt;br /&gt;       return "Hello Stage 1";&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This class is almost a POJO (plain old Java object). It doesn't implement a particular interface, or extend a class. It has a very simple method ('getClichedMessage') that returns a regular Java string. How can this implement a hello world web service? The key to making it a JAX-RS resource class is the annotations. Let's examine each one:&lt;br /&gt;&lt;br /&gt;@Path&lt;br /&gt;&lt;br /&gt;The '@Path' annotation attached to the class itself tells a JAX-RS what URI or set of URIs this class serves. In this case, we are specifying just the URI of "/helloworld". Thus, with the base URI of "http://localhost:9998" we would access this resource via the URI "http://localhost:9998/helloworld".&lt;br /&gt;&lt;br /&gt;@GET&lt;br /&gt;&lt;br /&gt;The @GET annotation on a method tells JAX-RS that the Java method implements HTTP's GET verb (the verb your browser uses every time you visit a web page). So JAX-RS now knows that a client doing a GET of "http://localhost:9998/helloworld" should result in the 'getClichedMessage' method being invoked.&lt;br /&gt;&lt;br /&gt;@Produces&lt;br /&gt;&lt;br /&gt;The @Produces annotation tells JAX-RS that the MIME type of the resource returned by 'getClichedMessage' is "text/plan", which is the MIME type for a plain text document. We've aimed for a minimal service here, so we're going for the simplest type of resource we can. It's also possible to return other kinds of entities as the result of a request, and it's even possible to use separate conversion classes to convert different kinds of Java objects into particular kinds of response entities (like a JPEG image, for example). But this service just returns a text document.&lt;br /&gt;&lt;br /&gt;At this point, JAX-RS knows everything it needs to know to support a client doing a GET on the resource identified by the URI "http://localhost:9998/helloworld". When we compile and run our service, it doesn't output anything at first. But if we open a browser and go to the URL, we see some output from Grizzly/Jersey:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init&lt;br /&gt;INFO: Scanning for root resource and provider classes in the packages:&lt;br /&gt; net.gilstraps.server&lt;br /&gt;Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init&lt;br /&gt;INFO: Root resource classes found:&lt;br /&gt; class net.gilstraps.server.HelloWorldResource&lt;br /&gt;Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init&lt;br /&gt;INFO: Provider classes found:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;From this we can see that Grizzly brings Jersey in to handle the request we made with the browser. Jersey then scans for classes which serve up REST resources. It tells us that it found our class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init&lt;br /&gt;INFO: Root resource classes found:&lt;br /&gt; class net.gilstraps.server.HelloWorldResource&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Even more interesting, the browser where we made the GET request gets the string that was returned from 'getClichedMessage' as the result of it's GET request:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_nyDSKzPKawU/SjMKVib_nOI/AAAAAAAAAEE/aCgx4OPwd5U/s1600-h/Stage1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 388px;" src="http://3.bp.blogspot.com/_nyDSKzPKawU/SjMKVib_nOI/AAAAAAAAAEE/aCgx4OPwd5U/s400/Stage1.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5346628547756661986" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And that's a simple 'hello world' implemented using JAX-RS with Jersey and Grizzly.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[Next: &lt;span class="Apple-style-span" style="font-size: 18px; color: rgb(204, 102, 0); line-height: 25px; "&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/07/multiple-representations-of-rest.html"&gt;Multiple representations of a REST Resource using JAX-RS&lt;/a&gt;&lt;a href="http://viewfromthefringe.blogspot.com/2009/07/multiple-representations-of-rest.html" style="color: rgb(51, 51, 51); text-decoration: none; display: inline !important; font-weight: normal; "&gt;]&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-910793106040087686?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/910793106040087686/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=910793106040087686' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/910793106040087686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/910793106040087686'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/06/jax-rs-hello-world.html' title='JAX-RS Hello World'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_nyDSKzPKawU/SjMKVib_nOI/AAAAAAAAAEE/aCgx4OPwd5U/s72-c/Stage1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-5054163628420560026</id><published>2009-06-02T21:06:00.003-05:00</published><updated>2009-07-21T23:48:52.713-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='grizzly servlet'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='Maven'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><category scheme='http://www.blogger.com/atom/ns#' term='JSR-311'/><title type='text'>Setting up Jersey for use</title><content type='html'>If you use Maven you can use it to download Jersey and keep it up to date. There are links to the POM files on the getting started page. In particular, you need the jersey-server module, and the grizzly-servlet-webserver module. If you want to use WADL with JDK 1.5 you need the jaxb-impl module. If you are not using Maven, you need only a few JARs:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/2/com/sun/grizzly/grizzly-servlet-webserver/1.8.3/grizzly-servlet-webserver-1.8.3.jar"&gt;grizzly-servlet-webserver.jar&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/2/com/sun/jersey/jersey-server/0.9-ea/jersey-server-0.9-ea.jar"&gt;jersey-server.jar&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/2/com/sun/jersey/jersey-server/0.9-ea/jersey-server-0.9-ea.jar"&gt;jersey-core.jar&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/2/javax/ws/rs/jsr311-api/0.9/jsr311-api-0.9.jar"&gt;jsr311-api.jar&lt;/a&gt;&lt;br /&gt;&lt;a href="http://repo1.maven.org/maven2/asm/asm/3.1/asm-3.1.jar"&gt;asm.jar&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;[NOTE: these projects are under active development, so you may find newer versions of these jars available]&lt;br /&gt;&lt;br /&gt;As with using Jersey via Maven, if you want to use WADL with JDK 1.5, you need some additional jars:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/1/com.sun.xml.bind/jars/jaxb-impl-2.1.jar"&gt;jaxb-impl.jar&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/1/javax.xml.bind/jars/jaxb-api-2.1.jar"&gt;jaxb-api.jar&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/1/javax.activation/jars/activation-1.1.jar"&gt;activation.jar&lt;/a&gt;&lt;br /&gt;&lt;a href="http://download.java.net/maven/1/javax.xml.stream/jars/stax-api-1.0-2.jar"&gt;stax-api.j&lt;/a&gt;ar&lt;br /&gt;&lt;br /&gt;We will ignore WADL for now, which limits the required jars to just the first five. But in addition to the binaries, I prefer to have the sources and javadoc available, which makes the total number of jars 15 (a binary, source, and javadoc jar for each).&lt;br /&gt;&lt;br /&gt;Once you have the JARs, configure your classpath or your IDE to include them in your project. You're then ready to write the obligatory 'hello world' program.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[Next: &lt;a href="http://viewfromthefringe.blogspot.com/2009/06/jax-rs-hello-world.html"&gt;JAX-RS Hello World&lt;/a&gt;]&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-5054163628420560026?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/5054163628420560026/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=5054163628420560026' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/5054163628420560026'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/5054163628420560026'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/06/setting-up-jersey-for-use.html' title='Setting up Jersey for use'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-6823986872373877430</id><published>2009-05-27T13:29:00.006-05:00</published><updated>2009-07-21T23:47:41.747-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RESTful'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='container'/><category scheme='http://www.blogger.com/atom/ns#' term='web service'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><title type='text'>Exploring RESTful web services with JAX-RS and Jersey.</title><content type='html'>I've been reading about RESTful web services for a while now. The best book on the subject I've found is &lt;a href="http://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260"&gt;RESTful Web Services&lt;/a&gt; by Leonard Richardson and Sam Ruby. I've been reading this book for what seems like forever (a function of my life and available time, not the book). I'm getting close to done and have begun using &lt;a href="https://jersey.dev.java.net/"&gt;Jersey&lt;/a&gt;, the reference implementation of the &lt;a href="http://jcp.org/aboutJava/communityprocess/final/jsr311/index.html"&gt;JAX-RS&lt;/a&gt; specification to experiment with building RESTful web services.&lt;br /&gt;&lt;br /&gt;I really like Jersey. A big reason for that is because Jersey makes it straightforward to stand up a standalone RESTful service. And once you've done it once, standing up additional services is easy. Deploying services without a heavyweight container makes me happy. Containers were created in the early days of Java when Java's limitations made them necessary. Since then, they have taken on a life of their own. Yet, in today's world I think the container has outlived almost all of its purpose. It's disadvantages now outweigh the few remaining benefits.&lt;br /&gt;&lt;br /&gt;In the coming months, I plan to explore how to build, deploy, replicate, secure, and evolve industrial-class services without a container. I think I will succeed, but who knows? Maybe I'll learn a bunch due to abject failures. Either way, it should be fun.&lt;br /&gt;&lt;br /&gt;My next post will describe how you download all the various pieces needed to stand up a Jersey-based REST service. There aren't very many, and it doesn't take long. Stay tuned.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[Next: &lt;a href="http://viewfromthefringe.blogspot.com/2009/06/setting-up-jersey-for-use.html"&gt;Setting up Jersey for Use&lt;/a&gt;]&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-6823986872373877430?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/6823986872373877430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=6823986872373877430' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6823986872373877430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6823986872373877430'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2009/05/exploring-restful-web-services-with-jax.html' title='Exploring RESTful web services with JAX-RS and Jersey.'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-4508194242541604721</id><published>2008-11-12T08:39:00.003-06:00</published><updated>2008-11-12T08:59:14.447-06:00</updated><title type='text'>Everybody Needs Cron</title><content type='html'>I was looking at one of my daily emails from javablogs.com when I saw one entitled "Nobody should need cron". This struck me as rather naive, so I went over to read &lt;a href="http://oddthesis.org/"&gt;Odd Thesis's&lt;/a&gt; entry: &lt;a href="http://oddthesis.org/posts/2008-11-nobody-should-need-cron"&gt;Nobody Should Need Cron&lt;/a&gt;, hoping to learn what I've been doing wrong all these years.&lt;br /&gt;&lt;br /&gt;I posted a comment on the blog which hasn't shown up yet. I'll recreate it here.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Trying to recreate cron in Java/Ruby is asking for trouble. Any real-world application will have activities which are hard or impossible to implement in Java or Ruby. They will be OS-specific, or low-level, or be needed to monitor the very container you are trying to run them from. They often need to be extremely robust (more robust than Java apps can be) in the face of very unusual conditions on the box (huge load, almost zero memory available, etc). Cron and OS-specific tools are well-suited to these situations. Java and Ruby are not.&lt;br /&gt;&lt;br /&gt;I'm a software engineer who has been working with Java since 1.0 beta, and I find it a great development language (I'm less experienced with Ruby but it is nice also). But they are simply inappropriate replacements for cron.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;I understand the desire to stay in the productive, known world of Java or Ruby. And for periodic tasks "within" the application, some sort of cron-like facility is a nice feature. But robust software systems that are resilient and handle 'unexpected' (i.e. difficult to handle) situations without simply falling over require developers to step outside the virtual machine and deal with the OS and hardware (and databases and filesystems and other software systems) that the virtual machine depends upon. For those watchdog or maintenance activities that have to occur outside the virtual machine, cron (or it's OS-specific equivalent) are indespensible.&lt;br /&gt;&lt;br /&gt;If you really think you can build a non-trivial system without cron, you've failed to identify or are ignoring requirements which you must satisfy to make your system robust.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-4508194242541604721?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/4508194242541604721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=4508194242541604721' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4508194242541604721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4508194242541604721'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2008/11/everybody-needs-cron.html' title='Everybody Needs Cron'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-5325655131371520904</id><published>2008-09-11T19:00:00.006-05:00</published><updated>2008-09-11T22:24:51.738-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='interface chaining'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='user interface design'/><category scheme='http://www.blogger.com/atom/ns#' term='design pattern'/><category scheme='http://www.blogger.com/atom/ns#' term='DSL'/><category scheme='http://www.blogger.com/atom/ns#' term='WAX'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Interface Chaining</title><content type='html'>I was recently talking with my friend Mark Volkmann about an application framework he was developing. The framework, named &lt;a href="http://www.ociweb.com/wax"&gt;WAX&lt;/a&gt;, is a very nice Java API for writing out XML. Despite it's small size, it's much nicer than any other XML writing API I've ever seen and you should go &lt;a href="http://www.ociweb.com/wax"&gt;check it out&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As part of the API, Mark implementing method chaining. This allows for very compact code that is still quite easy to read. For example, you could produce this XML:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;car year="2008"&amp;gt;&lt;br /&gt;  &amp;lt;model&amp;gt;Prius&amp;lt;/model&amp;gt;&lt;br /&gt;&amp;lt;/car&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;with just this code::&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;WAX wax = new WAX(WAX.Version.V1_0);&lt;br /&gt;    wax.start("car").attr("year", 2008)&lt;br /&gt;        .start("model" ).text( "Prius").close();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Much of the compactness comes from the fact that each method called returns the WAX object, allowing the developer to chain method calls together.&lt;br /&gt;&lt;br /&gt;As we were discussing the code, the topic of incorrect ordering of calls came up. For example, what happens if you try to call the &lt;i&gt;attr&lt;/i&gt; method to create an attribute after already creating some text inside the element with the &lt;i&gt;text&lt;/i&gt; method?&lt;br /&gt;&lt;br /&gt;Because WAX is a streaming API, you can't 'go back' and add an attribute in the start element tag after having already 'moved on' to putting text in the body of the element. For example, what if I tried to create an attribute to the model of the car after putting text in it, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;WAX wax = new WAX(WAX.Version.V1_0);&lt;br /&gt;    wax.start("car").attr("year", 2008)&lt;br /&gt;        .start("model" ).text( "Prius").&lt;b&gt;&lt;u&gt;attr( "trim", "GT" )&lt;/u&gt;&lt;/b&gt;.close();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;WAX detects this situation and throws an exception at runtime, which it should do. But I'm a fan of static type checking. I love having tools do as much checking for me as possible. So I suggested to Mark that he create a set of interfaces. Each interface would contain only the methods that make sense at the current point in the output stream. This would allow the compiler to catch invalid method invocations. Even better, modern IDEs like IDEA would flag the method invocation visually even before compilation.&lt;br /&gt;&lt;br /&gt;For example, if we arrange for the &lt;i&gt;text&lt;/i&gt; method to return an interface which does not have an &lt;i&gt;attr&lt;/i&gt; method, then the compiler will catch it and the IDE will flag the error visually. For example, in IDEA, the error is flagged like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_nyDSKzPKawU/SMm4uP58F6I/AAAAAAAAADk/qB6j_dJxxxw/s1600-h/BadInvocation.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_nyDSKzPKawU/SMm4uP58F6I/AAAAAAAAADk/qB6j_dJxxxw/s400/BadInvocation.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5244926345732691874" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now we get the compactness of runtime exceptions (which Mark really likes) and static checking (which I really like). I did some looking around and couldn't find a design pattern that really fits. A number of people suggested that this was an example of a domain-specific language (DSL). But to my thinking, something that deserves the moniker of 'language' should be more involved than simply constructing some interfaces so that static checking is applied. &lt;br /&gt;&lt;br /&gt;But whatever you call it, I think it's a useful design pattern than can be applied in other situations to make for clean, compact, yet statically checked APIs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-5325655131371520904?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/5325655131371520904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=5325655131371520904' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/5325655131371520904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/5325655131371520904'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2008/09/interface-chaining.html' title='Interface Chaining'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_nyDSKzPKawU/SMm4uP58F6I/AAAAAAAAADk/qB6j_dJxxxw/s72-c/BadInvocation.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-2156996263950555036</id><published>2008-07-17T12:31:00.014-05:00</published><updated>2008-07-17T22:31:20.219-05:00</updated><title type='text'>Create inbox items in OmniFocus from the dashboard</title><content type='html'>I am a very heavy user of OmniFocus. It has many ways to enter information. For example, you can use the "Quick Entry Window", which is great for creating an item with a project, context, and optional description.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_nyDSKzPKawU/SH-D-SxzrOI/AAAAAAAAABw/cQ0tfnexqtc/s1600-h/QuickEntry.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_nyDSKzPKawU/SH-D-SxzrOI/AAAAAAAAABw/cQ0tfnexqtc/s400/QuickEntry.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224039198988938466" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;But sometimes I am not in OmniFocus and don't have any text selected. This means there is nothing for OmniFocus to treat as a clipping. As a result it won't bring up the quick entry window.&lt;br /&gt;&lt;br /&gt;So, I wrote my first Dashboard widget. It has a text area where you type the text for your item and a "Send It" button. When you click the button or hit enter in the text area, a new item is sent to your inbox.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_nyDSKzPKawU/SH-QVNFGLpI/AAAAAAAAAB4/y5ks8_4s4bY/s1600-h/WidgetSnapshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_nyDSKzPKawU/SH-QVNFGLpI/AAAAAAAAAB4/y5ks8_4s4bY/s400/WidgetSnapshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5224052786735754898" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I submittted it to Apple for inclusion in their download area for dashboard widgets &lt;a href="http://www.apple.com/downloads/dashboard/"&gt;download site&lt;/a&gt;. Until it shows up there, you can get it directly from &lt;a href="http://www.mediafire.com/?smzylhd2mys"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;[Updated 2008-07-17 21:05]&lt;br /&gt;&lt;br /&gt;Someone pointed out that I had forgotten that there is a separate keystroke to bring up the quick entry window without having any text selected. This eliminates much of the purpose of this widget unfortunately, unless you spend a lot of time in the dashboard anyway. At least it was a good learning exercise... :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-2156996263950555036?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/2156996263950555036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=2156996263950555036' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2156996263950555036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2156996263950555036'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2008/07/create-inbox-items-to-omnifocus-from.html' title='Create inbox items in OmniFocus from the dashboard'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_nyDSKzPKawU/SH-D-SxzrOI/AAAAAAAAABw/cQ0tfnexqtc/s72-c/QuickEntry.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-2962162128673767732</id><published>2008-05-05T14:20:00.004-05:00</published><updated>2008-05-05T14:54:38.890-05:00</updated><title type='text'>Groovy Type Handling is Broken</title><content type='html'>Here's a very simple Java program snippet:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;List&lt;&lt;T&gt;Integer&gt;&lt;/T&gt; list = new ArrayList&lt;&lt;T&gt;Integer&gt;&lt;/T&gt;();&lt;br /&gt;Collection&lt;&lt;T&gt;Integer&gt;&lt;/T&gt; col = list;&lt;br /&gt;list.add( 2 );&lt;br /&gt;list.add( 1 );&lt;br /&gt;list.add( 0 );&lt;br /&gt;System.out.println( list );&lt;br /&gt;col.remove( 0 );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Run this and you'll get what you'd expect. Before using the &lt;i&gt;col&lt;/i&gt; reference to remove the integer/Integer with value 0 (zero), you see:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[2, 1, 0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, after calling remove on the collection, we see the integer/Integer with value zero is gone (that's the one we said to remove):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[2, 1]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Groovy says that it allows you to use types and will honor them at runtime. But it doesn't.&lt;br /&gt;&lt;br /&gt;Take the same snippet of code and run it as a Groovy script. Remember that the Collection interface has a &lt;i&gt;remove&lt;/i&gt; method, and that method expects you to pass it an object which it will remove from the collection. So we would expect to see the same result as in Java. But we don't. Instead, we get this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[2, 1, 0]&lt;br /&gt;[1, 0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Groovy has removed the Integer with a value of 2! It did this because of Groovy's 'multi-methods'. Groovy looked at the real type of &lt;i&gt;col&lt;/i&gt;, which is an &lt;i&gt;ArrayList&lt;/i&gt;. It then found a &lt;i&gt;remove&lt;/i&gt; method that takes an integer/Integer. That method removes the Nth item from the list. It used that method and removed the zero-th item (the one with a value of 2).&lt;br /&gt;&lt;br /&gt;But remember, I invoked the &lt;i&gt;remove&lt;/i&gt;. method through the &lt;i&gt;Collection&lt;/i&gt; interface. &lt;b&gt;The collection interface does not have a method taking an integer/Integer but it does have a remove method taking the object to be removed&lt;/b&gt;!&lt;br /&gt;&lt;br /&gt;This is a truly awful result. It removes our ability to reason about how code will behave by looking at the types involved. In order to know how this will behave, I cannot make any use of the type information; in fact the type information is downright misleading. Even worse, as the type system evolves and new classes are introduced, new methods with entirely different signatures may get invoked.&lt;br /&gt;&lt;br /&gt;This approach to handling types is essentially broken. The claim that Groovy honors types (even the weakened form of honoring it by doing casts at runtime) is flat out wrong.&lt;br /&gt;&lt;br /&gt;Groovy needs to either start honoring type information, or get rid of it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-2962162128673767732?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/2962162128673767732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=2962162128673767732' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2962162128673767732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2962162128673767732'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2008/05/groovy-type-handling-is-broken.html' title='Groovy Type Handling is Broken'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-8697598060912512558</id><published>2008-04-24T16:08:00.004-05:00</published><updated>2008-04-24T16:28:17.560-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='code coupling'/><category scheme='http://www.blogger.com/atom/ns#' term='assertions'/><title type='text'>Insidious Coupling</title><content type='html'>This will get to the topic of code coupling soon enough, but bear with me for a bit of background.&lt;br /&gt;&lt;br /&gt;I like to use the 'fail fast' approach to my code. This means that invalid inputs cause a failure in the code as quickly as possible. Perhaps the simplest example of this in the JDK is the collections classes, whose iterators fail as soon as they detect that the underlying collection object has been modified. This allows them to avoid synchronization costs while also detecting unsafe usage.&lt;br /&gt;&lt;br /&gt;In my own code, the most common situation where I find fast-fail code is in constructors. If an argument the constructor cannot be null (either because it will be used in the constructor, saved to a field, or both), I check that the object reference is not null. For example:&lt;br /&gt;&lt;pre style="jcode"&gt;&lt;br /&gt;public class Foo {&lt;br /&gt; // ...&lt;br /&gt; public Foo( Bar b ) {&lt;br /&gt;  // Check that b is not null&lt;br /&gt;  // rest of constructor...&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I started out performing this check with an &lt;i&gt;if&lt;/i&gt; statement:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Foo {&lt;br /&gt; // ...&lt;br /&gt; public Foo( final Bar b ) {&lt;br /&gt;  if ( b == null ) {&lt;br /&gt;   throw new IllegalArgumentException( "Bar b cannot be null" );&lt;br /&gt;  }&lt;br /&gt;  // rest of constructor...&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This gets us a pretty understandable stack trace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Exception in thread "main" java.lang.IllegalArgumentException: Bar b cannot be null&lt;br /&gt; at org.sandbox.Foo.&lt;init&gt;(Foo.java:10)&lt;br /&gt; at org.sandbox.SimpleDoesStuff.doIt(SimpleDoesStuff.java:11)&lt;br /&gt; at org.sandbox.Baz.main(Baz.java:10)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But this Java code is pretty verbose, especially if you have multiple arguments to check. After that I tried creating a static method named &lt;i&gt;assureNotNull&lt;/i&gt; which would perform the check:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Foo {&lt;br /&gt; // ...&lt;br /&gt; public Foo( final Bar b ) {&lt;br /&gt;  assureNotNull( b );&lt;br /&gt;  // rest of constructor...&lt;br /&gt; }&lt;br /&gt; //...&lt;br /&gt; private static void assureNotNull( final Object o ) throws IllegalArgumentException {&lt;br /&gt;  if ( o == null ) {&lt;br /&gt;   throw new IllegalArgumentException( "Object cannot be null" );&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This has the unfortunate consequence that it becomes harder to track down the nature of the problem.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Exception in thread "main" java.lang.IllegalArgumentException: Object cannot be null&lt;br /&gt; at org.sandbox.Foo.assureNotNull(Foo.java:14)&lt;br /&gt; at org.sandbox.Foo.&lt;init&gt;(Foo.java:9)&lt;br /&gt; at org.sandbox.SimpleDoesStuff.doIt(SimpleDoesStuff.java:11)&lt;br /&gt; at org.sandbox.Baz.main(Baz.java:10)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The stack trace on the exception shows &lt;i&gt;assureNotNull&lt;/i&gt; as the source of the exception, which while strictly true doesn't clearly show that the real problem was with someone passing a null Bar to the Foo constructor. This can be mitigated to some degree by taking an description as argument:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Foo {&lt;br /&gt; // ...&lt;br /&gt; public Foo( final Bar b ) {&lt;br /&gt;  assureNotNull( b, "Bar b cannot be null" );&lt;br /&gt;  // rest of constructor...&lt;br /&gt; }&lt;br /&gt; //...&lt;br /&gt; private static void assureNotNull( final Object o, final String msg ) throws IllegalArgumentException {&lt;br /&gt;  if ( o == null ) {&lt;br /&gt;   throw new IllegalArgumentException( msg );&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This provides a somewhat more helpful exception message but the method generating the exception is still 'off by one':&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Exception in thread "main" java.lang.IllegalArgumentException: Bar b cannot be null&lt;br /&gt; at org.sandbox.Foo.assureNotNull(Foo.java:14)&lt;br /&gt; at org.sandbox.Foo.&lt;init&gt;(Foo.java:9)&lt;br /&gt; at org.sandbox.SimpleDoesStuff.doIt(SimpleDoesStuff.java:11)&lt;br /&gt; at org.sandbox.Baz.main(Baz.java:10)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And even with this semi-fix to help find the cause of the problem, we're still left with recreating this method in every class. From here the logical next step is to centralize the method. We can do this with an Assertions class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Assertions {&lt;br /&gt; // ...&lt;br /&gt; public  static void assureNotNull( final Object o, final String msg ) throws IllegalArgumentException {&lt;br /&gt;  if ( o == null ) {&lt;br /&gt;   throw new IllegalArgumentException( msg );&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; // ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, our Foo constructor becomes simpler:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import org.acme.Assertions;&lt;br /&gt;&lt;br /&gt;public class Foo {&lt;br /&gt; // ...&lt;br /&gt; public Foo( final Bar b ) {&lt;br /&gt;  Assertions.assureNotNull( b, "Bar b cannot be null" );&lt;br /&gt;  // rest of constructor...&lt;br /&gt; }&lt;br /&gt; //...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And with Java 5's static imports, we can even simplify it further:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import static org.acme.Assertions.assureNotNull;&lt;br /&gt;&lt;br /&gt;public class Foo {&lt;br /&gt; // ...&lt;br /&gt; public Foo( final Bar b ) {&lt;br /&gt;  assureNotNull( b, "Bar b cannot be null" );&lt;br /&gt;  // rest of constructor...&lt;br /&gt; }&lt;br /&gt; //...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But we still have the wrong method as the first listed in the stack trace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Exception in thread "main" java.lang.IllegalArgumentException: Bar b cannot be null&lt;br /&gt; at org.acme.Assertions.assureNotNull(Assertions.java:9)&lt;br /&gt; at org.sandbox.Foo.&lt;init&gt;(Foo.java:7)&lt;br /&gt; at org.sandbox.SimpleDoesStuff.doIt(SimpleDoesStuff.java:11)&lt;br /&gt; at org.sandbox.Baz.main(Baz.java:10)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since all of us Java developers are big on frameworks, we might even take it to the 'next level'. Why not centralize the Assertions in some common framework? If the Assertions class were in a common framework, the thinking goes, then we'd all be able to use it everywhere. That consistency would really help us read unfamiliar code.&lt;br /&gt;&lt;br /&gt;Ignoring the issue of getting any set of developers of size greater than one to agree on anything, especially a basic assertions framework used everywhere, we've  just introduced insidious code coupling where it really doesn't belong. In our desire to achieve clarity, brevity, and avoid redundancy, we've coupled code together. If we have a common class in our framework, all the classes in the framework are now coupled to it and indirectly to each other. If we move the class to a generic framework, we externalize the coupling, making it even broader. This coupling introduces many hazards for versioning and incompatibility problems that come with the independent evolution of code.&lt;br /&gt;&lt;br /&gt;So, what choice are we left with? Well, we could always depend upon the runtime itself to generate an exception for us. We can do this quite briefly, with less redundancy of code than the other approaches, and with zero coupling:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Foo {&lt;br /&gt; // ...&lt;br /&gt; public Foo(Bar b) {&lt;br /&gt;  b.getClass(); // Assure not null&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This code is quite short, and generates a NullPointerException with the top element pointing to exactly the line of code where the problem occurred:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Exception in thread "main" java.lang.NullPointerException&lt;br /&gt; at org.sandbox.Foo.&lt;init&gt;(Foo.java:7)&lt;br /&gt; at org.sandbox.SimpleDoesStuff.doIt(SimpleDoesStuff.java:11)&lt;br /&gt; at org.sandbox.Baz.main(Baz.java:10)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This code also avoids nearly all redundancy,  and I think after seeing it once it becomes quite clear. As a bonus, it's easy to create a macro in any modern IDE to help in generating the code.&lt;br /&gt;&lt;br /&gt;Sometimes, the right solution to a problem is not abstracting code into a separate method/class/framework.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8697598060912512558?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8697598060912512558/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8697598060912512558' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8697598060912512558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8697598060912512558'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2008/04/insidious-coupling.html' title='Insidious Coupling'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-7661341337769159748</id><published>2008-03-07T18:53:00.005-06:00</published><updated>2008-03-07T21:58:03.875-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='user interface design'/><category scheme='http://www.blogger.com/atom/ns#' term='human factors'/><category scheme='http://www.blogger.com/atom/ns#' term='software design'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Something else Apple Gets Right</title><content type='html'>Eric Burke made &lt;a href="http://stuffthathappens.com/blog/2008/03/05/simplicity/"&gt;a reference&lt;/a&gt; to the work Apple puts into making their applications simple but capable. He also gave Google as an example. That item produced quite a few comments and Eric followed up with &lt;a href="http://stuffthathappens.com/blog/2008/03/06/i-disagree/"&gt;more details&lt;/a&gt;. Again, the discussion wasn't just about Apple but the company was again an example of hard work making things simple and functional. I agree with his comments, and remembered another example of Apple's efforts toward elegant designs.&lt;br /&gt;&lt;br /&gt;For as many years as I can remember, the Apple website has had simple URLs to get to various parts of the website. For example:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.apple.com/developer"&gt;http://www.apple.com/developer&lt;/a&gt; - the developer site&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.apple.com/mac"&gt;http://www.apple.com/mac&lt;/a&gt; - information about Macs&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.apple.com/iphone"&gt;http://www.apple.com/iphone&lt;/a&gt; - information about the iPhone&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.apple.com/ipod"&gt;http://www.apple.com/ipod&lt;/a&gt; - information about the iPod&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.apple.com/store"&gt;http://www.apple.com/store&lt;/a&gt; - the online Apple store &lt;b&gt;and&lt;/b&gt; information about the retail stores&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Yes, other sites do this now. But Apple was doing this many years ago, long before competitor's sites.&lt;br /&gt;&lt;br /&gt;This seemingly simple idea makes it much easier to navigate their web site for the vast majority of people going to the site. It allows people to get more quickly to the area they want. Without this, the task becomes much more involved:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Visit the home page&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Parse the layout&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Find the navigation links&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Find the right link - if it's not there guess where to drill down&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Get to where you wanted to go&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-7661341337769159748?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/7661341337769159748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=7661341337769159748' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/7661341337769159748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/7661341337769159748'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2008/03/something-else-apple-gets-right.html' title='Something else Apple Gets Right'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1150675830427338338</id><published>2008-02-13T10:42:00.004-06:00</published><updated>2008-02-13T13:03:47.274-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='closures'/><category scheme='http://www.blogger.com/atom/ns#' term='BGGA'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='syntax'/><title type='text'>An alternate syntax for Java closures</title><content type='html'>I'm no expert on the details of the closures proposals. I do think if we're going to put closures into the language, we need to make them complete, full-fledged closures. Generics are a mess precisely because the semantics and features were compromised for the goal of getting it into the language.&lt;br /&gt;&lt;br /&gt;But one thing I do know about the BGGA closures proposal is that I don't like the syntax. Take the example in Weiqi Gao's &lt;a href="http://www.weiqigao.com/blog/2008/02/01/friday_java_quiz_know_your_closures.html"&gt;recent Java Quiz&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Fib {&lt;br /&gt;  private static {int=&gt;int} fib = { int n =&gt;&lt;br /&gt;      n==0 ? 0 :&lt;br /&gt;      n==1 ? 1 :&lt;br /&gt;      fib.invoke(n-1) + fib.invoke(n-2)&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    System.out.println(fib.invoke(6));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To me, this is just hard to read. The whole "=&gt;" thing seems like a weak attempt to create a new lexical character and is too visually similar to ==, &gt;&gt;, etc. I also don't like the 'invoke' syntax. I was discussing this with Weiqi the other day and came up with a syntax that I like better.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Fib {&lt;br /&gt;  private static {int # int} fib = { # int n #&lt;br /&gt;      n==0 ? 0 :&lt;br /&gt;      n==1 ? 1 :&lt;br /&gt;      #fib(n-1) + #fib(n-2)&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {&lt;br /&gt;    System.out.println(#fib(6));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that you could also invoke &lt;i&gt;fib&lt;/i&gt; like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;System.out.println(#Fib.fib(6));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I wonder what other people think of this alternative?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1150675830427338338?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1150675830427338338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1150675830427338338' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1150675830427338338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1150675830427338338'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2008/02/alternate-syntax-for-java-closures.html' title='An alternate syntax for Java closures'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-428543020753886390</id><published>2007-11-12T22:08:00.000-06:00</published><updated>2007-11-12T22:19:40.466-06:00</updated><title type='text'>The Wall of Erasure</title><content type='html'>I mentioned the term "Wall of Erasure" in my previous post about &lt;a href="http://viewfromthefringe.blogspot.com/2007/11/java-5-iterator-adapters.html"&gt;Iterator adapters&lt;/a&gt;.  My friend &lt;a href="http://www.weiqigao.com/blog"&gt;Weiqi Gao&lt;/a&gt; described it best in &lt;a href="http://www.weiqigao.com/blog/2007/11/04/the_wall_of_erasure.html"&gt;his blog entry&lt;/a&gt; where he said 'Every &lt;i&gt;really cool&lt;/i&gt; idea involving Java generics will invariably lead to insurmountable difficulties the root cause of which is erasure'.&lt;br /&gt;&lt;br /&gt;I just got a chance to read Eric Burke's latest blog entries, and he too has &lt;a href="http://stuffthathappens.com/blog/2007/11/04/foiled-by-erasure/"&gt;hit the Wall of Erasure&lt;/a&gt; (some commentors are suggesting it's not "all" erasure's fault, but that's a red herring).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-428543020753886390?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/428543020753886390/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=428543020753886390' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/428543020753886390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/428543020753886390'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/11/wall-of-erasure.html' title='The Wall of Erasure'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-4264378132746697688</id><published>2007-11-05T08:56:00.001-06:00</published><updated>2007-11-05T09:03:46.515-06:00</updated><title type='text'>Unit Testing Simple Code</title><content type='html'>I just read &lt;a href="http://tech.puredanger.com/"&gt;Alex Miller's&lt;/a&gt; blog entry where he states quite nicely &lt;a href="http://tech.puredanger.com/2007/11/02/unit-testing-is-dumb/"&gt;why unit testing even very simple code is &lt;b&gt;not&lt;/b&gt; dumb&lt;/a&gt;. He posted because of &lt;a href="http://stuffthathappens.com/blog/"&gt;Eric Burke's&lt;/a&gt; excellent &lt;a href="http://stuffthathappens.com/blog/2007/11/01/easy-2-test-less-reason-to-test/"&gt;discussion of the matter.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I really don't have much more to add, except to add one more voice to those who state that unit testing seemingly trivial code is important. I will say that I've actually caught silly mistakes (like failing to set a field to the correct value in a constructor) with simple unit tests. Also, writing tests for such code lets me spend more time thinking about the hard parts of the problem and less time examining 'simple' code to make sure it is correct.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-4264378132746697688?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/4264378132746697688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=4264378132746697688' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4264378132746697688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4264378132746697688'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/11/unit-testing-simple-code.html' title='Unit Testing Simple Code'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1371969141692658484</id><published>2007-11-04T21:28:00.001-06:00</published><updated>2007-11-04T21:47:43.352-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='generics'/><category scheme='http://www.blogger.com/atom/ns#' term='Wall of Erasure'/><category scheme='http://www.blogger.com/atom/ns#' term='iterators'/><category scheme='http://www.blogger.com/atom/ns#' term='adapters'/><title type='text'>Java 5+ Iterator Adapters</title><content type='html'>I've been working with Java Generics more lately, and have run smack into the "Wall of Erasure". I'm referring to the fact that generics disappear in the bytecode. As you probably know, generics were a targeted feature at a time when Sun was attempting to avoid changing the Java Virtual Machine (JVM). They really wanted to maintain backward compatibility. So, this compromise was put forth to maintain backward compatibility. In a classic case of irony, other changes forced a change the JVM in the same release that gave us generices, but the compromised version of generics stayed. So we are stuck with half an implementation of generics.&lt;br /&gt;&lt;br /&gt;Recently, I've been doing work with interfaces and extending interfaces, as in:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface Superclass {&lt;br /&gt;    // ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public interface Subclass extends Superclass {&lt;br /&gt;    // ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I have classes which implement a third interface:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface DoesStuff {&lt;br /&gt;    Iterator&lt;&lt;T&gt;Superclass&lt;/T&gt;&gt; iterator();&lt;br /&gt;    // ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You would think that an implementation of DoesStuff could return an &lt;i&gt;Iterator&lt;Subclass&gt;&lt;/i&gt; for the &lt;i&gt;iterator&lt;/i&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class MyClass implements DoesStuff {&lt;br /&gt;    // ...&lt;br /&gt;    private Set&lt;&lt;T&gt;Subclass&lt;/T&gt;&gt; myThings = new HashSet&lt;&lt;T&gt;Subclass&lt;/T&gt;&gt;();&lt;br /&gt;    // ...&lt;br /&gt;    public Iterator&lt;&lt;T&gt;Superclass&lt;/T&gt;&gt; iterator() {&lt;br /&gt;        return myThings.iterator();&lt;br /&gt;    }&lt;br /&gt;    // ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt; Unfortunately, the Wall of Erasure means we can't do this. Try it for yourself if you don't believe me.&lt;br /&gt;&lt;p&gt;&lt;br /&gt;So, what to do? Happily, there is a simple answer. We create an adapter that adapts the subclass iterator to the superclass iterator:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/**&lt;br /&gt; * Adapts a derived class iterator to a base class.&lt;br /&gt; */&lt;br /&gt;public class IteratorAdapter&lt;&lt;T&gt;B, D extends B&lt;/T&gt;&gt; implements Iterator&lt;&lt;T&gt;B&lt;/T&gt;&gt; {&lt;br /&gt;    private Iterator&lt;&lt;T&gt;D&gt; d;&lt;br /&gt;&lt;br /&gt;    public IteratorAdapter(Iterator&lt;&lt;T&gt;D&lt;/T&gt;&gt; aD) {&lt;br /&gt;        d = aD;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean hasNext() {&lt;br /&gt;        return d.hasNext();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public B next() {&lt;br /&gt;        return d.next();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void remove() {&lt;br /&gt;        d.remove();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The sad thing is that this shouldn't be necessary. It's trivial. It shouldn't have to be written by developers. The simplicity of this is a clear sign that we need to tear down the Wall of Erasure.&lt;br /&gt;&lt;br /&gt;Will it happen any time soon? I hope so. Though I'm hearing talk that "erasing erasure" is probably off the table for Java 7. That would be a truly sad thing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1371969141692658484?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1371969141692658484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1371969141692658484' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1371969141692658484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1371969141692658484'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/11/java-5-iterator-adapters.html' title='Java 5+ Iterator Adapters'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-4593629829317895774</id><published>2007-10-26T09:44:00.000-05:00</published><updated>2007-10-26T13:29:45.629-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Iterable'/><category scheme='http://www.blogger.com/atom/ns#' term='iterator'/><category scheme='http://www.blogger.com/atom/ns#' term='BufferedReader'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Making BufferedReader Iterable</title><content type='html'>I was reading recently about a issues around reading an entire file into a string. I pretty much never do that, since I like my code to be robust even in the face of very large files. But it did get me thinking about reading in lines of a file one at a time. I do have reason to do that on occasion.&lt;br /&gt;&lt;br /&gt;Originally, I might have done it something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;File file = ...&lt;br /&gt;BufferedReader br = new BufferedReader(new FileReader(file));&lt;br /&gt;String line = br.readLine();&lt;br /&gt;while ( line != null ) {&lt;br /&gt;    // Do something with line&lt;br /&gt;    line = br.readLine();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's not bad, but I've been working with Ruby lately and really like the terse yet easy-to-read nature of the code. We can improve the above code with a &lt;i&gt;for&lt;/i&gt; loop:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;File file = ...&lt;br /&gt;BufferedReader br = new BufferedReader(new FileReader(file));&lt;br /&gt;for (String line = br.readLine(); line != null; line = br.readLine()) {&lt;br /&gt;    // Do something with line&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Better, but still not great. Now, what if the &lt;i&gt;BufferedReader&lt;/i&gt; were &lt;i&gt;Iterable&lt;/i&gt;? It isn't, but we can wrap an &lt;i&gt;Iterable&lt;/i&gt; around it. First, let's look at usage:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;File file = ...&lt;br /&gt;BufferedReaderIterable bri = new BufferedReaderIterable(new BufferedReader(new FileReader(file)));&lt;br /&gt;for (String read : bri) {&lt;br /&gt;    // Do something with the line&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Very nice. So, all we need is to implement &lt;i&gt;BufferedReaderIterable&lt;/i&gt;. Happily, it's quite simple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class BufferedReaderIterable implements Iterable&lt;&lt;T&gt;String&lt;/T&gt;&gt; {&lt;br /&gt;&lt;br /&gt;    private Iterator&lt;&lt;T&gt;String&lt;/T&gt;&gt; i;&lt;br /&gt;&lt;br /&gt;    public BufferedReaderIterable( BufferedReader br ) {&lt;br /&gt;        i = new BufferedReaderIterator( br );&lt;br /&gt;    }&lt;br /&gt;    public Iterator&lt;String&gt; iterator() {&lt;br /&gt;        return i;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private class BufferedReaderIterator implements Iterator&lt;&lt;T&gt;String&lt;/T&gt;&gt; {&lt;br /&gt;        private BufferedReader br;&lt;br /&gt;        private java.lang.String line;&lt;br /&gt;&lt;br /&gt;        public BufferedReaderIterator( BufferedReader aBR ) {&lt;br /&gt;            (br = aBR).getClass();&lt;br /&gt;            advance();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public boolean hasNext() {&lt;br /&gt;            return line != null;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public String next() {&lt;br /&gt;            String retval = line;&lt;br /&gt;            advance();&lt;br /&gt;            return retval;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void remove() {&lt;br /&gt;            throw new UnsupportedOperationException("Remove not supported on BufferedReader iteration.");&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void advance() {&lt;br /&gt;            try {&lt;br /&gt;                line = br.readLine();&lt;br /&gt;            }&lt;br /&gt;            catch (IOException e) { /* TODO */}&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It would be nice if we could make the construction cleaner. What if &lt;i&gt;BufferedReaderIterable&lt;/i&gt; had a constructor that took a &lt;i&gt;File&lt;/i&gt;?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;public class BufferedReaderIterable implements Iterable&lt;&lt;T&gt;String&lt;/T&gt;&gt; {&lt;br /&gt;&lt;br /&gt;    private BufferedReader mine;&lt;br /&gt;    private Iterator&lt;&lt;T&gt;String&lt;/T&gt;&gt; i;&lt;br /&gt;&lt;br /&gt;    public BufferedReaderIterable( BufferedReader br ) {&lt;br /&gt;        i = new BufferedReaderIterator( br );&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public BufferedReaderIterable( File f ) throws FileNotFoundException {&lt;br /&gt;        mine = new BufferedReader( new FileReader( f ) );&lt;br /&gt;        i = new BufferedReaderIterator( mine );&lt;br /&gt;    }&lt;br /&gt;    public Iterator&lt;&lt;T&gt;String&lt;/T&gt;&gt; iterator() {&lt;br /&gt;        return i;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private class BufferedReaderIterator implements Iterator&lt;&lt;T&gt;String&lt;/T&gt;&gt; {&lt;br /&gt;        private BufferedReader br;&lt;br /&gt;        private String line;&lt;br /&gt;&lt;br /&gt;        public BufferedReaderIterator( BufferedReader aBR ) {&lt;br /&gt;            (br = aBR).getClass();&lt;br /&gt;            advance();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public boolean hasNext() {&lt;br /&gt;            return line != null;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public String next() {&lt;br /&gt;            String retval = line;&lt;br /&gt;            advance();&lt;br /&gt;            return retval;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void remove() {&lt;br /&gt;            throw new UnsupportedOperationException("Remove not supported on BufferedReader iteration.");&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void advance() {&lt;br /&gt;            try {&lt;br /&gt;                line = br.readLine();&lt;br /&gt;            }&lt;br /&gt;            catch (IOException e) { /* TODO */}&lt;br /&gt;            if ( line == null &amp;&amp; mine != null ) {&lt;br /&gt;                try {&lt;br /&gt;                    mine.close();&lt;br /&gt;                }&lt;br /&gt;                catch (IOException e) { /* Ignore - probably should log an error */ }&lt;br /&gt;                mine = null;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, usage is even cleaner:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;File file = ...&lt;br /&gt;BufferedReaderIterable bri = new BufferedReaderIterable(file);&lt;br /&gt;for (String read : bri) {&lt;br /&gt;    // Do something with the line&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;[Editorial Note: Updated to put the template angle brackets back in after Eric Burke noticed Blogger was eating them]&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-4593629829317895774?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/4593629829317895774/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=4593629829317895774' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4593629829317895774'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4593629829317895774'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/10/making-bufferedreader-iterable.html' title='Making BufferedReader Iterable'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-400093659271659954</id><published>2007-09-18T12:05:00.000-05:00</published><updated>2007-09-18T12:18:22.562-05:00</updated><title type='text'>Eric Burke suppressing a desire to get a Mac?</title><content type='html'>My friend and colleague, Eric Burke, &lt;a href="http://stuffthathappens.com/blog/2007/09/17/almost-bullet-proof-css-calendar/"&gt;recently altered his blog&lt;/a&gt; to show the date of the posting with a calendar 'icon' rendered by a CSS style-sheet. While I appreciate his frustration with the inconsistent handling of CSS by different browsers, that is not what caught my eye. Here is his graphic:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_nyDSKzPKawU/RvAH0DOEodI/AAAAAAAAABg/LiOwtrDpv8Q/s1600-h/CSSCalendar.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_nyDSKzPKawU/RvAH0DOEodI/AAAAAAAAABg/LiOwtrDpv8Q/s400/CSSCalendar.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5111594167864435154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And here is the icon for iCal (the calendar program on Mac OS X):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_nyDSKzPKawU/RvAHYjOEocI/AAAAAAAAABY/KVOGT7faW_g/s1600-h/iCalIcon.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_nyDSKzPKawU/RvAHYjOEocI/AAAAAAAAABY/KVOGT7faW_g/s400/iCalIcon.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5111593695418032578" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;What do you think? Is he is suppressing a desire to get a Mac?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-400093659271659954?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/400093659271659954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=400093659271659954' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/400093659271659954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/400093659271659954'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/09/eric-burke-suppressing-desire-to-get.html' title='Eric Burke suppressing a desire to get a Mac?'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_nyDSKzPKawU/RvAH0DOEodI/AAAAAAAAABg/LiOwtrDpv8Q/s72-c/CSSCalendar.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-2370958272721202072</id><published>2007-08-08T23:10:00.001-05:00</published><updated>2007-08-08T23:30:14.294-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='curves'/><category scheme='http://www.blogger.com/atom/ns#' term='project planning'/><category scheme='http://www.blogger.com/atom/ns#' term='risk'/><category scheme='http://www.blogger.com/atom/ns#' term='project management'/><category scheme='http://www.blogger.com/atom/ns#' term='estimation'/><title type='text'>Gilstrap Estimation Curves</title><content type='html'>I am constantly producing estimates. How long will this project take? How long will that feature take? How long will it take us to test things? And my favorite: How long will it take to fix the bugs we haven't found?&lt;br /&gt;&lt;br /&gt;Other than the last, nonsense question, these are legitimate issues to address in the process of software development. Unfortunately, our industry (and some others) tends to be very simplistic in their estimates.&lt;br /&gt;&lt;br /&gt;There are many ways to go wrong in estimating. Perhaps the the most common is attempting to pretend that people are 'resources' and that everyone is interchangeable. But that's a topic for another day. Today, I want to look at a way of estimating that is simple enough to use yet isn't one dimensional.&lt;br /&gt;&lt;br /&gt;One of the biggest problems with estimating is trying to incorporate some notion of risk in the estimate. Is your estimate iron-clad, where you are 99.99999% sure you'll meet the deadline (and consequently there's a chance you can beat it), or will you be working flat-out trying to meet the date with only a limited chance of making it? This is one of the most common problems in projects: one party gives an aggressive estimate and another party assumes it's conservative. We need a better way to communicate the likelihood of completing a task in a given amount of time.&lt;br /&gt;&lt;br /&gt;To capture this idea, we need to represent the work with more than a single number, and we need to incorporate risk. Happily, this lends itself to a two-dimensional graph, with effort (time) as the x-axis and likelihood of completion (what I'll call "confidence") as the y-axis, like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_nyDSKzPKawU/RrqUhEHG_0I/AAAAAAAAAAw/P9HdnEEIf9E/s1600-h/BasicCurve.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_nyDSKzPKawU/RrqUhEHG_0I/AAAAAAAAAAw/P9HdnEEIf9E/s400/BasicCurve.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5096549224083291970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The line represents your confidence, &lt;b&gt;at the time you make the estimate&lt;/b&gt;, that you will solve the problem with the effort specified. Depending on the scope of the work, the effort line might be measured in hours, days, weeks, months, or even years. The basic idea is that it will take some period of time to understand the unknowns of a problem, which is the early, low-confidence part of the curve. Once the basic problem is understood work proceeds apace, and confidence goes up rapidly. The tail of the curve represents the fact that things might go well and we could get done earlier than expected. Let's take an example. &lt;br /&gt;&lt;br /&gt;Assume you are asked to implement a new kind of cache for your system. It involves a &lt;a href="http://en.wikipedia.org/wiki/Cache_algorithms"&gt;"least recently used" (LRU) strategy&lt;/a&gt; along with a maximum age for cache entries. Instead of saying "it'll take 10 man-days of effort", as if it can not take only 9 or would never take 11, you can graph it, like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_nyDSKzPKawU/RrqUhUHG_1I/AAAAAAAAAA4/3PrmiDGau6E/s1600-h/TenDays.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_nyDSKzPKawU/RrqUhUHG_1I/AAAAAAAAAA4/3PrmiDGau6E/s400/TenDays.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5096549228378259282" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This says that you think there's about a 2% chance of completing the work in 1 man-day, a 20% chance in four man-days, a 90% chance you'll get done in 8 days, etc. You are not saying you'll be done in a specific number of man-days. Instead, you provide an estimate of the likelihood you'll complete the work in a particular number of man-days. This is useful because it helps provide some insight into the overall risk you see in the work.&lt;br /&gt;&lt;br /&gt;This is not to say that all problems follow this curve. Some may in fact be so difficult that they never approach 100%. For example, if you thought there was only a 50% chance you would actually be able to come up with a solution to a problem, you might have a graph like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_nyDSKzPKawU/RrqUhkHG_2I/AAAAAAAAABA/GXlCxi_DQ9c/s1600-h/LogFiftyPercent.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_nyDSKzPKawU/RrqUhkHG_2I/AAAAAAAAABA/GXlCxi_DQ9c/s400/LogFiftyPercent.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5096549232673226594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Similarly, if you think the problem is well-understood, your graph might look like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_nyDSKzPKawU/RrqUh0HG_3I/AAAAAAAAABI/4xqgdW0iOtE/s1600-h/WellUnderstood.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_nyDSKzPKawU/RrqUh0HG_3I/AAAAAAAAABI/4xqgdW0iOtE/s400/WellUnderstood.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5096549236968193906" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;At first this might seem odd. But if you really understand the problem you'll be able to estimate the required effort very accurately. This means there's very little chance of getting done faster than your estimate, but it also means there's very little chance you'll run long.&lt;br /&gt;&lt;br /&gt;Perhaps this is a well-known thing in project management circles, but I've never seen anything like it in over twenty years in industry. I plan to use these estimation curves to better communicate my estimates to others.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-2370958272721202072?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/2370958272721202072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=2370958272721202072' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2370958272721202072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2370958272721202072'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/08/gilstrap-estimation-curves.html' title='Gilstrap Estimation Curves'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_nyDSKzPKawU/RrqUhEHG_0I/AAAAAAAAAAw/P9HdnEEIf9E/s72-c/BasicCurve.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1767790223285949787</id><published>2007-07-31T10:31:00.000-05:00</published><updated>2007-07-31T11:30:38.899-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='distributed computing'/><category scheme='http://www.blogger.com/atom/ns#' term='iterator'/><category scheme='http://www.blogger.com/atom/ns#' term='RemoteIterator'/><title type='text'>RemoteIterator, episode 2</title><content type='html'>Last time, I lamented the lack of a standard RemoteIterator interface in Java. I went on to show a version that showed up not long ago in &lt;a href="http://jackrabbit.apache.org/"&gt;Jackrabbit&lt;/a&gt; (an implementation of &lt;a href="http://jcp.org/aboutJava/communityprocess/final/jsr170/index.html"&gt;JSR 170&lt;/a&gt;) and the version I've recreated several times in the past. Let's take a quick look at them again.&lt;br /&gt;&lt;br /&gt;Jackrabbit's:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface RemoteIterator extends Remote {&lt;br /&gt;&lt;br /&gt;    long getSize() throws RemoteException;&lt;br /&gt;&lt;br /&gt;    void skip(long items) throws NoSuchElementException, RemoteException;&lt;br /&gt;&lt;br /&gt;    Object[] nextObjects() throws IllegalArgumentException, RemoteException;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The RemoteIterator interface I've recreated a number of times over the years:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface RemoteIterator&lt;&lt;T&gt;T&lt;/T&gt;&gt; extends Remote {&lt;br /&gt;&lt;br /&gt;    public boolean hasMore() throws RemoteException;&lt;br /&gt;&lt;br /&gt;    public List&lt;&lt;T&gt;T&lt;/T&gt;&gt; next( int preferredBatchSize ) throws RemoteException;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's go through the methods individually:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;u&gt;long getSize() throws RemoteException&lt;/u&gt;&lt;br&gt;&lt;br&gt;This seems problematic to me. Perhaps in Jackrabbit, it is reasonable to assume that you will always know the size of the collection to be iterated. But in many situations, this is not the case. For example, if the iterator is ultimately coming from a database cursor and the collection is large, we want to avoid having to run two SQL queries or store the entire collection in memory (which may be prohibitively expensive). I can see a derived interface, perhaps named &lt;i&gt;BoundedRemoteIterator&lt;/i&gt; or something, but not in this interface.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;u&gt;void skip(long items) throws NoSuchElementException, RemoteException&lt;/u&gt;&lt;br&gt;&lt;br&gt;I love this idea. Wish I'd thought of it myself. A trivial implementation would do whatever &lt;i&gt;next( items ),&lt;/i&gt; would do and simply not return the items. A smart implementation can save time and space by not actually generating the objects (since they won't be returned). The only change I would make is to have a return value of the number of items skipped and get rid of the &lt;i&gt;NoSuchElementException&lt;/i&gt;. If I ask to skip 50 items and only 29 more exist, I would get a return of 29 and no exception. Admittedly, if the interface included a &lt;i&gt;getSize&lt;/i&gt; operation, throwing NoSuchElementException is less strange. But even in that case, it seems too rigid to consider skipping more items than are left an 'exception'.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;u&gt;boolean hasMore() throws RemoteException&lt;/u&gt;&lt;br&gt;&lt;br&gt;At first, you might think this is a wasteful operation. Why make a call to find out if there are more items? This results in 'extra' remote calls instead of simply asking for the next item. However, if we imagine the items themselves as quite large (e.g. multi-megapixel images), we start to see a use for this API. If we were keeping the &lt;i&gt;getSize&lt;/i&gt; operation, this might not be necessary, but again, getSize() requires state tracking on the client side that can substantially complicate the client code.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;u&gt;Object[] nextObjects() throws IllegalArgumentException, RemoteException&lt;/u&gt;&lt;br&gt;&lt;br&gt;This is similar to my &lt;i&gt;next&lt;/i&gt; operation discussed below, except (1) it throws &lt;i&gt;IllegalArgumentException&lt;/i&gt;, (2) returns an array of objects instead of a List of a specific type, and (3) does not take a preferred size.&lt;br&gt;&lt;br&gt;(1) I'm guessing that the &lt;i&gt;getSize&lt;/i&gt; operation is intended to be called and then the caller is expected to know when all items in the iterator are consumed. This creates a problem if the client consumes different parts of the iterator in different parts of the code. Now, they have to pass around 2 or 3 items (remote iterator, number of items consumed so far, and possibly the total size) to do this. This is clunky to say the least.&lt;br&gt;&lt;br&gt;(2) Jackrabbit may have been specified before 1.5 was in wide use, or may feel the need for backward compatibility, hence the use of an array of Object. The downside to this is the loss of type-safety and loss of an API that can be optimized. See the discussion of &lt;i&gt;next&lt;/i&gt; below.&lt;br&gt;&lt;br&gt;(3) See the discussion of &lt;i&gt;next&lt;/i&gt; below for the rationale for a preferred batch size.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;u&gt;List&amp;lt;T&amp;gt; next( int preferredBatchSize ) throws RemoteException&lt;/u&gt;&lt;br&gt;&lt;br&gt;By returning a &lt;i&gt;List&amp;lt;T&amp;gt;&lt;/i&gt;, we not only introduce type information/safety, we also allow for further optimization. The List returned could be a simple implementation such as LinkedList or ArrayList. Or, it could be a smart List that collaborates with the RemoteIterator to delay retrieving its contents or retrieve the contents in separate threads. There are lots of possibilities introduced by avoiding raw Java arrays.&lt;br&gt;&lt;br&gt;Allowing the client to pass a preferred batch size provides many optimization opportunities. For example, if displaying the items in a GUI, they can ask for 'page-sized' numbers of items. By making the preferred size a suggestion, it allows an implementation leeway to ignore requests that are deemed unmanageably small or large for the kind of information being returned.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;So, where does this leave us? Our RemoteIterator now looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import java.rmi.Remote;&lt;br /&gt;import java.rmi.RemoteException;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Interface for a remote iterator that returns entries of a particular type.&lt;br /&gt; * @see java.util.Iterator&lt;br /&gt; * @see java.rmi.Remote&lt;br /&gt; */&lt;br /&gt;public interface RemoteIterator&lt;&lt;T&gt;T&lt;/T&gt;&gt; extends Remote {&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Determine if there are more items to be iterated across&lt;br /&gt;     * @return true if there are more items to be&lt;br /&gt;     * iterated, false otherwise.&lt;br /&gt;     * @throws RemoteException if a problem occurs with&lt;br /&gt;     * communication or on the remote server.&lt;br /&gt;     */&lt;br /&gt;    boolean hasMore() throws RemoteException;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Skip some number of items.&lt;br /&gt;     * @param items the number of items to skip at maximum. If there are fewer&lt;br /&gt;     * items than this left, all remaining items are skipped.&lt;br /&gt;     * @return the number of itmes actually skipped. This will equal &lt;&lt;T&gt;code&lt;/T&gt;&gt;items&lt;&lt;T&gt;/code&lt;/T&gt;&gt;&lt;br /&gt;     * unless there were not that many items left in the iteration.&lt;br /&gt;     * @throws RemoteException if a problem occurs with&lt;br /&gt;     * communication or on the remote server.&lt;br /&gt;     */&lt;br /&gt;    int skip(long items) throws RemoteException;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Get some number of items&lt;br /&gt;     * @param preferredBatchSize a suggested number of items to return. The implementation&lt;br /&gt;     * is not required to honor this request if it will prove too difficult.&lt;br /&gt;     * @return a List of the next items in the iteration, in iteration order.&lt;br /&gt;     * @throws RemoteException if a problem occurs with&lt;br /&gt;     * communication or on the remote server.&lt;br /&gt;     */&lt;br /&gt;    List&lt;&lt;T&gt;T&lt;/T&gt;&gt; next( int preferredBatchSize ) throws RemoteException;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'm still wondering why this isn't part of the JDK.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1767790223285949787?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1767790223285949787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1767790223285949787' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1767790223285949787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1767790223285949787'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/07/remoteiterator-episode-2.html' title='RemoteIterator, episode 2'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-4868651670640202257</id><published>2007-07-19T21:56:00.000-05:00</published><updated>2007-07-31T10:33:24.686-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='distributed computing'/><category scheme='http://www.blogger.com/atom/ns#' term='iterator'/><title type='text'>RemoteIterator, where art thou?</title><content type='html'>I've written my fair share of remote iterators over the years. The concept is simple enough: provide an interface for iterating over a set of items that come from a remote source. The harder part is making a good abstraction that can still perform well.&lt;br /&gt;&lt;br /&gt;Even so, it surprises me that, as far as I can tell, there hasn't been a standardized form of this. There's been discussion for a long time, with my earliest memories stemming from the JINI mailing lists back in 2001. Yet there's nothing in the core as of Java 6.  There are random postings on various groups on the subject through the years, but no actual API that I can find.&lt;br /&gt;&lt;br /&gt;Recently, I was teaching my &lt;a href="http://www.cait.wustl.edu/index.php?option=com_courses&amp;task=view&amp;code=TTJV40"&gt;course in Java performance tuning&lt;/a&gt; and discussing remote iterators, when a student mentioned that  &lt;a href="http://jackrabbit.apache.org/"&gt;Jackrabbit&lt;/a&gt; (an implementation of &lt;a href="http://jcp.org/aboutJava/communityprocess/final/jsr170/index.html"&gt;JSR 170&lt;/a&gt;) has an RMI remoting API called &lt;a href="http://jackrabbit.apache.org/doc/components/jcr-rmi.html"&gt;Jackrabbit JCR-RMI&lt;/a&gt; which includes a &lt;a href="http://jsourcery.com/api/apache/jackrabbit/1.0.1/org/apache/jackrabbit/rmi/remote/RemoteIterator.html"&gt;RemoteIterator&lt;/a&gt;. As of version 1.0.1, it looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface RemoteIterator extends Remote {&lt;br /&gt;&lt;br /&gt;    long getSize() throws RemoteException;&lt;br /&gt;&lt;br /&gt;    void skip(long items) throws NoSuchElementException, RemoteException;&lt;br /&gt;&lt;br /&gt;    Object[] nextObjects() throws IllegalArgumentException, RemoteException;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is the RemoteIterator interface I've recreated a number of times over the years:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public interface RemoteIterator&lt;T&gt; extends Remote {&lt;br /&gt;&lt;br /&gt;    public boolean hasMore() throws RemoteException;&lt;br /&gt;&lt;br /&gt;    public List&lt;T&gt; next( int preferredBatchSize ) throws RemoteException;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;These two APIs are strikingly similar. They both extend Remote, they both provide a means to get some number of the next items to be retrieved (mine with generics and theirs without), and they are both quite small. Yet they are also strikingly different: JackRabbit's API has both &lt;i&gt;getSize&lt;/i&gt; and &lt;i&gt;skip&lt;/i&gt; while mine doesn't.&lt;br /&gt;&lt;br /&gt;Next time, I'll describe what I see as the major differences and what I like about each. I'll also compare the client-side front-end for remote iterators from Jackrabbit and the one I've created. In the mean time, consider it a challenge to comment about everything I was going to bring up and more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-4868651670640202257?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/4868651670640202257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=4868651670640202257' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4868651670640202257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4868651670640202257'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/07/remoteiterator-where-art-thou.html' title='RemoteIterator, where art thou?'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-1845835839152429035</id><published>2007-07-02T15:20:00.000-05:00</published><updated>2007-07-02T15:26:51.982-05:00</updated><title type='text'>Random Acts of Blog</title><content type='html'>I've been subscribed to Brian Coyner's blog for some time. I had this URL:&lt;br /&gt;&lt;br /&gt;feed://beanman.wordpress.com/atom.xml&lt;br /&gt;&lt;br /&gt;Recently, I was quite surprised when Brian seemed to stop blogging about software and began blogging about bands I had no idea he liked. But I chalked it up to a sudden burst of music interest.&lt;br /&gt;&lt;br /&gt;Then, today, I tried to go to his blog and got this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_nyDSKzPKawU/RolfHtuwR_I/AAAAAAAAAAg/Qw9m1wZPFw4/s1600-h/Freakish.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_nyDSKzPKawU/RolfHtuwR_I/AAAAAAAAAAg/Qw9m1wZPFw4/s320/Freakish.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5082698240603736050" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It turns out to be a blog feed for People Magazine's StyleWatch. WTF? A bit of digging and it would seem that the feed above no longer works. But instead of giving me an error, I'm directed to a random wordpress.com blog page instead. Talk about non-intuitive!&lt;br /&gt;&lt;br /&gt;Freaky....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-1845835839152429035?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/1845835839152429035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=1845835839152429035' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1845835839152429035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/1845835839152429035'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/07/random-acts-of-blog.html' title='Random Acts of Blog'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_nyDSKzPKawU/RolfHtuwR_I/AAAAAAAAAAg/Qw9m1wZPFw4/s72-c/Freakish.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-4788219833423985989</id><published>2007-06-28T22:09:00.001-05:00</published><updated>2007-06-28T22:17:09.528-05:00</updated><title type='text'>Friday Java Quiz</title><content type='html'>My friend, &lt;a href="http://www.weiqigao.com/blog/"&gt;Weiqi Gao&lt;/a&gt;, frequently posts a &lt;a href="http://www.weiqigao.com/blog/2007/06/22.html"&gt; Friday Java Quiz &lt;/a&gt;. I ran into a situation today that seems like a good one.&lt;br /&gt;&lt;br /&gt;Suppose you have the following two classes:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Test {&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        System.err.println( Foo.class.getName() );&lt;br /&gt;        System.err.println( "Testing, 1, 2, 3..." );&lt;br /&gt;        new Foo();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;public class Foo {&lt;br /&gt;    static {&lt;br /&gt;        System.err.println( "Foo here." );&lt;br /&gt;    }&lt;br /&gt;    public Foo() {&lt;br /&gt;        System.err.println( "New Foo!" );&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Without running this program, do you know what the output will be?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-4788219833423985989?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/4788219833423985989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=4788219833423985989' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4788219833423985989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/4788219833423985989'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/06/friday-java-quiz.html' title='Friday Java Quiz'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-2815171052262772129</id><published>2007-05-17T10:10:00.000-05:00</published><updated>2007-05-17T10:13:41.103-05:00</updated><title type='text'>Kyle Cordes on tools</title><content type='html'>Kyle Cordes wants people to view a &lt;a href="http://www.youtube.com/watch?v=4XpnKHJAok8"&gt;presentation by Linus Torvalds&lt;/a&gt; discussing distributed version control tools. I plan to watch the talk soon, but a comment Kyle made &lt;a href="http://kylecordes.com/2007/05/17/linux-git-distributed/"&gt;in his blog entry&lt;/a&gt; really rang true. It's something I've been saying (less eloquently) for a long time:&lt;br /&gt;&lt;br /&gt;"I have heard it said that these are all 'just tools' which don’t matter, you simply use whatever the local management felt like buying. That is wrong: making better tool choices will make your project better (cheaper, faster, more fun, etc.), making worse tool choices will make your project worse (more expensive, slower, painful, higher turnover, etc.)"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-2815171052262772129?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/2815171052262772129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=2815171052262772129' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2815171052262772129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/2815171052262772129'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/05/kyle-cordes-on-tools.html' title='Kyle Cordes on tools'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-6760713369264674340</id><published>2007-04-06T21:10:00.000-05:00</published><updated>2007-04-06T22:24:40.207-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blog-tag'/><title type='text'>I've been tagged</title><content type='html'>There is apparently a &lt;a href="http://pulverblog.pulver.com/archives/006087.html"&gt;blog-tag game&lt;/a&gt; going around, and &lt;a href="http://javajeff.blogspot.com/2007/04/i-have-been-tagged-twice.html"&gt;Jeff Brown tagged me&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;I practice &lt;a href="http://en.wikipedia.org/wiki/Cheng_Man-ch'ing"&gt;Yang-style short form T'ai Chi Ch'uan&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I've been having fun lately making home-made &lt;a href="http://www.youtube.com/watch?v=13Ah9ES2yTU"&gt;no knead bread&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I enjoy woodworking. I'm currently tuning my latest large tool purchase. It took me about a year to save enough to get my &lt;a href="http://www.deltamachinery.com/index.asp?e=136&amp;p=4743"&gt;bandsaw&lt;/a&gt;. I even &lt;a href="http://walkingthefringe.blogspot.com/2007/01/home-improvement-closet-project.html"&gt;build things&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I've used a Mac since &lt;a href="http://upload.wikimedia.org/wikipedia/commons/e/e3/Macintosh_128k_transparency.png"&gt;the original 128k version introduced in 1984&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Perhaps because I didn't drink much as a teenager, I never developed a taste for beer. I do enjoy &lt;a href="http://en.wikipedia.org/wiki/Single_Malt_Scotch"&gt;single malt scotch&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Now I'm tagging &lt;a href="http://www.weiqigao.com/blog/"&gt;Weiqi&lt;/a&gt;, &lt;a href="http://bradshuler.blogspot.com/"&gt;Brad&lt;/a&gt;, &lt;a href="http://web.mac.com/dsjove/iWeb/Site/Dave%27s%20Strange%20Days/Dave%27s%20Strange%20Days.html"&gt;Dave&lt;/a&gt;, &lt;a href="http://beanman.wordpress.com/"&gt;Brian&lt;/a&gt;, and &lt;a href="http://thebabycook.blogspot.com/"&gt;Melben&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-6760713369264674340?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/6760713369264674340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=6760713369264674340' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6760713369264674340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/6760713369264674340'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/04/ive-been-tagged.html' title='I&apos;ve been tagged'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-5761552709017043545</id><published>2007-03-20T00:03:00.000-05:00</published><updated>2007-03-20T00:27:01.499-05:00</updated><title type='text'>Too much C++</title><content type='html'>(with apologies to Monty Python)&lt;br /&gt;&lt;br /&gt;&lt;i&gt;A customer enters a software shop.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; 'Ello, I wish to register a complaint.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;The owner does not respond.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; 'Ello, Miss?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; What do you mean "miss"?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; I'm sorry, I have a cold. I wish to make a complaint!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; We're closin' for lunch.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; Never mind that, my lad. I wish to complain about this programming language what I purchased not half an hour ago from this very boutique.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; Oh yes, the, uh, the Danish C++...What's,uh...What's wrong with it?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; I'll tell you what's wrong with it, my lad. 'E's crap, that's what's wrong with it!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; No, no, 'e's uh,...it's resting.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; Look, matey, I know a crap language when I see one, and I'm looking at one right now.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; No no it's not crap, it's, it's restin'! Remarkable language, the Danish C++, idn'it, ay? Beautiful syntax!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; The syntax don't enter into it. It's stone crap.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; Nononono, no, no! 'E's resting!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; All right then, if it's restin', I'll wake him up! (shouting at the cage) 'Ello, Mister Plymorphic Language! I've got a lovely fresh computer for you if you show...&lt;br /&gt;&lt;br /&gt;&lt;i&gt;owner hits the cage&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; There, he moved!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; No, he didn't, that was you hitting the cage!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; I never!!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline: &lt;/b&gt;Yes, you did!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; I never, never did anything...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; (yelling and hitting the cage repeatedly) 'ELLO POLYMORPH!!!!! Testing! Testing! Testing! Testing! This is your nine o'clock alarm call!&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Takes language out of the cage and thumps its head on the counter. Throws it up in the air and watches it plummet to the floor.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; Now that's what I call a crap language.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; No, no.....No, 'e's stunned!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; STUNNED?!?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; Yeah! You stunned him, just as he was wakin' up! Danish C++'s stun easily, major.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; Um...now look...now look, mate, I've definitely 'ad enough of this. That language is definitely deceased, and when I purchased it not 'alf an hour ago, you assured me that its total lack of movement was due to it bein' tired and shagged out following a prolonged standards meeting.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; Well, it's...it's, ah...probably pining for a common runtime.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; PININ' for a COMMON RUNTIME?!?!?!? What kind of talk is that?, look, why did he fall flat on his back the moment I got 'im home?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; The Danish C++ prefers keepin' on it's back! Remarkable language, id'nit, squire? Lovely syntax!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; Look, I took the liberty of examining that language when I got it home, and I discovered the only reason that it had been sitting on its perch in the first place was that it had been NAILED there.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;pause&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; Well, o'course it was nailed there! If I hadn't nailed that language down, it would have nuzzled up to those computers, broke 'em apart with its beak, and VOOM! Feeweeweewee!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; "VOOM"?!? Mate, this language wouldn't "voom" if you put four million volts through it! 'E's bleedin' demised!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; No no! 'E's pining!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; 'E's not pinin'! 'E's passed on! This language is no more! He has ceased to be! 'E's expired and gone to meet 'is maker! 'E's a stiff! Bereft of life, 'e rests in peace! If you hadn't nailed 'im to the perch 'e'd be pushing up the daisies! 'Is processes are now 'istory! 'E's off the map! 'E's kicked the bucket, 'e's shuffled off 'is mortal coil, run down the curtain and joined the bleedin' choir invisibile!! THIS IS AN EX-LANGUAGE!!&lt;br /&gt;&lt;br /&gt;&lt;i&gt;pause&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; Well, I'd better replace it, then. (he takes a quick peek behind the counter) Sorry squire, I've had a look 'round the back of the shop, and uh, we're right out of languages.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; I see. I see, I get the picture.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; I got Java.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;pause&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; Pray, does it support pointers?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; Nnnnot really.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; WELL IT'S HARDLY A BLOODY REPLACEMENT, IS IT?!!???!!?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; N-no, I guess not. (gets ashamed, looks at his feet)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; Well.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;pause&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Owner:&lt;/b&gt; (quietly) D'you.... d'you want to come back to my place an' dereference some pointers?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Mr. Praline:&lt;/b&gt; (looks around) Yeah, all right, sure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-5761552709017043545?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/5761552709017043545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=5761552709017043545' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/5761552709017043545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/5761552709017043545'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/03/too-much-c.html' title='Too much C++'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-8387371179143255036</id><published>2007-03-15T14:51:00.000-05:00</published><updated>2007-03-15T21:58:50.986-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='XP'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='bad design'/><category scheme='http://www.blogger.com/atom/ns#' term='virtual memory'/><title type='text'>The ever-leaky abstractions of Microsoft</title><content type='html'>I was using Windows XP inside Parallels today and got a message from Windows that I had never seen before:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_nyDSKzPKawU/RfoHZlPt_OI/AAAAAAAAAAM/Y3EMTFvwdvQ/s1600-h/XPVirtualMemoryIsBroken.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_nyDSKzPKawU/RfoHZlPt_OI/AAAAAAAAAAM/Y3EMTFvwdvQ/s320/XPVirtualMemoryIsBroken.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5042350868870855906" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;How on earth could you design an operating system that could detect there wasn't enough virtual memory, expand the virtual memory pool automatically, but would deny applications requests for virtual memory at the same time?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-8387371179143255036?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/8387371179143255036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=8387371179143255036' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8387371179143255036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/8387371179143255036'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/03/ever-leaky-abstractions-of-microsoft.html' title='The ever-leaky abstractions of Microsoft'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_nyDSKzPKawU/RfoHZlPt_OI/AAAAAAAAAAM/Y3EMTFvwdvQ/s72-c/XPVirtualMemoryIsBroken.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-3743095141063713422</id><published>2007-03-01T10:57:00.000-06:00</published><updated>2007-03-01T12:37:30.371-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit testing'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C++ Unit testing frameworks: do they all suck?</title><content type='html'>Well, I've been doing some work with C++ lately and I quickly ran into the need for a unit testing framework. I found an &lt;a href="http://www.gamesfromwithin.com/articles/0412/000061.html"&gt;excellent article&lt;/a&gt; reviewing the state of C++ unit testing frameworks. My first surprise was how many of them there are. This article alone reviewed six of them. Even if those are all of the 'serious contenders', that's about 3 or 4 frameworks too many.&lt;br /&gt;&lt;br /&gt;I was also surprised at how primitive the C++ unit testing frameworks feel. I realized after a while that the lack of dynamic/reflective behavior in C++ is part (or even all) of the issue. Since reflection is not built in, many of the grungy tasks that unit testing frameworks in Java automate for us can't be automated in a C++ unit testing framework. What a shame. We're left using frameworks that feel awkard and leave a lot to be desired.&lt;br /&gt;&lt;br /&gt;Then I got to thinking: a number of the C++ wizards around here talk about the power of C++ template programming. It seems to me it ought to be possible to clean up and automate what would normally be done in Java with reflection using templates in C++. I wonder why this hasn't been done. I don't know templates well enough to do it myself, but I hope some wiz at C++ templates does this sometime soon.&lt;br /&gt;&lt;br /&gt;For now, I'll keep trudging along with &lt;a href="http://cppunit.sourceforge.net/cppunit-wiki"&gt;CppUnit&lt;/a&gt;, and maybe try out &lt;a href="http://cxxtest.sourceforge.net/"&gt;CxxTest&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-3743095141063713422?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/3743095141063713422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=3743095141063713422' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/3743095141063713422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/3743095141063713422'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/03/c-unit-testing-frameworks-do-they-all.html' title='C++ Unit testing frameworks: do they all suck?'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-117035106501228943</id><published>2007-02-01T18:55:00.000-06:00</published><updated>2007-02-01T20:41:06.508-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Xcode'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='JNI'/><title type='text'>Adding a JNI Library template to Xcode</title><content type='html'>I've been doing some work with &lt;a href="http://developer.apple.com/tools/xcode/"&gt;Xcode, Apple's IDE&lt;/a&gt;, recently. I wanted to use it for doing Java JNI work, since I'm doing some of that, but found the &lt;a href="http://developer.apple.com/java/jniuniversal.html"&gt;normal tutorial&lt;/a&gt; assumes you are building an entire application with Xcode. Well, Xcode pales in comparison to &lt;a href="http://www.jetbrains.com/idea/"&gt;IDEA&lt;/a&gt; for working with Java. &lt;br /&gt;&lt;br /&gt;So I wondered if I could use IDEA for working with the Java code and Xcode for managing the JNI bindings and the C++ code that goes with it. After a bit of playing around (primarily pulling bits and pieces from &lt;a href="http://developer.apple.com/java/jniuniversal.html"&gt;Apple's mini-tutorial on JNI with Xcode&lt;/a&gt;), I figured out how to build a JNI library that I can use easily from IDEA.&lt;br /&gt;&lt;br /&gt;That got me to thinking about the four or five steps I would have to do each time I created a new one of these projects (which I expect to be doing for a while). I vaguely remembered a friend talking about customizing Xcode by creating new templates. So I set out to create one for building JNI libraries.&lt;br /&gt;&lt;br /&gt;I googled around a bit and found out that Xcode keeps its templates in:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    /Library/Application Support/Apple/Developer Tools&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Looking there, I see a folder called "Project Templates", and sure enough inside are all the templates for Xcode projects. &lt;br /&gt;&lt;img style="float:center; margin:10px 10px 10px 10px;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/x/blogger/5235/562/400/622916/DeveloperToolsFolders.png" border="0" alt="" /&gt;&lt;br /&gt;There are several that look promising, but what's the difference between a "BSD Dynamic Library", a "C++ Dynamic Library", and a "C++ Standard Dynamic Library"? Something based on the "C++ Standard Dynamic Library" seems right.&lt;br /&gt;&lt;br /&gt;So, I create a test project and make a few changes to produce a JNI library:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Change "Executable Extension" to be "jnilib"&lt;/li&gt;&lt;li&gt;Change "Header Search Paths" to include "$(SDK)/System/Library/Frameworks/JavaVM.framework/Headers"&lt;/li&gt;&lt;li&gt;Delete the Carbon framework (why was it there in the first place?)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Now, save the project, make sure it builds (which it does) and create a simple Java HelloWorld with a native method that calls our Test class. &lt;br /&gt;&lt;br /&gt;Having verified that this sort of project can be used to produce a JNI Library, it's time to create the custom project template. First I copy the "C++ Standard Dynamic Library template" to "JNI C++ Standard Dynamic Library". Now I need to find the right places to insert the additional items that were changed (and how to remove the Carbon framework). Looking at the template directory, I see a number of files. &lt;a href="http://photos1.blogger.com/x/blogger/5235/562/1600/440205/ContentsOfTheCopiedTemplate.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/x/blogger/5235/562/400/150580/ContentsOfTheCopiedTemplate.png" border="0" alt="" /&gt;&lt;/a&gt;But most of these are just boilerplate for source files in the project ('.pch', '.h', '.cp'). I think I can safely ignore these files at least for now (though I made note of them; if there prove to be certain common files in my JNI libraries moving forward, I can come back and try to tweak the project template to have different boilerplates).&lt;br /&gt;&lt;br /&gt;The key 'file' appears to be CppShared.xcodeproj. Right-clicking on it shows it is really a 'package' and I take a look inside.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:center; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/x/blogger/5235/562/400/264644/CppShared.xcodeprojIsAPackage.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;When I do so, I see two files: "TemplateInfo.plist" and "project.pbxproj". The '.plist' file has some interesting items about template files and lists of files upon which to perform macro expansion. Interestingly, the list of files matches the list of boilerplate files almost exactly. For now, I'm leaving these alone (again making note in case I want to change the set of boilerplate files in the project). There is also a Description item, which shows up in the "New Project..." window in Xcode. After tweaking that to make it better describe this new kind of project I'm creating, I move on to the "project.pbxproj" file.&lt;br /&gt;&lt;br /&gt;The "project.pbxproj" file is semi-text and semi-binary. So it's time to be careful and make a backup before experimenting. The first section of interest is this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* Begin PBXFileReference section */&lt;br /&gt;                08FB77AAFE841565C02AAC07 /* Carbon.framework */ = {isa = ...&lt;br /&gt;                32BAE0B70371A74B00C91783 /* «PROJECTNAME»_Prefix.pch */ = {isa = ...&lt;br /&gt;                50149BD909E781A5002DEE6A /* «PROJECTNAME».h */ = {isa = ...&lt;br /&gt;                5073E0C409E734A800EC74B6 /* «PROJECTNAME».cp */ = {isa = ...&lt;br /&gt;                5073E0C609E734A800EC74B6 /* «PROJECTNAME»Proj.xcconfig */ = {isa = ...&lt;br /&gt;                5073E0C709E734A800EC74B6 /* «PROJECTNAME»Target.xcconfig */ = {isa = ...&lt;br /&gt;                50B2938909F016FC00694E55 /* «PROJECTNAME»Priv.h */ = {isa = ...&lt;br /&gt;                D2AAC09D05546B4700DB518D /* lib«PROJECTNAME».dylib */ = {isa = &lt;br /&gt;PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; &lt;br /&gt;includeInIndex = 0; path = "lib«PROJECTNAME».dylib"; sourceTree = BUILT_PRODUCTS_DIR; };&lt;br /&gt;/* End PBXFileReference section */&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I ditch the Carbon framework reference (since I'm doing JNI) by deleting the line that references Carbon. If I want to use Carbon in a JNI library, I can always add it later. It turns out there are a bunch of other references to Carbon in the template, and I have to remove those as well.&lt;br /&gt;&lt;br /&gt;Now, I see the line that makes reference to "lib«PROJECTNAME».&lt;b&gt;dylib&lt;/b&gt;". I want the library to end with '.jnilib' since that is required by Mac OS X. So I patch this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;                D2AAC09D05546B4700DB518D /* lib«PROJECTNAME».jnilib */ = {isa = &lt;br /&gt;PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; &lt;br /&gt;includeInIndex = 0; path = "lib«PROJECTNAME».jnilib"; sourceTree = BUILT_PRODUCTS_DIR; };&lt;br /&gt;/* End PBXFileReference section */&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, I want the header search paths to include the location of the Java headers, and the executable extension to be '.jnilib'. Searching the test project's '.pbxproj' file, I find them in the Debug and Release build sections. However, there are two pairs of these, one pair for the library target and one pair for the overall project. I need to change the ones for the overall project:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;                1DEB916508733D950010E9CD /* Debug */ = {&lt;br /&gt;                        isa = XCBuildConfiguration;&lt;br /&gt;                        baseConfigurationReference = 5073E0C609E734A800EC74B6 /* «PROJECTNAME»&lt;b&gt;Proj.xcconfig&lt;/b&gt; */;&lt;br /&gt;                        buildSettings = {&lt;br /&gt;                                COPY_PHASE_STRIP = NO;&lt;br /&gt;                                DEBUG_INFORMATION_FORMAT = dwarf;&lt;br /&gt;                                &lt;b&gt;EXECUTABLE_EXTENSION = jnilib;&lt;/b&gt;&lt;br /&gt;                                GCC_ENABLE_FIX_AND_CONTINUE = YES;&lt;br /&gt;                                GCC_OPTIMIZATION_LEVEL = 0;&lt;br /&gt;                                &lt;b&gt;HEADER_SEARCH_PATHS = "$(SDK)/System/Library/Frameworks/JavaVM.framework/Headers";&lt;/b&gt;&lt;br /&gt;                                ZERO_LINK = YES;&lt;br /&gt;                        };&lt;br /&gt;                        name = Debug;&lt;br /&gt;                };&lt;br /&gt;                1DEB916608733D950010E9CD /* Release */ = {&lt;br /&gt;                        isa = XCBuildConfiguration;&lt;br /&gt;                        baseConfigurationReference = 5073E0C609E734A800EC74B6 /* «PROJECTNAME»&lt;b&gt;Proj.xcconfig&lt;/b&gt; */;&lt;br /&gt;                        buildSettings = {&lt;br /&gt;                                ARCHS = (&lt;br /&gt;                                        ppc,&lt;br /&gt;                                        i386,&lt;br /&gt;                                );&lt;br /&gt;                                DEAD_CODE_STRIPPING = YES;&lt;br /&gt;                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";&lt;br /&gt;                                &lt;b&gt;EXECUTABLE_EXTENSION = jnilib;&lt;br /&gt;                                HEADER_SEARCH_PATHS = "$(SDK)/System/Library/Frameworks/JavaVM.framework/Headers";&lt;/b&gt;&lt;br /&gt;                                PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;&lt;br /&gt;                                SEPARATE_STRIP = YES;&lt;br /&gt;                        };&lt;br /&gt;                        name = Release;&lt;br /&gt;                };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I was encouraged when creating a new project didn't fail. Looking at the default setup, everything seems to be in order (no Carbon framework, all the boilerplate files are there, etc.). A few minutes to add in the JNI binding function and change the Java program to use the new JNI library and I have a successful test.&lt;br /&gt;&lt;br /&gt;I'm sure there are many other customizations I could make if I only knew about them. I also wish there were a better tool than a text editor for editing these files (hopefully there is and someone will point it out to me).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-117035106501228943?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/117035106501228943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=117035106501228943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/117035106501228943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/117035106501228943'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/02/adding-jni-library-template-to-xcode.html' title='Adding a JNI Library template to Xcode'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-116944265935267613</id><published>2007-01-21T23:05:00.000-06:00</published><updated>2007-01-21T23:10:59.360-06:00</updated><title type='text'>Learning Cocoa</title><content type='html'>If you have any interest in the &lt;a href="http://developer.apple.com/cocoa/"&gt;Cocoa frameworks&lt;/a&gt; for Mac OS X, you might be interested &lt;a href="http://walkingthefringe.blogspot.com/2007/01/learning-cocoa-peeling-currency.html"&gt;a blog entry&lt;/a&gt; about learning how to program Cocoa. It's on my other blog, &lt;a href="http://walkingthefringe.blogspot.com"&gt;Walking the Fringe&lt;/a&gt;, because it's more exploratory than the material I like to put on this blog.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-116944265935267613?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/116944265935267613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=116944265935267613' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116944265935267613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116944265935267613'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/01/learning-cocoa.html' title='Learning Cocoa'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-116924381489333336</id><published>2007-01-19T17:22:00.000-06:00</published><updated>2007-01-21T13:03:13.793-06:00</updated><title type='text'>Configuring logging in your Spring configuration file</title><content type='html'>I detest multiple configuration files. Generally, I can't avoid having them, but whenever I can find a way to reduce the number of configuration files, the better.&lt;br /&gt;&lt;br /&gt;So, when I need to tailor the logging levels in my application, I have a choice: edit the default logging file (possibly effecting other applications if that copy of the JDK is shared as is typical), or create a separate logging configuration file and point logging to it (in the case of the JDK's logging, this means setting the &lt;i&gt;java.util.logging.config.file&lt;/i&gt; property).&lt;br /&gt;&lt;br /&gt;When using Spring, I'd really like to put my custom logging levels in my Spring configuration file, so I don't have yet another file that has to travel with my application. There doesn't appear to be any way built into Spring to do this, but by adding a tiny Java class to the application (one that is reusable for each application), we gain the ability to do this.&lt;br /&gt;&lt;br /&gt;The idea is to create a bean at the top of your Spring configuration file that will set your custom logging levels. For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &amp;lt;bean id="loggingConfigurer"&lt;br /&gt;          class="com.acme.logging.LoggingConfigurer"&lt;br /&gt;          lazy-init="false"&amp;gt;&lt;br /&gt;        &amp;lt;constructor-arg&amp;gt;&lt;br /&gt;            &amp;lt;props&amp;gt;&lt;br /&gt;                &amp;lt;prop key="org.springframework"&amp;gt;WARNING&amp;lt;/prop&amp;gt;&lt;br /&gt;                &amp;lt;prop key="org.hibernate"&amp;gt;WARNING&amp;lt;/prop&amp;gt;&lt;br /&gt;            &amp;lt;/props&amp;gt;&lt;br /&gt;        &amp;lt;/constructor-arg&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;i&gt;LoggingConfigurer&lt;/i&gt; class is trivial:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;package com.acme.logging;&lt;br /&gt;&lt;br /&gt;import java.util.Iterator;&lt;br /&gt;import java.util.Properties;&lt;br /&gt;import java.util.logging.Level;&lt;br /&gt;import java.util.logging.Logger;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Configures logging by being constructed. &lt;br /&gt; * Especially useful when created as a bean from a Spring configuration file.&lt;br /&gt; */&lt;br /&gt;public class LoggingConfigurer {&lt;br /&gt;&lt;br /&gt;  public LoggingConfigurer(Properties props) {&lt;br /&gt;    Iterator i = props.keySet().iterator();&lt;br /&gt;    while (i.hasNext()) {&lt;br /&gt;      String loggerName = (String) i.next();&lt;br /&gt;      String levelName = props.getProperty(loggerName);&lt;br /&gt;      try {&lt;br /&gt;        Level level = Level.parse(levelName);&lt;br /&gt;        Logger l = Logger.getLogger(loggerName);&lt;br /&gt;        l.setLevel(level);&lt;br /&gt;      }&lt;br /&gt;      catch ( IllegalArgumentException e ) {&lt;br /&gt;        System.err.println( "WARNING: Unable to parse '" &lt;br /&gt;           + levelName + "' as a java.util.Level for logger " &lt;br /&gt;           + loggerName + "; ignoring..." );&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, when Spring is initialized, very early on in the process, it encounters the entry for our &lt;i&gt;loggingConfigurer&lt;/i&gt; bean. Since the bean has lazy initialization turned off, it is immediately created and configured. That sets our logging levels to what we want.&lt;br /&gt;&lt;br /&gt;Admittedly, this isn't perfect. For example, these few lines of logging show up when launching a Spring and Hibernate application (prior to our bean turning the logging level to WARNING):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.core.CollectionFactory &lt;clinit&gt;&lt;br /&gt;INFO: JDK 1.4+ collections available&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions&lt;br /&gt;INFO: Loading XML bean definitions from class path resource [baseContext.xml]&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions&lt;br /&gt;INFO: Loading XML bean definitions from class path resource [unitTestingContext.xml]&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractRefreshableApplicationContext refreshBeanFactory&lt;br /&gt;INFO: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=2490106]: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [loggingConfigurer,hibernateStatsService,mbeanServer,exporter,persistence,setPersistenceManager,customEditorConfigurer,mySessionFactory]; root of BeanFactory hierarchy&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractApplicationContext refresh&lt;br /&gt;INFO: 8 beans defined in application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=2490106]&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractApplicationContext initMessageSource&lt;br /&gt;INFO: Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@a9a32c]&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractApplicationContext initApplicationEventMulticaster&lt;br /&gt;INFO: Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@2153fe]&lt;br /&gt;Jan 19, 2007 3:44:41 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons&lt;br /&gt;INFO: Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [loggingConfigurer,hibernateStatsService,mbeanServer,exporter,persistence,setPersistenceManager,customEditorConfigurer,mySessionFactory]; root of BeanFactory hierarchy]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But this is a great improvement over the huge amounts of output from Spring and (especially) Hibernate that show up otherwise.&lt;br /&gt;&lt;br /&gt;I'm sure a similar approach can be used with log4j. If anyone adapts this to log4j I'd love to know how that works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-116924381489333336?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/116924381489333336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=116924381489333336' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116924381489333336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116924381489333336'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/01/configuring-logging-in-your-spring.html' title='Configuring logging in your Spring configuration file'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-116910330642681956</id><published>2007-01-17T22:39:00.000-06:00</published><updated>2007-01-18T01:00:04.266-06:00</updated><title type='text'>Simplified Database Management in Spring-based Hibernate Unit Tests</title><content type='html'>I've been working with Hibernate recently, and needed a way to implement good unit tests. The goal of these unit tests is to make sure the Java code works in conjunction with the Hibernate mapping files. I found an &lt;a href="http://www.theserverside.com/tt/articles/article.tss?l=UnitTesting"&gt;excellent article&lt;/a&gt; on using HSQLDB to do unit testing of Hibernate classes on The Server Side.&lt;br /&gt;&lt;br /&gt;Unfortunately, one of the drawbacks of the approach described is that it requires you to write some custom JDBC code to clear out your tables after each unit test. From the article:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    statement.executeUpdate("delete from Batting");&lt;br /&gt;    statement.executeUpdate("delete from Fielding");&lt;br /&gt;    statement.executeUpdate("delete from Pitching");&lt;br /&gt;    statement.executeUpdate("delete from Player");&lt;br /&gt;    connection.commit();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Another possible option for handling this scenario if you are using Hibernate 3 is to use it's bulk update feature. I haven't used this feature yet, but apparently one wrinkle is that bulk deletes don't seem to cause cascade deletes. This requires that you remember to update your code each time you add a new table to those used by your application, or end up with problems in your unit tests due to data left over from the last test method.&lt;br /&gt;&lt;br /&gt;But in my opinion there's a better way: leverage the SchemaExport utility of Hibernate to automaticallly wipe out and recreate all your tables for you. The basic idea is that the &lt;i&gt;setUp&lt;/i&gt; method of your unit test calls the SchemaExport utility to wipe out the existing tables and recreate them. This leaves you with the proper tables and a clean slate upon which to create exactly the objects needed to run your test. This simplifies your unit testing and avoids having to maintain both the mapping files and the bulk delete code in tandem.&lt;br /&gt;&lt;br /&gt;To accomplish this, you need to make sure your Hibernate mapping files specify enough information for Hibernate's SchemaExport utility to generate and run database-specific DDL to recreate your tables, indexes, foreign keys, etc. For example, you might have a mapping file for a &lt;i&gt;SimplePerson&lt;/i&gt; class that looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD/EN"&lt;br /&gt;        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"&amp;gt;&lt;br /&gt;&amp;lt;hibernate-mapping package="com.acme.project"&amp;gt;&lt;br /&gt;    &amp;lt;class name="SimplePerson" table="PERSONS"&amp;gt;&lt;br /&gt;        &amp;lt;id name="id" type="int"&amp;gt;&lt;br /&gt;            &amp;lt;column name="CODE" not-null="true"/&amp;gt;&lt;br /&gt;            &amp;lt;generator class="native"/&amp;gt;&lt;br /&gt;        &amp;lt;/id&gt;&lt;br /&gt;        &amp;lt;property name="firstName" column="FIRST_NAME" not-null="true"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="lastName" column="LAST_NAME" not-null="true"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="contactEmail" column="CONTACT_EMAIL_ADDRESS"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="disabled" type="boolean"&amp;gt;&lt;br /&gt;            &amp;lt;column name="DISABLE" default="0" not-null="true"/&amp;gt;&lt;br /&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;    &amp;lt;/class&amp;gt;&lt;br /&gt;&amp;lt;/hibernate-mapping&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You need to make sure you have information about the SQL types of your mapped columns so Hibernate knows how to generate the tables, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD/EN"&lt;br /&gt;        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"&amp;gt;&lt;br /&gt;&amp;lt;hibernate-mapping package="com.acme.project"&amp;gt;&lt;br /&gt;    &amp;lt;class name="SimplePerson" table="PERSONS"&amp;gt;&lt;br /&gt;        &amp;lt;id name="id" type="int"&amp;gt;&lt;br /&gt;            &amp;lt;column name="CODE" &lt;b&gt;sql-type="integer"&lt;/b&gt; not-null="true"/&amp;gt;&lt;br /&gt;            &amp;lt;generator class="native"/&amp;gt;&lt;br /&gt;        &amp;lt;/id&gt;&lt;br /&gt;        &amp;lt;property name="firstName" column="FIRST_NAME" not-null="true"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="lastName" column="LAST_NAME" not-null="true"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="contactEmail" column="CONTACT_EMAIL_ADDRESS"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="disabled" type="boolean"&amp;gt;&lt;br /&gt;            &amp;lt;column name="DISABLE" &lt;b&gt;sql-type="int"&lt;/b&gt; default="0" not-null="true"/&amp;gt;&lt;br /&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;    &amp;lt;/class&amp;gt;&lt;br /&gt;&amp;lt;/hibernate-mapping&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;i&gt;sql-type&lt;/i&gt; attributes are what tell Hibernate how to generate DDL for your table. Once you have this, you can invoke the SchemaExport utility on your in-memory HSQLDB database and it will wipe out the old tables and their contents and recreate them empty.&lt;br /&gt;&lt;br /&gt;Now our only catch is invoking SchemaExport when using the Spring framework. If you're using Spring outside of any other container, you might use a &lt;i&gt;LocalSessionFactoryBean&lt;/i&gt; to do your Hibernate mapping work, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &amp;lt;bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&amp;gt;&lt;br /&gt;        &amp;lt;property name="mappingResources"&amp;gt;&lt;br /&gt;            &amp;lt;list&amp;gt;&lt;br /&gt;                &amp;lt;value&gt;com/acme/project/SimplePerson.hbm.xml&amp;lt;/value&amp;gt;&lt;br /&gt;                &amp;lt;!-- ... --&amp;gt;&lt;br /&gt;            &amp;lt;/list&amp;gt;&lt;br /&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;        &amp;lt;property name="configLocation" value="hibernateConfig.xml"/&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is great, except for one thing: you need access to the Hibernate &lt;font style="fixed"&gt;Configuration&lt;/font&gt; object in order to invoke SchemaExport and this is not easily available (from what I can find). There is nice workaround for this problem, though. If you use the ampersand ('&amp;') character in front of the name of a bean, you can get at the actual Spring object instead of the bean it creates. For example, the &lt;i&gt; LocalSessionFactoryBean&lt;/i&gt; has a public &lt;i&gt;getConfiguration&lt;/i&gt; method will get us the Hibernate &lt;i&gt;Configuration&lt;/i&gt; object we want. So we can implement a &lt;i&gt;setUp&lt;/i&gt; method that contains code like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;        LocalSessionFactoryBean l = (LocalSessionFactoryBean) factory.getBean("&lt;b&gt;&lt;font size="+2"&gt;&amp;&lt;/font&gt;&lt;/b&gt;mySessionFactory");&lt;br /&gt;        Configuration cfg = l.getConfiguration();&lt;br /&gt;        SchemaExport schemaExport = new SchemaExport(cfg);&lt;br /&gt;        schemaExport.create(false, true);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, every test method in your unit test will have a clean database with all the right tables, indexes, etc.&lt;br /&gt;&lt;br /&gt;One side benefit of this is that there's a good chance you will make the tests run faster than when connecting to an external database. But, most importantly, by avoiding dependencies on an external database you improve the reliability of your Hibernate mapping unit tests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-116910330642681956?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/116910330642681956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=116910330642681956' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116910330642681956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116910330642681956'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/01/simplified-database-management-in.html' title='Simplified Database Management in Spring-based Hibernate Unit Tests'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-116846127188232424</id><published>2007-01-10T14:13:00.000-06:00</published><updated>2007-01-10T14:34:31.900-06:00</updated><title type='text'>Hibernate: Simple Classes Aren't So Simple</title><content type='html'>I was fighting with Hibernate the other day, trying to figure out why it was giving me a query error. After much too long, I decided to implement unit tests for my measly two classes, which I thought were too simple to really need unit tests.&lt;br /&gt;&lt;br /&gt;Almost immediately, I found bugs in one of the two persistent classes I was working with that were confusing Hibernate and leading it to generate the error message that was confusing me.&lt;br /&gt;&lt;br /&gt;I checked the &lt;a href="http://www.hibernate.org/hib_docs/reference/en/html/best-practices.html"&gt;Hibernate best practices page&lt;/a&gt; and my lesson isn't listed. It may sound obvious, but here it is:&lt;br /&gt;&lt;br /&gt;While your classes may be simple, Hibernate is not. Always implement a unit test to verify that the simple parts of your persistent objects are mapped correctly by Hibernate before moving on to more complex issues like inheritance, relationships, containment, etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-116846127188232424?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/116846127188232424/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=116846127188232424' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116846127188232424'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116846127188232424'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2007/01/hibernate-simple-classes-arent-so.html' title='Hibernate: Simple Classes Aren&apos;t So Simple'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-116846152590520625</id><published>2006-09-11T14:36:00.000-05:00</published><updated>2007-01-10T14:40:34.656-06:00</updated><title type='text'>Open Source Router</title><content type='html'>As part of a project to move my noisy Power Mac G4 to the basement (see my blog entery &lt;a href="http://viewfromthefringe.blogspot.com/2006/09/silence-is-golden.html"&gt;Silence is Golden&lt;/a&gt;), I recently bought a LinkSys WRT45GL. This is a 4-port ethernet and WiFi (802.11g) router which is hooked up to my DSL line and provides a firewall and port forwarding for the G4. I know that Kevin Heifner got this same router and immediately &lt;a href="http://heifner.blogspot.com/2006/09/10-steps-to-setup-wrt54gl-with-thibors.html"&gt;installed Thibor's HyperWRT version of the firmware.&lt;/a&gt; I don't use Vonage, and Skype has been working fine for me, so I didn't go so far as to replace the stock firmware (yet).&lt;br /&gt;&lt;br /&gt;This router has the distinction of being built on top of a Linux kernel (interestingly, it was only after someone figured out that the firmware was based upon Linux that the firmware and technical details were released: &lt;a href="http://en.wikipedia.org/wiki/WRT54GL"&gt;the wikipedia for the WRT45GL&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;I don't know if I'll ever replace the stock firmware with one of the open source versions, but it's nice to have the option. I doubt I'll try to do anything about it unless I run into a problem caused by the current firmware that isn't addressed by a stock updated from Cisco and is solved by some open source version with very high stability. I don't mind spending time setting these sorts of things up, but I do &lt;b&gt;not&lt;/b&gt; want to maintain them on a regular basis.&lt;br /&gt;&lt;br /&gt;By the way, I was amazed at the level of detail about the WRT45GL available from wikipedia. There's another blog topic for some other time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-116846152590520625?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/116846152590520625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=116846152590520625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116846152590520625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116846152590520625'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2006/09/open-source-router.html' title='Open Source Router'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-116846160780151369</id><published>2006-09-10T19:39:00.000-05:00</published><updated>2007-01-10T14:40:07.803-06:00</updated><title type='text'>Silence is Golden</title><content type='html'>I recently got a new 20" iMac (yes, recently enough to be saddened that they just came out with a &lt;a href="http://www.apple.com/imac/"&gt;24" model&lt;/a&gt;). Since I got the new machine, I've been working to make a place in the basement where I could move the six year old &lt;a href="http://manuals.info.apple.com/Apple_Support_Area/Manuals/desktops/0341000APMG4SU.PDF"&gt;Power Mac G4&lt;/a&gt;). The G4 is the mail server for the family domain and will eventually host a family web site.&lt;br /&gt;&lt;br /&gt;I finally got that project completed today. It involved running ethernet cables to the attic and basement (solid masonry homes substantially decrease the range of WiFi), putting a shield along the stringers for the basement stairs (to prevent dust, crud, and dropped objects from falling onto the computer), and getting power to the location.&lt;br /&gt;&lt;br /&gt;The actual move of the computer was anti-climactic, which is a good thing. Shut it down, disconnect the wires, move the UPS, move the computer, monitor and keyboard, and hook everything back up. A small hiccup with the outlet having a loose wire was quickly solved and everything 'just worked'. Of course, this was after about two months of on-and-off work to get networking cables run and tested, get the dirt shield created, etc. I ran into a problem with attenuation of the Ethernet signal that required me to install a 5-port ethernet switch in the basement. Just goes to show you that 'cat-5' wiring isn't always really 'cat-5' (whether it was not up to spec to begin with, or whether I crimped something and created a problem will probably never be known).&lt;br /&gt;&lt;br /&gt;With the G4's (notorioiusly loud) cooling fans no longer generating dBs, the study where the iMac is located is now soothingly quiet. Tonight, while I was fighting with the PC the kids use to play computer games, it was noisy again (the PCs fans are even louder than the G4s). But after I shut that off, I sat in front of the iMac and enjoyed the quiet. In fact, it was so quiet I could hear the sound of a cricket outside the study window trying to foster a next generation before the end of summer.&lt;br /&gt;&lt;br /&gt;As for the girl's PC, configuring it to use a WiFi card and to use the printer attached to the iMac was a blunt reminder of just how crappy PC hardware and software really is. But that's a story for another day.&lt;br /&gt;&lt;br /&gt;Now, I have to figure out how to convince my wife to let me buy a 24" iMac and give the 20" iMac to the girls. Then we'd be PC-free once again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-116846160780151369?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/116846160780151369/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=116846160780151369' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116846160780151369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/116846160780151369'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2006/09/silence-is-golden.html' title='Silence is Golden'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8355257.post-109539425733600995</id><published>2004-09-16T23:10:00.000-05:00</published><updated>2004-09-21T16:34:51.400-05:00</updated><title type='text'>Incenting behavior: why IS strives for mediocrity</title><content type='html'>There is a frequent complaint I hear from people working in the Information Systems (IS) departments of corporations: the IS department doesn't seem to care about producing good software, and in some cases doesn't even seem to care about producing software at all.&lt;br /&gt;&lt;br /&gt;At first, this behavior would seem counter-intuitive, since the purpose of an IS department is to see to the computing needs of its company. But if we remind ourselves that people and organizations do what they are incented to do, things become clear. Before you go arguing that there are altruistic individuals in all organizations, including IS, I agree that such individuals exist. But as in all social situations, the number of altruistic individuals is not enough to change the overall behavior of the group.&lt;br /&gt;&lt;br /&gt;To understand why mediocrity ends up as the goal of IS, it's important to realize that the IS department is a cost center, a red line on the balance sheet. Because of this, the IS department can only look good by (a) not screwing up and (b) by lowering it's average cost to get things done. At first, this suggests that IS departments will aim for the best and cheapest solution to any given computing problem. In order to understand why this isn't true, we have to remember that IS departments are run by individuals and individuals do what they are incented to do. We don't care about the individuals who are happy in their current position, as they will simply try to deal with everything thrown at them without losing their jobs. We need to focus our attention on the member of IS who has aspirations.&lt;br /&gt;&lt;br /&gt;A staff member who has aspirations has to achieve uncommon success to get promoted (unless they have family ties or blackmail material; and while both of these occur, they are rare enough that we can essentially ignore them). Such a person can never make the company money (remember, IS is a cost center), so they need to stand out in other ways. They could try to get more done with the same amount of time and money, and frequently individual managers do this. But the truth is that there is a maximum productivity that can be achieved for a given budget. And getting more done in the short run doesn't help. Anyone who bases their promotions on getting more done with less has to reach new heights of achievement each time they seek the next promotion. While some exceptional individuals may succeed this way, there is an upper bound on what a single technical person can achieve, and so a limit to how far they can rise and how quickly. For a manager, there is a path of lesser resistance: grow bigger.&lt;br /&gt;&lt;br /&gt;Despite some obvious flaws in the logic, the accepted wisdom in IS departments is that a manager with a larger team is doing more work and is therefore more capable than a manager with a smaller team. This creates an obvious path to promotion: build your team as large as possible while avoiding major failures. Individuals who do this while dodging the obstacles thrown at them by others seeking promotions (or those who manage to sabotage their competitors) get promoted. The net effect of this is IS departments run by people who believe the best way to succeed is to grow the IS department as large as possible.&lt;br /&gt;&lt;br /&gt;As a result of this "bigger is better" mentality, we end up with mediocrity. If the staff is too large and too unskilled, there will be too many things done poorly and we will end up with major failures. If the staff is too small, no matter how talented, upper management will view us as less important precisely because the group is small. So the real goal of IS mangers with aspirations is to grow as large as possible with minimally qualified staff.&lt;br /&gt;&lt;br /&gt;Of course, a good manager has to make sure they are in fact getting things done that help the company. So there is an additional incentive to make sure that work done furthers the goals of the company. But for an individual trying to get promoted, this goal is secondary and only need be minimally achieved.&lt;br /&gt;&lt;br /&gt;Unfortunately, the only way to solve this problem is to get upper management which actively discourages the view that "bigger is better". This is extraordinarily hard to achieve, since CIOs are people who have risen to the top by succeeding at the "bigger is better" game. And so the wheel goes round and round with no end in sight.&lt;br /&gt;&lt;br /&gt;If you want to work with better than mediocre people, count yourself lucky if you live in a place where software companies exist (since the developers there are producing the lifeblood of the company). If, like most computing professionals, you live in an area where IS departments are the only real option, don't hold your breath waiting for things to change.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8355257-109539425733600995?l=viewfromthefringe.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://viewfromthefringe.blogspot.com/feeds/109539425733600995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8355257&amp;postID=109539425733600995' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/109539425733600995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8355257/posts/default/109539425733600995'/><link rel='alternate' type='text/html' href='http://viewfromthefringe.blogspot.com/2004/09/incenting-behavior-why-is-strives-for.html' title='Incenting behavior: why IS strives for mediocrity'/><author><name>Brian Gilstrap</name><uri>http://www.blogger.com/profile/11799840454645440786</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='26' height='32' src='http://photos10.flickr.com/13616077_25b937abf3_m.jpg'/></author><thr:total>3</thr:total></entry></feed>
