<?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-4286850429573966280</id><updated>2012-02-16T02:18:56.375-06:00</updated><category term='SpringFramework'/><category term='Grails'/><category term='Misc'/><category term='Hardware'/><category term='Patterns'/><category term='Tips'/><category term='JavaScript'/><category term='Java'/><category term='Groovy'/><category term='Programming'/><title type='text'>CoffeaElectronica</title><subtitle type='html'>A smooth well-coded brew.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>16</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-7138438081340218102</id><published>2011-12-19T12:07:00.001-06:00</published><updated>2011-12-19T12:07:46.583-06:00</updated><title type='text'>Searching for a New Format</title><content type='html'>I have not posted in a while. I have been pretty busy in my work and personal life with little time left over to document and blog. Also, I have run into a bit of an issue. I am not sure the blog format really works for me any more. As I have been going back through my old posts trying to bring them over into this new blog I have found quite a few things that I would do differently or not at all. I need to decide whether I want to keep historical and potentially out-dated content around, or to bring it up to date and keep it fresh. I like the idea of having content that may span versions of a product/API, but not when it does not make sense to do so.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have toyed with the idea of a monthly format or just a free always up-to-date ebook or a wiki, but I am not sure which way I want to go at this point. My goal is to have more time, and a new direction ready to go after the first of the year.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Enjoy the holidays!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-7138438081340218102?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/7138438081340218102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2011/12/searching-for-new-format.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/7138438081340218102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/7138438081340218102'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2011/12/searching-for-new-format.html' title='Searching for a New Format'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-2870779898205956043</id><published>2011-10-15T12:07:00.000-05:00</published><updated>2011-10-15T12:07:24.536-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><title type='text'>Home Server v2</title><content type='html'>&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ec8kIN9rjzU/Tpm2ZncJzDI/AAAAAAAACAo/MefgQex832c/s1600/computer-lightning.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-ec8kIN9rjzU/Tpm2ZncJzDI/AAAAAAAACAo/MefgQex832c/s1600/computer-lightning.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;Since my &lt;a href="http://www.coffeaelectronica.com/2010/10/from-junk-box-to-jukebox-in-couple.html"&gt;last home server&lt;/a&gt; was fried by a lightning storm a few months back, I have been serverless, which is an uncomfortable position for a server software developer. I did some good research and decided to try and put together a new box from scratch. My previous server was just a converted "retired" desktop box I had so this would be a fun project.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://lifehacker.com/"&gt;Lifehacker&lt;/a&gt; put out a timely series of articles about how to build your own computer. It was an awesome introduction for someone like myself who had never done more than swap out some RAM or a hard drive. If you are a computer-build noob (like me), I would highly recommend giving the series a read (&lt;a href="http://lifehacker.com/5828747/how-to-build-a-computer-from-scratch-the-complete-guide"&gt;How to Build a Computer from Scrath: A Complete Guide&lt;/a&gt;) if you are thinking of putting together your own box.&lt;br /&gt;&lt;br /&gt;For my home server I had only a few requirements:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Low power consumption - this is running at home, I don't want to overly-inflate my electrical bill.&lt;/li&gt;&lt;li&gt;Low price - this is a home server box for my projects and some helpful apps for my wife, it needs to be cheap.&lt;/li&gt;&lt;li&gt;Room to grow - something with some upgrade room would be nice&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;What I was able to put together fit all those criteria nicely:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16811112219"&gt;LIAN LI PC-A05NA Silver Aluminum ATX Mini Tower Computer Case&lt;/a&gt;&amp;nbsp;-&amp;nbsp;I had originally found a cheaper, smaller case; however, the Lifehacker article recommends some brands over others so I spent a few dollars more and I feel that it was worth it. The case has a lot of room for what I have and may want to add in the future. It also has great cooling and even looks nice.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16817371035"&gt;Antec EarthWatts EA-500D Green 500W ATX12V v2.3 / EPS12V 80 PLUS BRONZE Certified Active PFC Power Supply&lt;/a&gt;&amp;nbsp;-&amp;nbsp;Again, I had a cheaper one picked out but deferred to a Lifehacker-recommended brand. It's also a "green" model with greater power efficiency and enough available power to allow for future growth.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16813121442"&gt;Intel BOXD525MW Intel Atom D525@ 1.8GHz (Dual Core) BGA559 Intel NM10 Mini ITX Motherboard/CPU Combo&lt;/a&gt;&amp;nbsp;-&amp;nbsp;I decided to start with an Intel motherboard with an Atom processor for low power usage and I think it should be enough processor power for my needs (1.8 GHz dual core 64-bit)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16820139033&amp;amp;cm_sp=Pers_InterAlsoBought-_-20-139-033_3_DM-_-13-121-442&amp;amp;nm_mc=AFC-C8Junction&amp;amp;cm_mmc=AFC-C8Junction-_-RSSDailyDeals-_-na-_-na&amp;amp;AID=10521304&amp;amp;PID=4165814&amp;amp;SID=2ixej4ic5pa5"&gt;Kingston ValueRAM 4GB (2 x 2GB) 204-Pin DDR3 SO-DIMM DDR3 1066 (PC3 8500) Laptop Memory Model KVR1066D3SOK2/4GR&lt;/a&gt;&amp;nbsp;- The supported max is 4GB, I bought 4GB.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16822136195&amp;amp;nm_mc=AFC-C8Junction&amp;amp;cm_mmc=AFC-C8Junction-_-RSSDailyDeals-_-na-_-na&amp;amp;AID=10521304&amp;amp;PID=4165814&amp;amp;SID=ppu8hu6z3sg"&gt;Western Digital Caviar Blue WD800AAJS 80GB 7200 RPM 8MB Cache SATA 3.0Gb/s 3.5" Internal Hard Drive -Bare Drive&lt;/a&gt;&amp;nbsp;- Your standard Western Digital in the smallest size that I could find (80GB) without going into something too old. My storage requirements are small. I had originally wanted to get a SSD but that would have blown the cost up too much.&lt;/li&gt;&lt;li&gt;No optical drive, sound card, video card or network card; the latter three all being integrated on the motherboard, the optical drive was not necessary for a server like this.&lt;/li&gt;&lt;/ul&gt;I bought&amp;nbsp;everything&amp;nbsp;from &lt;a href="http://newegg.com/"&gt;NewEgg&lt;/a&gt; for a grand total (with extra warranty and shipping) out of pocket of a hair over &lt;b&gt;$300&lt;/b&gt;. Not bad at all considering everything I looked at "off the rack" was at least $400, and usually had more or less than what I actually wanted. Maybe someone with more experience building systems could do better since there are probably areas I could have cut corners on where I played it safe.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-MGjWl4_K-4M/Tpm8SvFx2II/AAAAAAAACAw/NNwYVYiOGXo/s1600/compbuild1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="228" src="http://2.bp.blogspot.com/-MGjWl4_K-4M/Tpm8SvFx2II/AAAAAAAACAw/NNwYVYiOGXo/s320/compbuild1.jpg" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Boxes of parts.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Once all the parts came in (the following week, damn they ship fast), I put everything together with the help of the Lifehacker articles. With a little help and guidance it was actually a pretty easy and mostly straight-forward process that took about three hours. Once everything was secured and wired together, I fired it up for the first time and&amp;nbsp;was able to get into the BIOS; however, my hard drive was not being&amp;nbsp;recognized. After trying different cables and then trying the drive in my desktop computer the only thing I can figure is that the drive was dead on arrival, great quality control WD! I ran out to BestBuy and bought another of the same drive (well the same brand but larger size) for basically the same price to test my theory and I was correct. Once the new drive was in place everything worked fine. I returned the dead drive to NewEgg and have gotten my refund with no problems.&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-oFPX_9NHuDI/Tpm8duer7sI/AAAAAAAACA4/Mhwn0qmQZ58/s1600/compbuild12.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="180" src="http://4.bp.blogspot.com/-oFPX_9NHuDI/Tpm8duer7sI/AAAAAAAACA4/Mhwn0qmQZ58/s320/compbuild12.jpg" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Secured and wired up.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Next, I moved on to the OS. My server OS of choice is &lt;a href="http://www.ubuntu.com/"&gt;Ubuntu&lt;/a&gt; so I downloaded the 64-bit version of Ubuntu 11.04 and followed the instructions to create a USB installer... I have no optical drive, remember. It was down right simple and it booted from the stick with no problem and installed, as always, like a dream.&lt;br /&gt;&lt;br /&gt;From there I basically followed along with my previous post to install my shared directories and the applications I wanted. It's been nice having a server up and running again.&lt;br /&gt;&lt;br /&gt;I think in a couple years when it's time to upgrade my desktop, I will do a custom build. It's kind of fun and you get exactly what you want.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-2870779898205956043?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/2870779898205956043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2011/10/home-server-v2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/2870779898205956043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/2870779898205956043'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2011/10/home-server-v2.html' title='Home Server v2'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-ec8kIN9rjzU/Tpm2ZncJzDI/AAAAAAAACAo/MefgQex832c/s72-c/computer-lightning.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-1253998814589587518</id><published>2011-09-05T11:31:00.000-05:00</published><updated>2011-09-13T13:08:14.588-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Groovy'/><title type='text'>Determining Overlap Across Multiple Variables</title><content type='html'>&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-4p-FCTX7ypM/TmTqDSe0TCI/AAAAAAAABxU/_NyoOE-UPe0/s1600/simple_venn.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://4.bp.blogspot.com/-4p-FCTX7ypM/TmTqDSe0TCI/AAAAAAAABxU/_NyoOE-UPe0/s200/simple_venn.png" width="195" /&gt;&lt;/a&gt;&lt;/div&gt;I have run into two cases now where I have needed to validate that no overlap exists for data across three axes of comparison, each with potentially large value-spaces containing single values and/or ranges of values. An example of such a comparison could be something like a filter rule used to find People based on demographic data. &lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;Say we have a search rule to find males between 15 and 25 years old who weigh 200 to 300 pounds. You could have something like:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;People A&lt;/b&gt;&lt;br /&gt;Ages: 15-25&lt;br /&gt;Gender: M&lt;br /&gt;Weight: 200-300&lt;br /&gt;&lt;br /&gt;Now consider the case when you have other search rules in the same search and that for the sake of efficiency we want to find overlapping rules and merge them into one (we will only consider the overlap determination here). With two other rules:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;People B&lt;/b&gt;&lt;br /&gt;Ages: 13-20&lt;br /&gt;Gender: M&lt;br /&gt;Weight: 250-400&lt;br /&gt;&lt;br /&gt;&lt;b&gt;People C&lt;/b&gt;&lt;br /&gt;Ages: 8-10&lt;br /&gt;Gender: M&lt;br /&gt;Weight: 200-300&lt;br /&gt;&lt;br /&gt;It's not hard to look at the rules and determine which ones overlap. "People A" overlaps with "People B", while "People C" does not overlap with either of the other two. Programmatically this can be a difficult comparison to do correctly and efficiently. Also, as your number of comparison axes increases, so does the complexity of determining overlap for any given pair of objects.&lt;br /&gt;&lt;br /&gt;What is needed is a simple means of determining the overlap of two objects and the best way I have found to do that is to break each object down into its overlap-comparison components, each of which I will call a "Lobe" from here on out. I chose the term lobe because it is defined as:&lt;br /&gt;&lt;blockquote&gt;"any rounded projection forming part of a larger structure"&lt;/blockquote&gt;Also, terms like element and node are used far too much already in programming.&lt;br /&gt;&lt;br /&gt;When you break each object down into it's lobes, you will have one lobe for Gender, one for Ages, and one for weights. Now you can build your overlap determination based on whether or not each lobe overlaps with its corresponding lobe on the other object. If all of the lobes overlap those of the other object, then the two objects are considered overlapping, otherwise they are not. This allows for a fail-fast comparison since if the comparison of any given lobe fails, you cannot have an overlapping object and no further comparison is necessary.&lt;br /&gt;&lt;br /&gt;Programmatically a lobe can be defined by interface as:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:groovy"&gt;interface Lobe {&lt;br /&gt;    boolean overlaps( Lobe other )&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Each lobe implementation defines what it means to overlap another Lobe of the same type. Using Groovy and some generic logic we can easily come up with a &lt;a href="http://github.com/cjstehno/overlap/blob/master/src/main/groovy/com/stehno/overlap/ComparableLobe.groovy"&gt;ComparableLobe&lt;/a&gt; which is based on single values and ranges of Comparable objects such as numbers, strings and ranges. This allows us to do things like:&lt;br /&gt;&lt;pre class="brush:groovy"&gt;new ComparableLobe( 10..20, 50, 75 )&lt;br /&gt;new ComparableLobe( 'a', 'h'..'j', 'm' )&lt;br /&gt;&lt;br /&gt;lobeA.overlaps( lobeB )&lt;br /&gt;&lt;/pre&gt;which can make the overlap determination very flexible.&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;Note:&lt;/b&gt; All of the source code from this article is available in my &lt;a href="http://github.com/"&gt;GitHub&lt;/a&gt; repo called &lt;a href="http://github.com/cjstehno/overlap"&gt;Overlap&lt;/a&gt;.&lt;/blockquote&gt;Now we can create Lobes for each of the overlap-comparable parts of our People, using "People A" as an example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:groovy"&gt;def genderLobe = new ComparableLobe( 'M' )&lt;br /&gt;def agesLobe = new ComparableLobe( 15..25 )&lt;br /&gt;def weightsLobe = new ComparableLobe( 200..300 )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The next thing we need is a way of comparing these lobes in a simple and repeatable manner and that's where the &lt;a href="http://github.com/cjstehno/overlap/blob/master/src/main/groovy/com/stehno/overlap/Overlappable.groovy"&gt;Overlappable&lt;/a&gt; interface comes in:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:groovy"&gt;interface Overlappable {&lt;br /&gt;    boolean overlaps ( Overlappable other )&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Overlappable interface defines an object that can be compared for overlap. The required method is basically the same as that of the Lobe; however, this interface is for the parent object itself. By providing an abstract implementation of this interface we have a nice clean way of providing overlap detection functionality for an object type. The &lt;a href="http://github.com/cjstehno/overlap/blob/master/src/main/groovy/com/stehno/overlap/AbstractOverlappable.groovy"&gt;AbstractOverlappable&lt;/a&gt; provides an implementation of the overlaps() method using the OverlapBuilder, which we will talk about in a minute. What the abstract class also provides is a simple way of emitting the Lobes for the given object in a simple manner, as an array. You can use this with our example above to create a simple Overlappable Person object:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:groovy"&gt;class People extends AbstractOverlappable {&lt;br /&gt;    String gender&lt;br /&gt;    IntRange ages&lt;br /&gt;    IntRange weights&lt;br /&gt;	&lt;br /&gt;    @Override&lt;br /&gt;    Lobe[] lobes() {&lt;br /&gt;        [&lt;br /&gt;            new ComparableLobe(gender),&amp;nbsp;&lt;br /&gt;            new ComparableLobe(ages),&amp;nbsp;&lt;br /&gt;            new ComparableLobe(weights)&amp;nbsp;&lt;br /&gt;        ]&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The overlaps() method uses the provided Lobes to popualte an &lt;a href="http://github.com/cjstehno/overlap/blob/master/src/main/groovy/com/stehno/overlap/OverlapBuilder.groovy"&gt;OverlapBuilder&lt;/a&gt;, which is basically a helper class for performing the actual Lobe-to-Lobe comparison of a given set of Lobes. The OverlapBuilder is inspired by the builder in the Apache Commons - Lang API, such as EqualsBuilder and HashCodeBuilder. You create an instance and append your Lobes to it, then execute the overlap() method to perform the comparison.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:groovy"&gt;new OverlapBuilder()&lt;br /&gt;    .appendLobe(new ComparableLobe(1..10), new ComparableLobe(5..15))&lt;br /&gt;    .overlap()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It also provides an append method for simple comparable cases:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:groovy"&gt;overlapBuilder.appendComparable( 20..25, 15..30 )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;which just wraps each value in a ComparableLobe.&amp;nbsp;Now, given a list of People objects, you can determine if any of them overlap any of the others simply by iterating over the list and comparing each element with the others:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:groovy"&gt;def list = [&lt;br /&gt;    new Person( gender:'M', ages:15..25, weights:200..300 ),&lt;br /&gt;    new Person( gender:'M', ages:13..20, weights:250..400 ),&lt;br /&gt;    new Person( gender:'M', ages:8..10, weights:200..300 )&lt;br /&gt;]&lt;br /&gt;&lt;br /&gt;list[0..-2].eachWithIndex { self, idx-&amp;gt;&lt;br /&gt;    list[(idx+1)..(-1)].each { other-&amp;gt;&lt;br /&gt;        if( self.overlaps( other ) ){&lt;br /&gt;            println "$self overlaps $other"&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As a final little bonus feature, I have added a ComparableLobe.ANY object which denotes a Lobe that will always be considered to overlap, no matter what the other value is.&lt;br /&gt;&lt;br /&gt;This kind of multi-variable overlap-checking scenario may not come up all that often, but when it does, it can give you a lot of grief, especially if the fields being compared are numerous and complex. I have had to create a couple additional Lobe implementations but am also using the ComparableLobe quite a bit. This library has been a real time-saver. And, once broken down into component parts, the overlap determination becomes simple to implement and test, which makes it much more stable over time.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-1253998814589587518?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/1253998814589587518/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2011/09/determining-overlap-across-multiple.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/1253998814589587518'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/1253998814589587518'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2011/09/determining-overlap-across-multiple.html' title='Determining Overlap Across Multiple Variables'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-4p-FCTX7ypM/TmTqDSe0TCI/AAAAAAAABxU/_NyoOE-UPe0/s72-c/simple_venn.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-3034978427897784746</id><published>2011-08-01T08:33:00.001-05:00</published><updated>2011-08-01T08:33:58.651-05:00</updated><title type='text'>I Want My GoogleTV...</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://t0.gstatic.com/images?q=tbn:ANd9GcQ9P6DBY9JZERFPHAvxb6ZxeRVdUYwE9OiVOIklgxGR1zpUwXCW" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://t0.gstatic.com/images?q=tbn:ANd9GcQ9P6DBY9JZERFPHAvxb6ZxeRVdUYwE9OiVOIklgxGR1zpUwXCW" /&gt;&lt;/a&gt;&lt;/div&gt;...to be usable.&lt;br /&gt;&lt;br /&gt;A few months ago my wife and I purchased a Logitech Revue (Google TV) mainly for my wife, since she has medical issues that make it difficult for her to use a desktop or laptop computer.&lt;br /&gt;&lt;br /&gt;Overall, it's a great product. It provides a much better interface for watching web video content. You can surf the web with a full version of Chrome (with flash) and you can run various apps, which soon will be augmented by the Android Market (supposedly coming this year).&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The downside comes in when you start finding the holes in the product. My wife wants to be able to view our photos and then share them on facebook or email them. We have our photos on a DLNA-enabled NAS so it should be easy. The Media Player app was able to see the server but was never able to get the content. Unable to find any error information or details we decided to try just putting our photos on PicasaWeb and then viewing them through the Gallery app.&lt;br /&gt;&lt;br /&gt;The Gallery App worked for viewing and it did it quite nicely, very similar to the Gallery app found on Android phones. The problem we had was that when you try to "share" a photo, you have only one option, Twitter. My wife is not on Twitter, has no friends on Twitter and does not really want to sign up. This was my first WTF moment with GoogleTV. The second was when I realized that the depth of the problem. There is no Facebook app, there is no GMail app, there are very few apps at all. As it stands, it is a very anti-social product.&lt;br /&gt;&lt;br /&gt;Putting that problem aside, I thought well, she could share photos with Facebook via PicasaWeb. Wrong. PicasaWeb only shares via email.&lt;br /&gt;&lt;br /&gt;Okay, Facebook might have a work-around for the problem. We tried to share a photo from Facebook (web) and they have no "Upload photo via URL" so the best she could do is share a link to the photo on PicasaWeb.&lt;br /&gt;&lt;br /&gt;My wife, who is not a techie, found this all very frustrating and annoying, while I was very disappointed in Google's release of an unfinished product. These Google TV units have been out for a couple years now, they really should have better apps and more social interaction available by default. I know that Google keeps stating that the Android Market will be available soon, but they need to start giving more details before the users give up and sell the unit.&lt;br /&gt;&lt;br /&gt;My wife's other main complaint was that it seems to expect too much from the user; basically, if she didn't have me she would be lost. There are a few tutorials that she has found, but she had to dig around for them.&lt;br /&gt;&lt;br /&gt;Overall, it's a great product and we love it and the entertainment that it has opened up for us, but there are some really frustrating points that need to be addressed. I will say though, that if the Android Market update does not address these issues and start polishing up the system, Google TV will be nothing more than a niche product like its WebTV ancestor, and that would be really sad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-3034978427897784746?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/3034978427897784746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2011/08/i-want-my-googletv.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/3034978427897784746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/3034978427897784746'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2011/08/i-want-my-googletv.html' title='I Want My GoogleTV...'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-3082328155882062365</id><published>2011-07-09T09:05:00.001-05:00</published><updated>2011-07-09T09:06:37.501-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Groovy'/><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>A Groovy Year</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-gx3R4WhcAQw/ThhUU0KtsjI/AAAAAAAABaA/Uc6Rjl7wWcE/s1600/groovy-grails-logo.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-gx3R4WhcAQw/ThhUU0KtsjI/AAAAAAAABaA/Uc6Rjl7wWcE/s1600/groovy-grails-logo.png" /&gt;&lt;/a&gt;&lt;/div&gt;It's been just over a year now that I have been working full time with &lt;a href="http://groovy.codehaus.org/"&gt;Groovy&lt;/a&gt; and &lt;a href="http://grails.com/"&gt;Grails&lt;/a&gt;&amp;nbsp;so I figured it was a good time for a retrospective. I will go over general thoughts and lessons learned here and then I plan on covering some of the topics in greater detail in separate postings.&lt;br /&gt;&lt;br /&gt;In the company I work for we do all of our web application development work using Grails, which is all done using Groovy. We also have quite a few external projects that feed into our Grails applications; these are written mostly in Groovy and Java. Ask three different developers in the company how they feel about our technology choices and you will get three different answers. Personally, these technologies are perfectly suited for what we are doing, but there are always pros and cons with any environment and that's what I want to address here.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Groovy&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Groovy is a great language. Its flexible, expressive and concise. It really does wipe away all the development fluff and leave you to do actual coding work.&lt;br /&gt;&lt;br /&gt;The problem with Groovy is that it is deceptively simple and at times too "magic" for under-skilled developers.&amp;nbsp;It has no real compile time checking, limited analysis tools and IDE support can be spotty depending on which one you are working with. In the hands of a less-seasoned&amp;nbsp;developer Groovy can lead to a free-for-all of odd constructs and bad coding practices that can be difficult to refactor away.&amp;nbsp;Refactoring in Groovy can be a painful undertaking due to its dynamic nature and limited IDE support.&lt;br /&gt;&lt;br /&gt;Some suggestions I have for a team starting out with Groovy:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Unit testing is a requirement as are higher coverage targets, and I mean like 90-100% coverage. Since you don't have any compile-time checking and its very easy to have hidden runtime issues with Groovy, you need to enforce a high level of unit testing and unit test coverage.&lt;/li&gt;&lt;li&gt;Coding standards should be determined and enforced. Just because Groovy will let you do something doesn't mean you should do it.&lt;/li&gt;&lt;li&gt;Code reviews. With Groovy, especially starting out, you should do code reviews on pretty much everything you check in. Code reviews are always a good practice, but even more so when starting out with a dynamic language. The more eyes looking at the code, the better. This also helps to enforce coding standards.&lt;/li&gt;&lt;li&gt;Embrace Groovy, but don't get crazy about it. Groovy has some great time-saving and keystroke-saving features... use them, but remember what Uncle Ben said, "with great power comes great&amp;nbsp;responsibility".&lt;/li&gt;&lt;li&gt;Use a code analysis tool like &lt;a href="http://codenarc.sourceforge.net/"&gt;CodeNarc&lt;/a&gt; on your code to point out standards violations and possible bugs.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;Grails&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Grails is one of those life-changing frameworks that come along and change the way you do everything. I have done web application development with Servlet/JSP, Struts, Spring MVC (xml and annotations) and I can say that once you give Grails a try, it's hard to turn to anything else. It does so much of the boilerplate work for you that you are left to write the actual working code rather than all the bindings and plumbing. It has a strong plugin system and an active plugin community so it's easy to find what you need or to build it yourself.&lt;br /&gt;&lt;br /&gt;The biggest downside I have seen with Grails, is that it seems overly difficult to pull your domain objects out into separate projects rather than having them in the Grails project itself.&lt;br /&gt;&lt;br /&gt;My Grails recommendations:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Break your application up into projects appropriately. Just because you are using Grails doesn't mean everything has to be in your Grails project. GORM does seem to have issues outside of Grails but there are other things you can externalize and then provide as maven dependencies or as Grails plugins.&lt;/li&gt;&lt;li&gt;Follow Grails recommendations. If you do things the "Grails way" you will have a more simple experience and get the most out of grails.&lt;/li&gt;&lt;li&gt;Keep your Grails version as current as possible. I am not saying you have to live on the cutting edge of each release, but as the Grails versions progress, so should the version that you use. If you get too far behind, it will only be more painful than doing it incrementally.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;b&gt;IDE Support&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;IDE support has been slow coming and generally spotty. During this past year I have tried all of the "big three", &lt;a href="http://eclipse.org/"&gt;Eclipse&lt;/a&gt;, &lt;a href="http://netbeans.org/"&gt;NetBeans&lt;/a&gt; and &lt;a href="http://jetbrains.com/idea"&gt;IntelliJ&lt;/a&gt;&amp;nbsp;with varried results.&lt;br /&gt;&lt;br /&gt;I started with the IDE I had been using for years, Eclipse, which I quickly found out had terrible Groovy support and no Grails support. I switched to SpringSource Tool Suite (Eclipse + Spring), which had better Groovy support and some Grails support. I did run into a problem with our projects since most of our Grails projects at work are Grails 1.1.1, which is not well supported by the tool and as of the most current version of STS has no support.&lt;br /&gt;&lt;br /&gt;I decided to give NetBeans a try. It has good Groovy and Grails support, even with our outdated version of Grails. I used NetBeans for a while but was not very happy with its other features. It's SVN support seemed lacking and some of its other editors were not up to the tasks I was working on. Things like running our Grails projects, unit tests and debugging stuff in the IDE did not really work.&lt;br /&gt;&lt;br /&gt;I decided to break down and give IntelliJ a try. It has great Groovy and Grails support. I am able to debug internally and externally and run unit tests in the IDE. It has smart useful code suggestions and good Groovy DSL support. I have made IntelliJ my IDE of choice, though it does have a price tag associated with it.&lt;br /&gt;&lt;br /&gt;My IDE recommendation would be IntelliJ if you or your company is willing to pay for it, or NetBeans if you are on your own, or have no IDE budget.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Groovy and Grails are a great combination. You just have to realize that as a developer you still have to pay attention to what you are doing and how you are doing it. As far as coding goes, this past year with these tools has been the most enjoyable that I have had in a long time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-3082328155882062365?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/3082328155882062365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2011/07/groovy-year.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/3082328155882062365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/3082328155882062365'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2011/07/groovy-year.html' title='A Groovy Year'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-gx3R4WhcAQw/ThhUU0KtsjI/AAAAAAAABaA/Uc6Rjl7wWcE/s72-c/groovy-grails-logo.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-678968094546255773</id><published>2011-07-07T19:40:00.000-05:00</published><updated>2011-07-07T19:40:45.373-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Misc'/><title type='text'>A New (Free) Home</title><content type='html'>If you are one of the few followers of CoffeaElectronica, you will be happy to know that I have found a new home for my blog. I had looked into Blogger years ago when I was first setting up the site, but it seemed very sketchy, like it might not be around much longer. But, it's still here and I think it will be here for a while to come.&lt;br /&gt;&lt;br /&gt;I am still considering what to do about my older posts. You can find the raw source code for the posts at&amp;nbsp;&lt;a href="https://github.com/cjstehno/cjstehno.github.com"&gt;https://github.com/cjstehno/cjstehno.github.com&lt;/a&gt;, or you can view them (thought in a not so pretty format) at&amp;nbsp;&lt;a href="http://cjstehno.github.com/"&gt;http://cjstehno.github.com&lt;/a&gt;. I am considering importing them into this blog, thought I am also thinking about taking a new approach and maybe rewriting/updating some of my older posts or providing them in a more updated format.&lt;br /&gt;&lt;br /&gt;Time and level of interest will help decide.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-678968094546255773?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/678968094546255773/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2011/07/new-free-home.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/678968094546255773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/678968094546255773'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2011/07/new-free-home.html' title='A New (Free) Home'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-2761292272560334026</id><published>2010-10-17T08:41:00.000-05:00</published><updated>2011-09-23T09:17:10.818-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hardware'/><category scheme='http://www.blogger.com/atom/ns#' term='Misc'/><title type='text'>From Junk Box to Jukebox in a Couple Hours</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-UFmj6CxSqSc/TnyNTB1bIvI/AAAAAAAAB7w/dknz_DldUKk/s1600/jukebox.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-UFmj6CxSqSc/TnyNTB1bIvI/AAAAAAAAB7w/dknz_DldUKk/s1600/jukebox.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;I finally got tired of griping about not having enough space on my phone to store all of my music, and not wanting to pay for one of the music cloud storage services... so I decided to bite the bullet and setup my own using &lt;a href="http://ubuntu.com/"&gt;Ubuntu Server&lt;/a&gt;, &lt;a href="http://subsonic.org/"&gt;SubSonic Music Streamer&lt;/a&gt; and its Android app. It only took a couple hours, most of which was baby-sitting installations, and now I am able to listen to any/all of my music whenever and wherever I want; it's quite nice.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;The steps are pretty straight forward, but going into this process I must make it clear that I am not an expert network/server administrator, I am a developer, therefore if this process causes you or your equipment any harm in any way, sorry, but it's not my fault. Continue at your own risk.&lt;br /&gt;&lt;br /&gt;First, you need a computer to work with. I had an old Dell desktop that is about four or five years old and it is working nicely. Hook up a keyboard, mouse and monitor to the box... if it's an old desktop box you should have all the connections you need. You will also need to connect the box to your network.&lt;br /&gt;&lt;br /&gt;It almost goes without saying that you will need internet access of at least DSL, but really I would not do this with anything less than a high-speed cable internet connection.&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/-h5QLDp_huks/TnyNz3Jo4lI/AAAAAAAAB70/4J5WNvvQgtM/s1600/Ubuntu-logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-h5QLDp_huks/TnyNz3Jo4lI/AAAAAAAAB70/4J5WNvvQgtM/s200/Ubuntu-logo.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;Download &lt;a href="http://www.ubuntu.com/server"&gt;Ubuntu 10.10 Server&lt;/a&gt; (or whatever the most current version is when you read this). Make sure that you are downloading the server version, &lt;b&gt;not&lt;/b&gt; the desktop. Also make sure that you download the appropriate version for your system, 32- or 64-bit. I had to use 32-bit since it was an old desktop box.&lt;br /&gt;&lt;br /&gt;Burn the .iso file you downloaded to a CD (I recommend &lt;a href="http://www.imgburn.com/"&gt;ImgBurn&lt;/a&gt; if you don't have a favorite image buring application).&lt;br /&gt;&lt;br /&gt;Once you have the CD created, pop it into the box you are building and boot from it. You may have to fiddle with your boot order in your bios, but the CD is bootable.Once the Ubuntu installation menu comes up, just follow the instructions.&lt;br /&gt;&lt;br /&gt;A couple items to be aware of: You will want to enable the automatic security updates so that you don't have to bother with it yourself. Also, you will come to a server installation screen (after the restart I think)... at this point I selected LAMP server, Tomcat server and OpenSSH server. The SSH server is required for this, though the others are not; I plan on adding more to this box so I wanted to have the servers ready to go.&lt;br /&gt;&lt;br /&gt;Once the installation is done, disconnect the keyboard, mouse and monitor, but leave the box running. You now have a nice clean Linux web server to play with.I recommend giving the new server box a dedicated IP address on your network so that you don't need to keep track of it. This is gererally very specific to your router, so I can't really go into it here.&lt;br /&gt;&lt;br /&gt;From your desktop box, laptop, whatever, connect to the new server using your favorite SSH client (for windows you can use&amp;nbsp;&lt;a href="http://cygwin.com/"&gt;Cygwin&lt;/a&gt; or &lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/"&gt;Putty&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;All of my music is on a NAS on my local network, so I had to create a mount of the music directory on the server box. There is a nice wiki article about how to do this, &lt;a href="https://wiki.ubuntu.com/MountWindowsSharesPermanently"&gt;Mount Windows Shares Permanently&lt;/a&gt; so that's easy enough.&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/-l1J-rqH1650/TnyPHcmkawI/AAAAAAAAB74/SPtkmjcaTCQ/s1600/subsonic.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-l1J-rqH1650/TnyPHcmkawI/AAAAAAAAB74/SPtkmjcaTCQ/s1600/subsonic.png" /&gt;&lt;/a&gt;&lt;/div&gt;Now download the deb installer file from &lt;a href="http://www.subsonic.org/"&gt;SubSonic&lt;/a&gt; and follow the installation instructions. There are also some additional packages they recommend installing. I did.I eventually want to have SubSonic use the default servers on the box, but this installation method seemed to be the fastest one to get a server up and running.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Once you have SubSonic installed you will need to create a directory for your playlists, "/var/playlists" by default.Now that the server is running, go ahead and login and change the admin password and set the music and playlist locations. I created a second user to user for myself that did not have admin permissions. Make sure that you can see a list of your music artists down the left hand side and you are ready to go... at least for access on your own network.&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/-1ZljIp3T29s/TnyPVLDJ44I/AAAAAAAAB78/RaGjVjzLpJw/s1600/lifehacker_icon.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-1ZljIp3T29s/TnyPVLDJ44I/AAAAAAAAB78/RaGjVjzLpJw/s200/lifehacker_icon.jpg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;In order to have access to SubSonic from the outside world, you need to setup port forwarding on your router to map one of the external ports to the server and port that your SubSonic instance is running on. Again, this is router-specific, but &lt;a href="http://lifehacker.com/"&gt;Lifehacker&lt;/a&gt; has a good overview in the article "&lt;a href="http://lifehacker.com/127276/geek-to-live--how-to-access-a-home-server-behind-a-routerfirewall"&gt;How to Access a Home Server Behind a Router Firewall&lt;/a&gt;".&lt;br /&gt;&lt;br /&gt;Once you have access from the outside world, you can install the Android or iPhone apps for SubSonic and play your music while you are away from home. The Android app is quite good. I created two profiles, one for home when I can use my local wireless network, and another for access when I am away from my network... helps to keep the bandwidth down. Using the phone is also a nice way to test out the external interface. Turn off your wifi antenna and connect via 3G.&lt;br /&gt;&lt;br /&gt;One concern I have about running my own server is power consumption. The box I am using was a desktop box and not really made to conserve energy. I am looking into ways of setting up smart power usage, sleeping, etc, but that will come in another posting. For now I am going to test out the actual power consuption using one of those &lt;a href="http://www.thinkgeek.com/gadgets/travelpower/7657/"&gt;Killawatt&lt;/a&gt; plug-in wattage meters so that I can see what I am doing to my monthly power bill.One thought I had is that if the power consumption is too high, I might look into building a really low-power box with an atom processor and solid-state drive or something else really bare bones and cheap.I have a few other projects in mind for this now that I have a server running, though I don't think I will be moving my blog hosting there any time soon, but you never know.&lt;br /&gt;&lt;br /&gt;If any of this is too vague, please let me know and I can add more detail. This is intended for users with minimal Linux, networking, and hardware experience, but sometimes I take more for granted than I realize.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Good luck and have fun!&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Follow-up&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;After about six days for power monitoring, the server used 6.38 kilowatt-hours of power. With a little groovy calculating:&lt;br /&gt;&lt;pre class="brush:groovy"&gt;def kwh = 6.38, kwhcost = 0.12&lt;br /&gt;def duration /* days */ = (((time('10/22/2010 06:54') - time('10/16/2010 10:10')) / 1000) / 3600) / 24&lt;br /&gt;def costPerDay = (kwh * kwhcost)/duration&lt;br /&gt;def costPerMonth = costPerDay * 30&lt;br /&gt;def time( d ){ new java.text.SimpleDateFormat('MM/dd/yyyy HH:mm').parse( d ).getTime()}&lt;br /&gt;def money( m ){ new java.text.DecimalFormat('$0.00').format( m )}&lt;br /&gt;println "${money(costPerDay)}/day ==&amp;gt; ${money(costPerMonth)}/month"&lt;br /&gt;&lt;/pre&gt;I found that the server uses roughly $0.13/day or $3.93/month on average based on the time duration I tested in which the server was under normal usage. Obviously, this number is very dependent on the system you build your server with. Given some of the new Atom-based boxes that are available, you could probably really cut that cost down even more.If you consider that most hosting with any useful functionality is at least $3-5 per month, and a lot of the cloud-based services that are coming out are in the range of $5-10 per month... $3-5 per month for your own server with unlimited possibilities is not bad at all.&amp;nbsp;Also there is one cost-cutting measure you can do with your own server that you cannot do with hosting... turn it off.&lt;br /&gt;&lt;br /&gt;Finally, I also found a useful tool for working with the server. If you have an Android phone you can install &lt;a href="http://code.google.com/p/connectbot/"&gt;ConnectBot&lt;/a&gt; which gives you SSH shell access to your server. I don't have my SSH port forwarded from the router so this only works inside my home network via wireless, but ConnectBot makes it very easy to check on the status of the box, do a shutdown or restart the web server without having to fire up your desktop box.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-2761292272560334026?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/2761292272560334026/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2010/10/from-junk-box-to-jukebox-in-couple.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/2761292272560334026'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/2761292272560334026'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2010/10/from-junk-box-to-jukebox-in-couple.html' title='From Junk Box to Jukebox in a Couple Hours'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-UFmj6CxSqSc/TnyNTB1bIvI/AAAAAAAAB7w/dknz_DldUKk/s72-c/jukebox.jpg' height='72' width='72'/><thr:total>0</thr:total><georss:featurename>Plano, TX, USA</georss:featurename><georss:point>33.0198431 -96.6988856</georss:point><georss:box>32.913332600000004 -96.8568141 33.1263536 -96.5409571</georss:box></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-3986637921225674425</id><published>2008-05-31T10:21:00.000-05:00</published><updated>2011-10-02T10:41:26.137-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Rendering Calendars</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-KZmHznvYzjI/ToiFqbFTRCI/AAAAAAAAB-o/diz_3swy2yg/s1600/calendar_image.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-KZmHznvYzjI/ToiFqbFTRCI/AAAAAAAAB-o/diz_3swy2yg/s200/calendar_image.png" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;I am poking around with an idea I had to generate desktop background images with little calendars embedded in them and I realized that there is no really easy method of rendering your standard square text calendar display. I decided to play around with it a bit. The basic algorithm to generate the weeks and their associated days is nothing too complicated:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Figure out what day of the week the first day of the month falls on.&lt;/li&gt;&lt;li&gt;Subtract from that the value for Sunday (the start of the week), which assumes that the day of the week field values are sequential (which they are).&lt;/li&gt;&lt;li&gt;Iterate through the day of the month keeping track of the week breaks.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;I created a factory class, &lt;tt&gt;MonthFactory&lt;/tt&gt; and a couple helper model objects, &lt;tt&gt;Month&lt;/tt&gt; and &lt;tt&gt;Week&lt;/tt&gt; to make things a little easier. &lt;tt&gt;Month&lt;/tt&gt; and &lt;tt&gt;Week&lt;/tt&gt; are pretty simple data objects used to store the month and week data in a meaningful structure.&lt;br /&gt;&lt;pre class="brush:java"&gt;public class Week {&lt;br /&gt;    private final String[] days = new String[7];&lt;br /&gt;    private int index = 0;&lt;br /&gt;&lt;br /&gt;    public Iterable&amp;lt;string&amp;gt; days(){&lt;br /&gt;        return Arrays.asList(days);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    boolean append(final String day){&lt;br /&gt;        days[index++] = day;&lt;br /&gt;        return index != 7;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void padding(final int size){&lt;br /&gt;        index = size;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public String toString(){&lt;br /&gt;        return Arrays.toString(days);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;I used strings to store the day values, you could also use ints.&lt;br /&gt;&lt;pre class="brush:java"&gt;public class Month {&lt;br /&gt;    private final List&amp;lt;week&amp;gt; weeks = new LinkedList&amp;lt;week&amp;gt;();&lt;br /&gt;    private final String name;&lt;br /&gt;    private final int year;&lt;br /&gt;    &lt;br /&gt;    Month(final String name, final int year){&lt;br /&gt;        this.name = name;&lt;br /&gt;        this.year = year;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public Iterable&amp;lt;week&amp;gt; weeks(){&lt;br /&gt;        return weeks;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public String getName(){return name;}&lt;br /&gt;   &lt;br /&gt;    public int getYear(){return year;}&lt;br /&gt;    &lt;br /&gt;    void append(final Week week){&lt;br /&gt;        weeks.add(week);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    @Override&lt;br /&gt;    public String toString() {&lt;br /&gt;        return "{" + name + " " + year + ": weeks=" + weeks.toString();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;Both of these classes have very limited public interfaces. They exist in the same package as the &lt;tt&gt;MonthFactory&lt;/tt&gt; class which is used tobuild them. External client classes should really only be accessing them as read-only. The &lt;tt&gt;MonthFactory&lt;/tt&gt; is also pretty simple, but it does contain the algorithm I mentioned earlier so it is the meat of the whole example:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class MonthFactory {&lt;br /&gt;    &lt;br /&gt;    public static Month create(final int calendarMonth, final int year) throws Exception {&lt;br /&gt;        validate(calendarMonth,year);&lt;br /&gt;    &lt;br /&gt;        final Calendar cal = Calendar.getInstance();&lt;br /&gt;        cal.set(Calendar.MONTH, calendarMonth);&lt;br /&gt;        cal.set(Calendar.DAY_OF_MONTH, 1);&lt;br /&gt;        cal.set(Calendar.YEAR, year);&lt;br /&gt;  &lt;br /&gt;        final int daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);&lt;br /&gt;        final String name = cal.getDisplayName(Calendar.MONTH,Calendar.LONG,Locale.getDefault());&lt;br /&gt;        final Month month = new Month(name,year);&lt;br /&gt;   &lt;br /&gt;        Week week = new Week();&lt;br /&gt;        week.padding(cal.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY);&lt;br /&gt;    &lt;br /&gt;        for(int i=1; i&amp;lt;=daysInMonth; i++){&lt;br /&gt;            if(!week.append(String.valueOf(i))){&lt;br /&gt;                month.append(week);&lt;br /&gt;                week = new Week();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        month.append(week);&lt;br /&gt;  &lt;br /&gt;        return month;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    private static void validate(final int calMon,final int year){&lt;br /&gt;        if(calMon &amp;lt; Calendar.JANUARY || calMon &amp;gt; Calendar.DECEMBER){&lt;br /&gt;            throw new IllegalArgumentException("Invalid Calendar Month specified: " + calMon);&lt;br /&gt;        }&lt;br /&gt;        if(year &amp;lt; 0){&lt;br /&gt;            throw new IllegalArgumentException("Year must be non-negative: " + year);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;I even threw in some simple input validation for free. Now for a simple use of this code I created a &lt;tt&gt;TextCalendar&lt;/tt&gt; class that simply generates a text calendar which will look right if it's rendered in a fixed-width font.&lt;br /&gt;&lt;pre class="brush:java"&gt;public class TextCalendar {&lt;br /&gt;    &lt;br /&gt;    public static void main(final String[] args) throws Exception {&lt;br /&gt;        final Month month = MonthFactory.create(Calendar.JULY,2008);&lt;br /&gt;            &lt;br /&gt;        final StringBuilder str = new StringBuilder();&lt;br /&gt;    &lt;br /&gt;        final String header = month.getName() + " " + String.valueOf(month.getYear());&lt;br /&gt;        str.append(padding(10 - header.length()/2,' ')).append(header).append('\n');&lt;br /&gt;    &lt;br /&gt;        for(final Week week : month.weeks()){&lt;br /&gt;            for(final String day : week.days()){&lt;br /&gt;                if(day == null){&lt;br /&gt;                    str.append("  ");&lt;br /&gt;                } else {&lt;br /&gt;                    str.append(day.length() == 2 ? day : " " + day);&lt;br /&gt;                }&lt;br /&gt;                str.append(" ");&lt;br /&gt;            }&lt;br /&gt;            str.append('\n');&lt;br /&gt;        }&lt;br /&gt;   &lt;br /&gt;        System.out.println(str.toString());&lt;br /&gt; &lt;br /&gt;    }&lt;br /&gt;  &lt;br /&gt;    private static String padding(final int size, final char val){&lt;br /&gt;        final char[] pad = new char[size];&lt;br /&gt;        Arrays.fill(pad, val);&lt;br /&gt;        return new String(pad);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;This just serves to show how easy this makes calendar rendering. Upon running this little application you will get the following nicely formatted calendar (I even centered the header):&lt;br /&gt;&lt;pre&gt;          July 2008&lt;br /&gt;           1  2  3  4  5 &lt;br /&gt;     6  7  8  9 10 11 12 &lt;br /&gt;    13 14 15 16 17 18 19 &lt;br /&gt;    20 21 22 23 24 25 26 &lt;br /&gt;    27 28 29 30 31       &lt;br /&gt;&lt;/pre&gt;If there are existing libraries to do this, I would love to hear about them. I could not find anything. Obviously this example corresponds to the standard US display of the Gregorian Calendar. I would think that it could be made to work for other calendars as well, but I have never actually seen any other calendars displayed... I guess I need to travel the world more.I provide these as examples only; there may be better ways to accomplish this and there are definitely code modifications and improvements that could be made. I am not currently using this code anywhere... it was just an experiment. Use at your own risk. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-3986637921225674425?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/3986637921225674425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2008/05/rendering-calendars.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/3986637921225674425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/3986637921225674425'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2008/05/rendering-calendars.html' title='Rendering Calendars'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-KZmHznvYzjI/ToiFqbFTRCI/AAAAAAAAB-o/diz_3swy2yg/s72-c/calendar_image.png' height='72' width='72'/><thr:total>0</thr:total><georss:featurename>Plano, TX, USA</georss:featurename><georss:point>33.0198431 -96.6988856</georss:point><georss:box>32.913332600000004 -96.8568141 33.1263536 -96.5409571</georss:box></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-2529283645048015675</id><published>2007-11-01T12:39:00.000-05:00</published><updated>2011-11-17T12:42:59.871-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='SpringFramework'/><title type='text'>Custom Spring Editors</title><content type='html'>I had the need recently to inject an array of strings (&lt;tt&gt;java.lang.String[]&lt;/tt&gt;) into a bean property and I was curious about whether or not I could inject the strings as comma-separated values (CSV). With a little poking around in the &lt;a href="http://springframework.org/"&gt;Spring API&lt;/a&gt; I found that the supporting &lt;tt&gt;PropertyEditor&lt;/tt&gt; is already there, but not configured by default. My next question was about how you go about configuring custom property editors.Configuring custom property editors is quite easy, you add a &lt;tt&gt;CustomEditorConfigurer&lt;/tt&gt; bean to your context which will register itself with the bean factory at load-time. By mapping your custom editors to the &lt;tt&gt;CustomEditorConfigurer&lt;/tt&gt;, you register them with the enclosing bean factory... pretty simple:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;bean id="customEditorConfigurer" &lt;br /&gt;         class="org.springframework.beans.factory.config.CustomEditorConfigurer"&amp;gt;&lt;br /&gt;    &amp;lt;property name="customEditors"&amp;gt;&lt;br /&gt;        &amp;lt;map&amp;gt;&lt;br /&gt;            &amp;lt;entry key="java.lang.String[]"&amp;gt;&lt;br /&gt;                &amp;lt;bean class="org.springframework.beans.propertyeditors.StringArrayPropertyEditor"&amp;gt;&lt;br /&gt;                    &amp;lt;constructor-arg value=":" /&amp;gt;&lt;br /&gt;                &amp;lt;/bean&amp;gt;&lt;br /&gt;            &amp;lt;/entry&amp;gt;&lt;br /&gt;        &amp;lt;/map&amp;gt;&lt;br /&gt;    &amp;lt;/property&amp;gt;&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;Note that the value of the entry key attribute is the full class name of the property type to be handled by the editor. Only one editor can be registered for a given type. The &lt;tt&gt;StringArrayPropertyEditor&lt;/tt&gt; is available with the core Spring API and it will convert a delimited string into a string array. The delimiter is configurable as a constructor argument; it defaults to comma, but I have overridden it here to use a colon in this case as an example. Once you have this in place, the added configuration work is done.Let's create a simple test bean to ensure that the editor is registered properly:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class SomeBean {&lt;br /&gt;    private String[] array;&lt;br /&gt;&lt;br /&gt;    public void setArray(String[] array) {this.array = array;}&lt;br /&gt;    public String[] getArray() {return array;}&lt;br /&gt;}&lt;/pre&gt;Add it to the spring context:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;bean id="someBean" class="SomeBean"&amp;gt;&lt;br /&gt;    &amp;lt;property name="array" value="one:two:three:four" /&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;Now if you load the context and pull the bean out you will find that the array property contains four elements, with values of "one", "two", "three", and "four" respectively. It's just that easy!Just to verify that we have not lost any pre-existing functionality, you can add another bean that loads the array using the spring list tag:&lt;br /&gt;&lt;pre class="brush:java"&gt;&amp;lt;bean id="someBean2" class="SomeBean"&amp;gt;&lt;br /&gt;    &amp;lt;property name="array"&amp;gt;&lt;br /&gt;       &amp;lt;list&amp;gt;&lt;br /&gt;          &amp;lt;value&amp;gt;alpha&amp;lt;/value&amp;gt;&lt;br /&gt;          &amp;lt;value&amp;gt;bravo&amp;lt;/value&amp;gt;&lt;br /&gt;          &amp;lt;value&amp;gt;charlie&amp;lt;/value&amp;gt;&lt;br /&gt;       &amp;lt;/list&amp;gt;&lt;br /&gt;    &amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;You will notice that this method still works fine as well.There are a few other custom editors that spring provides in the &lt;tt&gt;org.springframework.bean.propertyeditors&lt;/tt&gt; package, and it is also quite easy to implement your own, but I will save that for another day.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;More Information&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://static.springframework.org/spring/docs/2.0.x/reference/validation.html#beans-beans-conversion"&gt;SpringFramework: Chapter 5: Validation, Data-binding, the BeanWrapper, and PropertyEditors&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-2529283645048015675?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/2529283645048015675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2007/11/custom-spring-editors.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/2529283645048015675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/2529283645048015675'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2007/11/custom-spring-editors.html' title='Custom Spring Editors'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-7393516362933191487</id><published>2007-09-28T12:20:00.000-05:00</published><updated>2011-11-17T12:22:18.141-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Recursion vs Iteration</title><content type='html'>Say you have a directory under which there can be multiple sub-directories of infinite depth each with any number of files. If you wanted to walk down the directory tree and do some sort of processing of the files the first thing that always came to my mind was recursion. Write a little recursive method to iterate over the files and call the method again on directories, performing the processing as you go. Something like this:&lt;pre class="brush:java"&gt;&lt;br /&gt;void walk(File dir){&lt;br /&gt;    for(File item : dir.listFiles()){&lt;br /&gt;        if(item.isDirectory()){&lt;br /&gt;            walk(item);&lt;br /&gt;        } else {&lt;br /&gt;            // process file&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now, this works fine and I have never run into problems with the approach; however, the iterative approach can be a useful and often more efficient alternative. Rather than recursively calling the &lt;tt&gt;walk()&lt;/tt&gt; method, perform the looping and use a &lt;tt&gt;Stack&lt;/tt&gt; object to maintain the set of directories to be searched. Here is what I mean:&lt;pre class="brush:java"&gt;&lt;br /&gt;void walk(File dir){&lt;br /&gt;    Stack&lt;File&gt; stack = new Stack&lt;File&gt;();&lt;br /&gt;    stack.push(dir);&lt;br /&gt;&lt;br /&gt;    while(!stack.isEmpty()){&lt;br /&gt;        for(File item : stack.pop().listFiles()){&lt;br /&gt;            if(item.isDirectory()){&lt;br /&gt;                stack.push(item);&lt;br /&gt;            } else {&lt;br /&gt;                // process file&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This method does the same thing but without recursion. I would like to do some profiling of the two approaches to see how each performs for various directory structures.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-7393516362933191487?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/7393516362933191487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2007/09/recursion-vs-iteration.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/7393516362933191487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/7393516362933191487'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2007/09/recursion-vs-iteration.html' title='Recursion vs Iteration'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-1161446943908549939</id><published>2007-09-27T12:25:00.000-05:00</published><updated>2011-11-17T12:28:54.371-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Map Iteration Tip</title><content type='html'>Say you have a &lt;tt&gt;Map&lt;/tt&gt; and that you need to iterate over its contents and do something with both the key and the value for each mapping. I have often seen the following code used:&lt;br /&gt;&lt;pre class="brush:java"&gt;Iterator i = map.keySet().iterator();&lt;br /&gt;while(i.hasNext()){&lt;br /&gt;    Object key = i.next();&lt;br /&gt;    Object val = map.get(key);&lt;br /&gt;    // do something with them&lt;br /&gt;}&lt;/pre&gt;While this is correct and generally there is nothing wrong with it, you are doing an extra &lt;tt&gt;get()&lt;/tt&gt; call into the map; however, if you iterate over the entry Set you can remove that extra call:&lt;br /&gt;&lt;pre class="brush:java"&gt;Iterator i = map.entrySet().iterator();&lt;br /&gt;while(i.hasNext()){&lt;br /&gt;    Entry entry = (Entry)i.next();&lt;br /&gt;    Object key = entry.getKey();&lt;br /&gt;    Object val = entry.getValue();&lt;br /&gt;    // do something with them&lt;br /&gt;}&lt;/pre&gt;Also, as a side note, with Java 5 and above you can use the new foreach loop to simplify things even more:&lt;br /&gt;&lt;pre class="brush:java"&gt;for(Entry&amp;lt;object,object&amp;gt; entry : map.entrySet()){&lt;br /&gt;    Object key = entry.getKey();&lt;br /&gt;    Object val = entry.getValue();&lt;br /&gt;    // do something&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;It's not going to double your processing speed or anything, but it is a little more efficient, especially when you are iterating over a large map of items.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-1161446943908549939?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/1161446943908549939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2007/09/map-iteration-tip.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/1161446943908549939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/1161446943908549939'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2007/09/map-iteration-tip.html' title='Map Iteration Tip'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-8690029326571735533</id><published>2005-06-15T09:38:00.000-05:00</published><updated>2011-11-03T09:41:11.613-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Dynamic JavaScript Loading</title><content type='html'>I figured out a way to dynamically load JavaScript files at runtime. There are times when you may not always need to import all of your external JavaScripts, or maybe you are using Ajax to load content into a div and you also need to import some script that the content needs. Here is the solution and it works in IE and &lt;a href="http://mozilla.org/firefox"&gt;FireFox&lt;/a&gt;: &lt;br /&gt;&lt;pre class="brush:javascript"&gt;function loadLibrary(path){&lt;br /&gt;    var headElt = document.getElementsByTagName("head").item(0);&lt;br /&gt;    var scriptElt = headElt.appendChild(document.createElement("script"));&lt;br /&gt;    scriptElt.setAttribute("type","text/javascript");&lt;br /&gt;    scriptElt.setAttribute("src",path);&lt;br /&gt;}&lt;/pre&gt;Pretty simple, and all you have to do to use it is: &lt;br /&gt;&lt;pre class="brush:javascript"&gt;loadLibrary("scripts/myscript.js");&lt;/pre&gt;This works for dynamically loading stylesheet too if you add a link element instead of a script element: &lt;br /&gt;&lt;pre class="brush:javascript"&gt;function loadStylesheet(path){&lt;br /&gt;    var headElt = document.getElementsByTagName("head").item(0);&lt;br /&gt;    var scriptElt = headElt.appendChild(document.createElement("link"));&lt;br /&gt;    scriptElt.setAttribute("type","text/css");&lt;br /&gt;    scriptElt.setAttribute("rel","stylesheet");&lt;br /&gt;    scriptElt.setAttribute("href",path);&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-8690029326571735533?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/8690029326571735533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2005/06/dynamic-javascript-loading.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/8690029326571735533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/8690029326571735533'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2005/06/dynamic-javascript-loading.html' title='Dynamic JavaScript Loading'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total><georss:featurename>Tucson, AZ, USA</georss:featurename><georss:point>32.2217429 -110.926479</georss:point><georss:box>32.0068154 -111.242336 32.436670400000004 -110.610622</georss:box></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-7034340419980736115</id><published>2005-02-18T07:59:00.010-06:00</published><updated>2011-08-24T08:08:55.881-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>End of Time, Well Date Anyway</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-sQoeqP6VIYE/TlT3XGCI5RI/AAAAAAAABuI/xOjKX7WUZww/s1600/timeclock.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-sQoeqP6VIYE/TlT3XGCI5RI/AAAAAAAABuI/xOjKX7WUZww/s1600/timeclock.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;I have run across this issue before you need to specify a "far-off" expiration date for something using the &lt;tt&gt;java.util.Date&lt;/tt&gt; class. Generally I have specified something 100 years in the future like 1/1/2100 or something like that using the old deprecated Date constructors... but that is not really a good practice or habit to get into. I came up with two better solutions.&lt;br /&gt;&lt;br /&gt;First, in the case where you want to specify a static date, you could run a quick test to determine the long time value for the date you create using the deprecated constructors. You then modify your code to use the long value in the constructor, which is the only remaining non-empty constructor.&lt;br /&gt;&lt;br /&gt;The second options is useful if you just want a date sometime in the unreachable future (avoiding any reasonable y2k-like issues) you can use the following:&lt;br /&gt;&lt;br /&gt;&lt;code lang="java"&gt;Date expiration = new Date(Long.MAX_VALUE);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;which will yield a date so far in the future that you would be proud to have your code exist that long and happy to fix it (at least your descendants should be happy to fix it). The date it represents is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Sun Aug 17 00:12:55 MST 292278994&lt;/pre&gt;&lt;br /&gt;I think that should suffice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-7034340419980736115?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/7034340419980736115/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2005/02/end-of-time-well-date-anyway.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/7034340419980736115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/7034340419980736115'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2005/02/end-of-time-well-date-anyway.html' title='End of Time, Well Date Anyway'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-sQoeqP6VIYE/TlT3XGCI5RI/AAAAAAAABuI/xOjKX7WUZww/s72-c/timeclock.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-4400371241346820432</id><published>2004-05-28T12:01:00.000-05:00</published><updated>2011-11-14T12:04:35.869-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Patterns'/><title type='text'>Worker Bean Pattern</title><content type='html'>&lt;br /&gt;I stumbled upon something that I thought would make a good design pattern, if no one else has already come up with it. The only name I could think of for it was the "Worker Bean Pattern". If this is not new, someone please let me know. &lt;br /&gt;&lt;br /&gt;Basically the pattern consists of two classes, the Manager class and the Worker class. The Manager class is used to create semi-transient Worker classes as required for use by client objects. The example I will use to help me describe this pattern is a web-based wizard-style input form.&lt;br /&gt;&lt;br /&gt;The Manager class is basically a loose combination of the Factory Pattern and the Singleton Pattern. There should be only one instance of a particular Manager object in the system. For my example, let's call the Manager class &lt;tt&gt;FormManager&lt;/tt&gt; and say that it is an object stored in the application scope of the server context (so that there will be only one instance per server context). The Manager class' function is to "manage" the Worker instances. It creates them, populates their data, and destroys them with no external objects acting directly on the Worker classes in a fashion other than read-only. We can stub out the methods of our &lt;tt&gt;FormManager&lt;/tt&gt; as the following:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;&lt;br /&gt;public class FormManager {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public FormWorker createFormWorker(){}&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public void postFormData(FormWorker fw, String name, String[] values){}&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public void destroyFormWorker(FormWorker fw){}&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;where &lt;tt&gt;FormWorker&lt;/tt&gt; is the Worker. The &lt;tt&gt;createFormWorker()&lt;/tt&gt; method is used to create a &lt;tt&gt;FormWorker&lt;/tt&gt; or use by the client when they first enter the wizard form set. This Worker could be pulled from a pool of available workers or created as needed. The client puts the &lt;tt&gt;FormWorker&lt;/tt&gt; the session scope. As the pages of the wizard form are submitted, each posts its data to the &lt;tt&gt;postFormData()&lt;/tt&gt; method, which processes the data and modifies the &lt;tt&gt;FormWorker&lt;/tt&gt; as necessary. Once the final wizard page has been submitted and the client has no more use for the &lt;tt&gt;FormWorker&lt;/tt&gt;, it is passed to the &lt;tt&gt;destroyFormWorker()&lt;/tt&gt; method so that it can be disposed of, or returned to the pool.&lt;br /&gt;&lt;br /&gt;The Worker is basically an encapsulation of data required to perform an operation in the Manager that requires more than a single step. In our example that &lt;tt&gt;FormWorker&lt;/tt&gt; is stored in the user's session so that the current state of the wizard form is maintained between pages. The Worker's data should not be accessible outside of the Manager. It exists only as a temporary extension of the Manager. Once the client has finished with a Worker it should be disposed of by the Manager. In our example, &lt;tt&gt;FormWorker&lt;/tt&gt;s could be pooled within the &lt;tt&gt;FormManager&lt;/tt&gt; to minimize new object creation and increase efficiency. Our &lt;tt&gt;FormWorker&lt;/tt&gt; would look something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;&lt;br /&gt;public class FormWorker {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FormWorker(){}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; void addData(String name, String[] value){}&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; String[] getData(String name){}&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; void setUserId(String userId){}&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public String getUserId(){}&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;You'll notice that most of the methods have the default access, accessible to only other classes in the same package. You would want to place the &lt;tt&gt;FormManager&lt;/tt&gt; and &lt;tt&gt;FormWorker&lt;/tt&gt; in the same package for this to work. Another interesting means of achieving this association, if you prefer, would be to nest the &lt;tt&gt;FormWorker&lt;/tt&gt; inside the &lt;tt&gt;FormManager&lt;/tt&gt; as a static nested class; with a nested class, you can enforce the isolation of the worker using private methods. Let's do that for our example and flesh out the methods a bit more for the final stub:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;&lt;br /&gt;// Manager&lt;br /&gt;&lt;br /&gt;public class FormManager {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public FormWorker createFormWorker(String userId){&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FormWorker fw = checkoutWorker();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if(fw != null){fw.setUserId(userId);}&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return(fw);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public void postFormData(FormWorker fw,String name,String[] values){&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fw.addData(name,values);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public void destroyFormWorker(FormWorker fw){&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fw.clearData();&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fw.setUserId(null);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; checkinWorker(fw);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Worker&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public static class FormWorker {&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private String userId;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private HashMap data;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private FormWorker(){this.data = new HashMap();}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private void addData(String name,String[] value){&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; data.put(name,value);&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private String[] getData(String name){&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return((String[])data.get(name));&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private void clearData(){data.clear();}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; private void setUserId(String userId){&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; this.userId = userId;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; public String getUserId(){return(userId);}&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Obviously this is still missing some code and does not really do anything interesting; however, it should give you the basic feel of my idea. I used something similar to this in a project recently (actually, a wizard web form set) and it worked very nicely. Of course there are other ways to skin this cat. You could even simply put the "working" data into the session itself, though I tend to like things neat and tidy without having to keep track of a lot of session variables. &lt;br /&gt;&lt;br /&gt;This may or may not already be a pattern on someone's list or in a book somewhere, but I did not find it. I present it in the hope that it will be useful to someone, and as I said, I am always open for comments or suggestions. &lt;br /&gt;&lt;div&gt;&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/4286850429573966280-4400371241346820432?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/4400371241346820432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2004/05/worker-bean-pattern.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/4400371241346820432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/4400371241346820432'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2004/05/worker-bean-pattern.html' title='Worker Bean Pattern'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total><georss:featurename>Tucson, AZ, USA</georss:featurename><georss:point>32.2217429 -110.926479</georss:point><georss:box>32.0068154 -111.242336 32.436670400000004 -110.610622</georss:box></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-513951524267698483</id><published>2004-05-28T10:27:00.000-05:00</published><updated>2011-11-02T10:29:59.345-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Proper Care and Feeding of Your JDBC Code</title><content type='html'>If you've been developing very long, you have probably run across this numerous times, but for those of you that are new to JDBC, here is a quick tip that will help you greatly in the long run (I have seen it done wrong many times). When you perform your connection retrieval (directly or from a pool - use a pool as often as possible), make sure that you clean up your resources when you are done.&lt;br /&gt;&lt;pre class="brush:java"&gt;Connection conn = null;&lt;br /&gt;Statement st = null;&lt;br /&gt;ResultSet rs = null;&lt;br /&gt;try {&lt;br /&gt;    conn = getConnection();&lt;br /&gt;    st = conn.createStatement();&lt;br /&gt;    rs = st.executeQuery(sql);&lt;br /&gt;    while(rs.next()){&lt;br /&gt;        // ... work it&lt;br /&gt;    }&lt;br /&gt;    // minimize code here to free conn ASAP.&lt;br /&gt;} catch(Exception ex){&lt;br /&gt;    // log this or something&lt;br /&gt;} finally {&lt;br /&gt;    if(rs != null){&lt;br /&gt;        try {rs.close();} catch(Exception e){}&lt;br /&gt;    }&lt;br /&gt;    if(st != null){&lt;br /&gt;        try {st.close();} catch(Exception e){}&lt;br /&gt;    }&lt;br /&gt;    if(conn != null){&lt;br /&gt;        try {conn.close();} catch(Exception e){}&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;If you follow this format as a general rule, you will end up with fewer database related problems, especially if you use connection pooling. Connections that are not disposed of properly can cause very bad things to happen in your application. Also, note that I mention minimizing the code within the try/catch block. You want to use your connection and clean it up as soon as possible (again, especially if you are pooling) so that the resources are not hanging around needlessly. Another interesting point is the set of empty catch blocks. We have all been told that empty catch blocks are a bad thing; however, I have seen this style used in pretty much every description of this code. The JDBC developers (in their infinite wisdom) decided that every method should throw a &lt;tt&gt;SQLException&lt;/tt&gt;, even though there is little you can really do about it when you are trying to close up the connection. That is why you just ignore them; though here I ignore them separately so that one does not affect the others.You can clean up this code even more if you use the &lt;a href="http://commons.apache.org/dbutils"&gt;Jakarta Commons - DbUtils API&lt;/a&gt;:&lt;br /&gt;&lt;pre class="brush:java"&gt;Connection conn = null;&lt;br /&gt;Statement st = null;&lt;br /&gt;ResultSet rs = null;&lt;br /&gt;try {&lt;br /&gt;    conn = getConnection();&lt;br /&gt;    st = conn.createStatement();&lt;br /&gt;    rs = st.executeQuery(sql);&lt;br /&gt;    while(rs.next()){&lt;br /&gt;            // ... work it&lt;br /&gt;    }&lt;br /&gt;    // minimize code here to free conn ASAP.&lt;br /&gt;} catch(Exception ex){&lt;br /&gt;    // log this or something&lt;br /&gt;} finally {&lt;br /&gt;    DbUtils.closeQuietly(rs);&lt;br /&gt;    DbUtils.closeQuietly(st);&lt;br /&gt;    DbUtils.closeQuietly(conn);&lt;br /&gt;}&lt;/pre&gt;You are not really required to close the &lt;tt&gt;ResultSet&lt;/tt&gt;, and the &lt;tt&gt;Statement&lt;/tt&gt; if you close the &lt;tt&gt;Connection&lt;/tt&gt; properly; however, it adds an extra level of assurance especially when you are using a driver that may not be fully compliant or properly implemented.The &lt;a href="http://springframework.org/"&gt;Spring Framework&lt;/a&gt; also provides some very useful APIs for working with JDBC such as template methods, row mappers, and callback closure-like structures, an example would be:&lt;br /&gt;&lt;pre class="brush:java"&gt;new ConnectionCallback(){&lt;br /&gt;    public Object doInConnection(Connection conn) throws SQLException, DataAccessException {&lt;br /&gt;        st = conn.createStatement();&lt;br /&gt;        rs = st.executeQuery(sql);&lt;br /&gt;        while(rs.next()){&lt;br /&gt;            // ... work it&lt;br /&gt;        }&lt;br /&gt;        // minimize code here to free conn ASAP.&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;There are quite a few helpful constructs in the Spring JDBC API to make JDBC interaction easier to work with.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-513951524267698483?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/513951524267698483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2004/05/proper-care-and-feeding-of-your-jdbc.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/513951524267698483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/513951524267698483'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2004/05/proper-care-and-feeding-of-your-jdbc.html' title='Proper Care and Feeding of Your JDBC Code'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total><georss:featurename>Tucson, AZ, USA</georss:featurename><georss:point>32.2217429 -110.926479</georss:point><georss:box>32.0068154 -111.242336 32.436670400000004 -110.610622</georss:box></entry><entry><id>tag:blogger.com,1999:blog-4286850429573966280.post-6892729932682641658</id><published>2004-02-11T06:00:00.000-06:00</published><updated>2011-10-02T11:00:25.326-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Proxy Method Logging</title><content type='html'>Here's an interesting spin on logging. Say you have a service of some kind that loads in other classes dynamically such as modules or plug-ins and you want to provide a seamless debugging mode no matter what kind of logging the module developer has done. You can use a &lt;tt&gt;java.lang.reflect.Proxy&lt;/tt&gt; to do your method call logging. Basically, you build a wrapper around your module that does the logging for any module.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;First off, you need an interface to do this, any interface that your modules implement will work.&lt;br /&gt;&lt;pre class="brush:java"&gt;public interface SomeInterface {&lt;br /&gt;    public void doSomethingA(String p1, int p2);&lt;br /&gt;    public String[] doSomethingB(Map map);&lt;br /&gt;}&lt;/pre&gt;Second, you will need an implementation of that interface, your module or plug-in class. This is the actual object doing the work whose method calls will be logged. &lt;br /&gt;&lt;pre class="brush:java"&gt;public class SomeImplnterfaceImpl implements SomeInterface {&lt;br /&gt;    public SomeImplnterfaceImpl(){}&lt;br /&gt;&lt;br /&gt;    public void doSomethingA(String p1, int p2){&lt;br /&gt;            System.out.println("doSomethingA with ("+p1+" and "+p2+")");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String[] doSomethingB(Map map){&lt;br /&gt;            System.out.println("doSomethingB with a Map: " + map);&lt;br /&gt;            return((String[])map.values().toArray(new String[0]));&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;Both of the above items you should already have in your system. If not, in most cases, they can be easily added. Now we get into the good stuff. The Proxy class is an interesting little creature. It allows you to give it an array of interfaces that it is to "implement" by passing method calls to an instance of the &lt;tt&gt;InvocationHandler&lt;/tt&gt; interface, which determines how the method calls are to be processed. Our proxy will simply log the method call and pass it along to the real implementation.We'll make a factory class to create our logging proxy. It will have one method with two arguments. The first is the interface that is to be logged, SomeInterface in our example, and the second is the implementation of that interface, &lt;tt&gt;SomeInterfaceImpl&lt;/tt&gt; for our example.&lt;br /&gt;&lt;pre class="brush:java"&gt;public final class LoggingProxyFactory {&lt;br /&gt;    private LoggingProxyFactory(){}&lt;br /&gt;&lt;br /&gt;    public static final Object create(Class interfc,Object impl) throws Exception {&lt;br /&gt;            LoggingHandler handler = new LoggingHandler(impl);&lt;br /&gt;            return(Proxy.newProxyInstance(&lt;br /&gt;                    impl.getClass().getClassLoader(),&lt;br /&gt;                    new Class[]{interfc},&lt;br /&gt;                    handler)&lt;br /&gt;            );&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;You see that creating a proxy is pretty simple. You create a proxy instance using your interfaces, your class loader and your invocation handler. The invocation handler is where the action happens. Our &lt;tt&gt;InvocationHandler&lt;/tt&gt; implementation is going to be a static inner class of the &lt;tt&gt;LoggingProxyFactory&lt;/tt&gt; class. What it needs to do is logs the method calls as they come in and then pass along the call to the real implementation. Here is the code for the inner class.&lt;br /&gt;&lt;pre class="brush:java"&gt;private static final class LoggingHandler implements InvocationHandler {&lt;br /&gt;    private Log log;&lt;br /&gt;    private Object impl;&lt;br /&gt;&lt;br /&gt;    private LoggingHandler(Object impl){&lt;br /&gt;            this.impl = impl;&lt;br /&gt;            this.log = LogFactory.getLog(impl.getClass());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Object invoke(Object obj,Method method,Object[] params) throws Throwable{&lt;br /&gt;            if(log.isInfoEnabled()) log.info("Entering: "+method.getName();&lt;br /&gt;&lt;br /&gt;            if(log.isDebugEnabled()){&lt;br /&gt;                    for(int p=0; p&amp;amp;tl;params.length;p++){&lt;br /&gt;                            log.debug(method.getName() + " Param[" + p + "]: " +&lt;br /&gt;                                    params[p].toString());&lt;br /&gt;                    }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            Object ret = null;&lt;br /&gt;&lt;br /&gt;            try {ret = method.invoke(impl, params);}&lt;br /&gt;            catch(Exception ex){&lt;br /&gt;                    if(log.isErrorEnabled()){&lt;br /&gt;                            log.error(method.getName()+" Exception: "+&lt;br /&gt;                                    ex.getMessage(),ex);&lt;br /&gt;                    }&lt;br /&gt;                    throw ex;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            if(log.isDebugEnabled()) log.debug(method.getName()+" Returned: "+ret);&lt;br /&gt;&lt;br /&gt;            if(log.isInfoEnabled()) log.info("Leaving");&lt;br /&gt;&lt;br /&gt;            return(ret);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;I am using the &lt;a href="http://commons.apache.org/logging"&gt;Jakarta Logging API&lt;/a&gt; for the logging in this case; however, you could use any logging API or simple standard out statements as you see fit. The main thing to notice in the code above is the invocation of the method on the implementation object.&lt;br /&gt;&lt;pre class="brush:java"&gt;ret = method.invoke(impl, params);&lt;/pre&gt;It is this line that passes on the method call to the real implementation. The rest of the method is logging of the entering and leaving, parameters and return value. Exceptions are also logged. To test out the proxy and see it in action, you can run the following tester app:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class Tester {&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args){&lt;br /&gt;            try {&lt;br /&gt;                    Object obj = LoggingProxyFactory.create(&lt;br /&gt;                            SomeInterface.class,&lt;br /&gt;                            new SomeImplnterfaceImpl()&lt;br /&gt;                    );&lt;br /&gt;                    SomeInterface siObj = (SomeInterface)obj;&lt;br /&gt;                    siObj.doSomethingA("Hello",31);&lt;br /&gt;&lt;br /&gt;                    Map map = new HashMap();&lt;br /&gt;                    map.put("a", "Some data 1");&lt;br /&gt;                    map.put("b", "Some data 2");&lt;br /&gt;                    map.put("c", "Some data 3");&lt;br /&gt;                    map.put("d", "Some data 4");&lt;br /&gt;                    siObj.doSomethingB(map);&lt;br /&gt;&lt;br /&gt;            } catch(Exception ex){ex.printStackTrace();}&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;Which yields the method call results and their logging information.  This was an interesting idea I have tossed around for a while now. It does work, though it has not been rigorously tested. If nothing else, it is a description of how to use the proxy class.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4286850429573966280-6892729932682641658?l=www.coffeaelectronica.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.coffeaelectronica.com/feeds/6892729932682641658/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.coffeaelectronica.com/2004/02/proxy-method-logging.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/6892729932682641658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4286850429573966280/posts/default/6892729932682641658'/><link rel='alternate' type='text/html' href='http://www.coffeaelectronica.com/2004/02/proxy-method-logging.html' title='Proxy Method Logging'/><author><name>Christopher Stehno</name><uri>https://profiles.google.com/113887627602850751282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-6C2TXNk05pI/AAAAAAAAAAI/AAAAAAAACvc/c6k_WWw_qYc/s512-c/photo.jpg'/></author><thr:total>0</thr:total><georss:featurename>Oro Valley, AZ, USA</georss:featurename><georss:point>32.3909071 -110.966488</georss:point><georss:box>32.3372746 -111.045452 32.4445396 -110.887524</georss:box></entry></feed>
