<?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-443704514548190823</id><updated>2012-02-13T16:18:36.404-08:00</updated><category term='mocks'/><category term='error_handling'/><category term='dbunit'/><category term='office'/><category term='webtestingexplorer'/><category term='java'/><category term='workflow'/><category term='web'/><category term='books'/><category term='dozer'/><category term='soa'/><category term='models'/><category term='junit'/><category term='selenium'/><category term='webdriver'/><category term='aspectj'/><category term='unit_testing'/><category term='nunit'/><category term='web_services'/><category term='hsqldb'/><category term='easymock'/><category term='cplusplus'/><category term='log4net'/><category term='design'/><category term='eclipse'/><category term='openarchitectureware'/><category term='testing'/><category term='dotnet'/><category term='architecture'/><category term='aws'/><title type='text'>Software Development With Scott</title><subtitle type='html'>Technical Info on .NET and Java, Software Tool and Book Recommendations, and Thoughts on Software Development in General from Scott McMaster.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>41</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-7517421972975668273</id><published>2012-01-11T09:13:00.001-08:00</published><updated>2012-01-11T09:17:38.811-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webtestingexplorer'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>WebTestingExplorer Documentation Moving Forward</title><content type='html'>I started writing some documentation for &lt;a href="http://code.google.com/p/webtestingexplorer/"&gt;WebTestingExplorer&lt;/a&gt; in &lt;a href="http://code.google.com/p/webtestingexplorer/w/list"&gt;the site Wiki&lt;/a&gt;. Because I would like people to start trying it out, I'm hoping that it will help them get started without necessarily having to pour over all the code and Javadocs. The core API documentation is (and always will be) in the Javadocs, but I will use the Wiki to explain some of the higher-level concepts and design philosophies. So if you get the chance, download, build, try it out, and, of course, let me know how it goes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-7517421972975668273?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/7517421972975668273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=7517421972975668273' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7517421972975668273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7517421972975668273'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2012/01/webtestingexplorer-documentation-moving.html' title='WebTestingExplorer Documentation Moving Forward'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-4004693126849912024</id><published>2012-01-01T16:07:00.000-08:00</published><updated>2012-01-01T16:15:00.890-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webtestingexplorer'/><category scheme='http://www.blogger.com/atom/ns#' term='webdriver'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><title type='text'>Suppressing Firefox favicon Requests from WebDriver</title><content type='html'>Right now, &lt;a href="http://code.google.com/p/webtestingexplorer/"&gt;WebTestingExplorer&lt;/a&gt; only works with Firefox. There are a couple of reasons for that which I won't go into here. Anyway, we decided to wire our HTTP requests to go through a proxy by default. This allows us to look for specific status codes, presumably "undesirable" ones like the 500-series, or possibly 404's.&lt;div&gt;
&lt;/div&gt;&lt;p/&gt;&lt;div&gt;When we did this and pointed the tool at a specific URL, the first thing we noticed was Firefox firing off a bunch of requests to various locations on the target site fishing around for the &lt;a href="http://en.wikipedia.org/wiki/Favicon"&gt;favicon&lt;/a&gt;, most of which (kind of by definition) ended with a 404. I'm surprised that for as long as I have used Firefox attached to a proxy for testing and debugging, I had never taken note of this before. In this case, it's really annoying -- after all, one of the points of using the proxy in the first place was to verify that all requests came back with a valid response.&lt;/div&gt;&lt;p/&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;The first thing I considered was adding special-case code for all favicon requests in the handlers for the proxy. That seemed kind of lame, so the second thing I thought about was looking for a way to suppress the requests in the first place. It turns out that there are Firefox preferences for this -- &lt;span&gt;browser.chrome.favicons&lt;/span&gt; and &lt;span&gt;browser.chrome.site_icons&lt;/span&gt; -- as described &lt;a href="http://www.howtogeek.com/howto/internet/firefox/quick-tip-disable-favicons-in-firefox/"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;p/&gt;&lt;div&gt;I have two main points left for this post. First, I wanted to show the code for how set these preferences in Selenium WebDriver when you build your &lt;a href="http://selenium.googlecode.com/svn/trunk/docs/api/java/org/openqa/selenium/firefox/FirefoxProfile.html"&gt;FirefoxProfile&lt;/a&gt;:&lt;/div&gt;&lt;p/&gt;&lt;div&gt;
&lt;div align="left" class="java"&gt;
&lt;table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;final&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;FirefoxProfile&amp;nbsp;profile&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;FirefoxProfile&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;The&amp;nbsp;following&amp;nbsp;preferences&amp;nbsp;control&amp;nbsp;Firefox's&amp;nbsp;default&amp;nbsp;behavior&amp;nbsp;of&amp;nbsp;sending&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;requests&amp;nbsp;for&amp;nbsp;favicon.ico,&amp;nbsp;which&amp;nbsp;results&amp;nbsp;in&amp;nbsp;lots&amp;nbsp;of&amp;nbsp;bogus&amp;nbsp;404's&amp;nbsp;for&amp;nbsp;sites&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;that&amp;nbsp;don't&amp;nbsp;have&amp;nbsp;favicons.&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;profile.setPreference&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#2a00ff"&gt;&amp;#34;browser.chrome.favicons&amp;#34;&lt;/font&gt;&lt;font color="#000000"&gt;,&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;false&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;profile.setPreference&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#2a00ff"&gt;&amp;#34;browser.chrome.site_icons&amp;#34;&lt;/font&gt;&lt;font color="#000000"&gt;,&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;false&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt; &amp;nbsp; &amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;FirefoxDriver&amp;nbsp;driver&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;FirefoxDriver&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;profile&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p/&gt;&lt;div&gt;
&lt;/div&gt;&lt;p/&gt;&lt;div&gt;Second, I will pitch my opinion that in most automated testing scenarios, you probably ought to be suppressing favicon requests from Firefox, even if your site actually has one. A good way to do this is to save the preferences into a custom Firefox profile used for automated testing. If you don't already have one of those, you should. I will try to provide some tips on how to maintain and use one of those in a future post.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-4004693126849912024?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/4004693126849912024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=4004693126849912024' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4004693126849912024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4004693126849912024'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2012/01/suppressing-firefox-favicon-requests.html' title='Suppressing Firefox favicon Requests from WebDriver'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-4660879963856021365</id><published>2012-01-01T15:09:00.000-08:00</published><updated>2012-01-01T15:19:56.511-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webtestingexplorer'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Introducing WebTestingExplorer</title><content type='html'>My friend and I recently started working on a new open-source project, &lt;a href="http://code.google.com/p/webtestingexplorer/"&gt;WebTestingExplorer&lt;/a&gt; (hosted on code.google.com). We are setting out to do a high-quality, industrial-strength implementation of some of the ideas we worked on in graduate school at the University of Maryland, specifically applied to web application testing. The super-high-level idea is to incrementally "explore" the web user interface, simultaneously looking for bugs and generating test cases. Although the project is in a very early state, the current implementation can do a few useful things, such as:&lt;div&gt;&lt;ul&gt;&lt;li&gt;Detecting unexpected HTTP status codes and Javascript errors during exploration.&lt;/li&gt;&lt;li&gt;Generating and replaying regression test cases, looking for differences in state.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;A few tenets we have adopted are:&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Most everything regarding the exploration and test case replay process is configurable.&lt;/li&gt;&lt;li&gt;Configuration is done primarily via code, not command-line arguments or XML files.&lt;/li&gt;&lt;li&gt;The application-under-test is inherently "testable" (or we can make it so), such as having unique id's for important HTML elements, and hooks that enable reliable timing of actions.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;If you are interested in automated test case generation and/or web application testing, feel free to start playing with it and sending feedback through the Google Code project. Although the primary vehicle for project communication will be the Project Wiki, I hope to blog here about some of the interesting issues we encounter. So until next time...&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-4660879963856021365?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/4660879963856021365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=4660879963856021365' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4660879963856021365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4660879963856021365'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2012/01/introducing-webtestingexplorer.html' title='Introducing WebTestingExplorer'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-7785525670320142694</id><published>2011-05-29T15:35:00.001-07:00</published><updated>2011-05-29T16:48:59.153-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocks'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='easymock'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>EasyMock Issue #2: Bad Argument equals()</title><content type='html'>One of the two canonical EasyMock failure messages (along with &lt;span style="font-style:italic;"&gt;"Expectation failure on verify"&lt;/span&gt; is &lt;i&gt;"Unexpected method call"&lt;/i&gt;. This failure happens between &lt;span class="Apple-style-span"&gt;replay()&lt;/span&gt; and &lt;span class="Apple-style-span"&gt;verify()&lt;/span&gt; whenever EasyMock sees the class-under-test making a method call on a mock object for which the test did not set up an expectation. Generally, there are two possible problems to immediately look for:&lt;div&gt;&lt;ol&gt;&lt;li&gt;A deficiency in the test, i.e. the behavior of the class-under-test is not correctly modeled during the expectation-setting phase of the test case.&lt;/li&gt;&lt;li&gt;A bug in the class-under-test, i.e. the test is expecting the &lt;i&gt;right&lt;/i&gt; thing but the code isn't doing it.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;br/&gt;(Incidentally, when it turns out to be #1, programmers tend to complain about how difficult it is to write and maintain mock-object tests, and when it is instead #2, they often fail to give mock-object-testing enough credit for detecting their bug.)&lt;/div&gt;&lt;br/&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;But there is a third possibility, which I would argue is neither a bug in the test nor in the class-under-test, but rather a bug in a completely separate, possibly innocuous-looking class that they both use. As usual, let's look at an example.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;Br/&gt;&lt;div&gt;Here is the class-under-test, &lt;span class="Apple-style-span" &gt;CustomerController &lt;/span&gt;for this post (slightly modified from the one I used in the previous two posts:&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;
&lt;div align="left" class="java"&gt;
&lt;table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;class&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;private&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;orderService;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;orderService&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;this&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;.orderService&amp;nbsp;=&amp;nbsp;orderService;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;void&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;Order...orders&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;orderService.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;Arrays.asList&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;orders&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;And the test for the &lt;span class="Apple-style-span" &gt;postOrders()&lt;/span&gt; method, which uses a mock &lt;span class="Apple-style-span" &gt;OrderService&lt;/span&gt;:&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;
&lt;div align="left" class="java"&gt;
&lt;table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;class&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;TestCustomerController&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#646464"&gt;@Test&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;void&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;testPostOrders&lt;/font&gt;&lt;font color="#000000"&gt;()&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;mockOrderService&amp;nbsp;=&amp;nbsp;EasyMock.createMock&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;OrderService.&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;class&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&amp;nbsp;controller&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;List&amp;lt;Order&amp;gt;&amp;nbsp;expectedOrders&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;ArrayList&amp;lt;Order&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;expectedOrders.add&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Order&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;EasyMock.expect&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;expectedOrders&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;.andReturn&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;EasyMock.replay&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;controller.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Order&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;&lt;div&gt;It is also important to note that we are posting &lt;span class="Apple-style-span" &gt;Order &lt;/span&gt;instances:&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;
&lt;div align="left" class="java"&gt;
&lt;table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;class&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Order&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;private&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;int&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;orderId;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Order&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;int&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;orderId&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;this&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;.orderId&amp;nbsp;=&amp;nbsp;orderId;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;&lt;div&gt;When you run this, alas, you get an error:&lt;/div&gt;&lt;div&gt;&lt;Br/&gt;
&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;java.lang.AssertionError: &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;  Unexpected method call postOrders([Order@1e4cbc4]):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;    postOrders([Order@b2fd8f]): expected: 1, actual: 0&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;        at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:92)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;        at OrderService$$EnhancerByCGLIB$$6d00fba4.postOrders(&lt;generated&gt;)&lt;/generated&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;        at CustomerController.postOrders(CustomerController.java:11)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" &gt;        at TestCustomerController.testPostOrders(TestCustomerController.java:19)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;Br/&gt;&lt;div&gt;The &lt;span class="Apple-style-span" &gt;CustomerController &lt;/span&gt;code is quite trivial and correct in this case. The test is less trivial but also, well, at least &lt;i&gt;ought &lt;/i&gt;to work. So what is going on here?&lt;/div&gt;&lt;br/&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;When EasyMock compares a mock method call to the expectation, it checks equality on each argument. In this case, that fails because the &lt;span class="Apple-style-span" &gt;Order &lt;/span&gt;instance in the expectation does not equal the &lt;span class="Apple-style-span" &gt;Order &lt;/span&gt;instance in the invocation. Note that &lt;span class="Apple-style-span" &gt;Order &lt;/span&gt;is not overriding &lt;span class="Apple-style-span" &gt;equals()&lt;/span&gt;, and since I set the test up to use different instances, the default instance-equality check fails. This example is pretty contrived. In reality, the more common case I've seen is that there actually &lt;i&gt;is &lt;/i&gt;an equals() override, but somebody changes the class and breaks it, in which case the test is finding a real bug (albeit not in the actual class-under-test!).&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;br/&gt;&lt;div&gt;There are two ways to fix this problem. First, I could override &lt;span class="Apple-style-span" &gt;equals()&lt;/span&gt; (and &lt;span class="Apple-style-span" &gt;hashCode()&lt;/span&gt;, of course) if it doesn't already exist on the class in question (the case with &lt;span class="Apple-style-span" &gt;Order&lt;/span&gt;) or fix &lt;span class="Apple-style-span" &gt;equals()&lt;/span&gt; if it were already there. That solution is probably more robust. The quick-and-dirty fix is to make the expectation and the invocation use the exact same instance(s). I will actually show the code for that:&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;
&lt;div align="left" class="java"&gt;
&lt;table border="0" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;class&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;TestCustomerController&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#646464"&gt;@Test&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;void&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;testPostOrders&lt;/font&gt;&lt;font color="#000000"&gt;()&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;mockOrderService&amp;nbsp;=&amp;nbsp;EasyMock.createMock&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;OrderService.&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;class&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&amp;nbsp;controller&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;List&amp;lt;Order&amp;gt;&amp;nbsp;expectedOrders&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;ArrayList&amp;lt;Order&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#3f7f5f"&gt;//&amp;nbsp;I&amp;nbsp;will&amp;nbsp;use&amp;nbsp;this&amp;nbsp;here&amp;nbsp;AND&amp;nbsp;below...&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;Order&amp;nbsp;order&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Order&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;expectedOrders.add&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;order&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;EasyMock.expect&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;expectedOrders&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;.andReturn&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;EasyMock.replay&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;controller.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;order&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Sorry if I've rambled on for too long about something that is actually quite obvious. But I have made and seen others make this type of error enough times that I can hopefully save someone some time. Next time, I will discuss an EasyMock limitation that might impact your interface design.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-7785525670320142694?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/7785525670320142694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=7785525670320142694' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7785525670320142694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7785525670320142694'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2011/05/easymock-issue-2-bad-argument-equals.html' title='EasyMock Issue #2: Bad Argument equals()'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-8275629560813229111</id><published>2011-04-30T21:27:00.000-07:00</published><updated>2011-05-01T10:37:46.181-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocks'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='easymock'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>EasyMock Issue #1: Missing Behavior Definition</title><content type='html'>&lt;style type="text/css"&gt;    &lt;!--code { font-family: Courier New, Courier; font-size: 10pt; margin: 0px; }--&gt;  &lt;/style&gt;
I am planning to do a series of posts (including examples) on issues that people (myself included) run into while using &lt;a href="http://easymock.org/"&gt;EasyMock&lt;/a&gt;. The first of these involves EasyMock throwing an IllegalStateException "&lt;b&gt;&lt;i&gt;missing behavior definition for the preceding method call&lt;/i&gt;&lt;/b&gt;". The executive summary is that this is caused by the omission of a return value specification (&lt;span class="Apple-style-span" &gt;andReturn(...)&lt;/span&gt;). If that is all you need to know, thanks for coming. If you would like to see a specific example, keep reading.&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;br/&gt;Let's say we are writing unit tests for a &lt;span class="Apple-style-span" &gt;CustomerController &lt;/span&gt;class that uses an &lt;span class="Apple-style-span" &gt;OrderService &lt;/span&gt;class to post a &lt;span class="Apple-style-span" &gt;Customer&lt;/span&gt;'s &lt;span class="Apple-style-span" &gt;Orders&lt;/span&gt;:&lt;/div&gt;

&lt;br/&gt;
&lt;div align="left" class="java"&gt;
&lt;table border="2" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;OrderService.java&lt;/font&gt;&lt;br /&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;class&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;int&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;List&amp;lt;Order&amp;gt;&amp;nbsp;orders&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;return&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;orders.size&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;CustomerController.java&lt;/font&gt;&lt;br /&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;class&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;private&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;orderService;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;orderService&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;this&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;.orderService&amp;nbsp;=&amp;nbsp;orderService;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;void&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;Integer...orderIds&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;List&amp;lt;Order&amp;gt;&amp;nbsp;orderList&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;ArrayList&amp;lt;Order&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;for&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;int&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;orderId&amp;nbsp;:&amp;nbsp;orderIds&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;orderList.add&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Order&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;orderId&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;orderService.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;orderList&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;br/&gt;
In the real world, &lt;span class="Apple-style-span" &gt;OrderService &lt;/span&gt;probably connects to external resources and therefore we want to mock it when creating a unit test for &lt;span class="Apple-style-span" &gt;CustomerController&lt;/span&gt;. The interaction between &lt;span class="Apple-style-span" &gt;CustomerController &lt;/span&gt;and &lt;span class="Apple-style-span" &gt;OrderService &lt;/span&gt;consists of a single call to the &lt;span class="Apple-style-span" &gt;OrderService.postOrders(List&lt;orders&gt;&lt;/orders&gt;&lt;/span&gt;&lt;orders&gt;&lt;span class="Apple-style-span" &gt;)&lt;/span&gt; method. Simple enough, here is our first attempt at writing this test:
&lt;br/&gt;&lt;br/&gt;

&lt;div align="left" class="java"&gt;
&lt;table border="2" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;class&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;TestCustomerController&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#646464"&gt;@Test&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;public&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;void&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;testPostOrders&lt;/font&gt;&lt;font color="#000000"&gt;()&amp;nbsp;{&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;OrderService&amp;nbsp;mockOrderService&amp;nbsp;=&amp;nbsp;EasyMock.createMock&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;OrderService.&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;class&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&amp;nbsp;controller&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;CustomerController&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;List&amp;lt;Order&amp;gt;&amp;nbsp;expectedOrders&amp;nbsp;=&amp;nbsp;&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;ArrayList&amp;lt;Order&amp;gt;&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;expectedOrders.add&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#7f0055"&gt;&lt;b&gt;new&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;Order&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#3f7f5f"&gt;//&amp;nbsp;You&amp;nbsp;may&amp;nbsp;do&amp;nbsp;the&amp;nbsp;following&amp;nbsp;assuming&amp;nbsp;postOrders&amp;nbsp;returns&amp;nbsp;void:&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;expectedOrders&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;EasyMock.expectLastCall&lt;/font&gt;&lt;font color="#000000"&gt;()&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#3f7f5f"&gt;//&amp;nbsp;But&amp;nbsp;unfortunately&amp;nbsp;it&amp;nbsp;doesn't.&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;EasyMock.replay&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;controller.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;br/&gt;
When you run this, it blows up with the following exception:
&lt;pre&gt;java.lang.IllegalStateException: missing behavior definition for the preceding method call postOrders([Order@82c01f])
 at org.easymock.internal.MocksControl.replay(MocksControl.java:174)
 at org.easymock.EasyMock.replay(EasyMock.java:1970)
 at TestCustomerController.testPostOrders(TestCustomerController.java:22)
&lt;/pre&gt;
Since I gave away the punch line in the opening paragraph of this post, you know that this happens because &lt;span class="Apple-style-span" &gt;OrderService.postOrders()&lt;/span&gt; is &lt;i&gt;not&lt;/i&gt; a &lt;span class="Apple-style-span" &gt;void &lt;/span&gt;method; rather, it returns an &lt;span class="Apple-style-span" &gt;int&lt;/span&gt;. I would argue that this is a pretty easy mistake to make, though, because the implementation of &lt;span class="Apple-style-span" &gt;CustomerController &lt;/span&gt;does not care about or capture this return value. So unless we're paying pretty close attention to the &lt;span class="Apple-style-span" &gt;OrderService &lt;/span&gt;interface, we might assume it returns &lt;span class="Apple-style-span" &gt;void&lt;/span&gt;. And unfortunately, &lt;i&gt;EasyMock requires that ALL non-void method calls on mocks have return values specified&lt;/i&gt;, even if the class under test will ignore it.
&lt;br/&gt;&lt;br/&gt;

So the correct way to set up the &lt;span class="Apple-style-span" &gt;OrderService.postOrders()&lt;/span&gt; expectation is as follows:
&lt;br/&gt;&lt;br/&gt;
&lt;div align="left" class="java"&gt;
&lt;table border="2" cellpadding="3" cellspacing="0" bgcolor="#ffffff"&gt;
   &lt;tr&gt;
  &lt;!-- start source code --&gt;
   &lt;td nowrap="nowrap" valign="top" align="left"&gt;
    &lt;code&gt;
&lt;font color="#000000"&gt;EasyMock.expect&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;mockOrderService.postOrders&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;expectedOrders&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;font color="#000000"&gt;.andReturn&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#990000"&gt;1&lt;/font&gt;&lt;font color="#000000"&gt;)&lt;/font&gt;&lt;font color="#000000"&gt;;&lt;/font&gt;&lt;/code&gt;
    
   &lt;/td&gt;
  &lt;!-- end source code --&gt;
   &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;br/&gt;
In my next "EasyMock Issues" post, I will talk about a couple of things to look for when your expectations are mysteriously failing to match.&lt;/orders&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-8275629560813229111?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/8275629560813229111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=8275629560813229111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8275629560813229111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8275629560813229111'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2011/04/easymock-issue-1-missing-behavior.html' title='EasyMock Issue #1: Missing Behavior Definition'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-3297547926349437393</id><published>2011-04-16T21:01:00.000-07:00</published><updated>2011-04-17T20:19:22.080-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocks'/><category scheme='http://www.blogger.com/atom/ns#' term='easymock'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><title type='text'>The Law of Demeter Makes You Create Mock-Friendly Objects</title><content type='html'>One of the reasons why I haven't been blogging is that much of the time, I have been busy with teaching -- specifically, as an adjunct in the &lt;a href="http://www.seattleu.edu/scieng/comsci/Default.aspx?id=9696"&gt;Master of Software Engineering program at Seattle University&lt;/a&gt; (of which I am also an alumnus).
&lt;br&gt;
&lt;div&gt;The last two winters, I have taught software design and modeling. Something that I touch on with my students is the &lt;a href="http://en.wikipedia.org/wiki/Law_of_Demeter"&gt;Law of Demeter&lt;/a&gt;. The LoD states (among a couple of other things) that a software object in an object-oriented program should only talk to its "immediate friends". That rules out long method call chains like this:&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;customer.getOrders().get(0).getLineItems().get(0).getProduct().getQuantity()&lt;/span&gt;&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;This is bad for at least two obvious reasons. First, the objects in this program are tightly-coupled, and changes to the object model will ripple through the system in annoying ways. Second, if you write this in a Java or C#-like language, rest assured that eventually you will run into a situation where something in this chain is null, and worst of all, the ensuing NullPointerException will not pinpoint exactly what it is. So don't write code like this.&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;But the reason I'm writing this post is to draw attention to a third disadvantage of long method call chains: They complicate mock object testing.&lt;/div&gt;&lt;br&gt;
&lt;div&gt;Let's look at a slightly less pathological example based on the following classes:&lt;/div&gt;&lt;br&gt;
&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public class CustomerController {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;private Customer customer;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public CustomerController(Customer customer) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.customer = customer;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public void postOrders() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;customer.getOrdersList().postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public class Customer {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;private OrdersList ordersList = new OrdersList();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public OrdersList getOrdersList() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return ordersList;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public void postOrders() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ordersList.postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public class OrdersList {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;private List&lt;order&gt; orders = new ArrayList&lt;order&gt;();&lt;/order&gt;&lt;/order&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public void postOrders() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (Order order : orders) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;order.post();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public class Order {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public void post() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println("Posting order");&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;br&gt;
&lt;div&gt;We have a CustomerController that controls (what else?) a Customer and triggers the posting of the customer's Orders. Forget the unlikeliness of this particular design. (In reality, the Customer, OrderList, and Order classes would probably be pure-data classes, and CustomerController would use a service class to post orders.) Let's think about how we would test CustomerController.&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;Presuming that the &lt;span class="Apple-style-span" style="font-family:courier;"&gt;Order.post()&lt;/span&gt; method ultimately makes external connections, we want to mock the &lt;span class="Apple-style-span" style="font-family:courier;"&gt;Customer.postOrders()&lt;/span&gt; method and simply verify that CustomerController calls it. If we try to write the test this way:&lt;/div&gt;&lt;br&gt;
&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;@Test&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public void testBadPostOrders() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;Customer customer = EasyMock.createMock(Customer.class);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;// Next line: NullPointerException!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;customer.getOrdersList().postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;EasyMock.expectLastCall();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;EasyMock.replay(customer);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;CustomerController controller = new CustomerController(customer);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;controller.postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;EasyMock.verify(customer);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;We should have known better than to try to call &lt;span class="Apple-style-span" style="font-family:courier;"&gt;customer.getOrdersList().postOrders()&lt;/span&gt; on an EasyMock mock. To fix this, we could add a mock OrderList, return it from an expected call to &lt;span class="Apple-style-span" style="font-family:courier;"&gt;customer.getOrderList()&lt;/span&gt; on the Customer mock, and create an&amp;nbsp;&amp;nbsp;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;orderList.postOrders()&lt;/span&gt; expectation. But doing that type of thing will rapidly become annoying. And this is a very simple example.&lt;/div&gt;&lt;br&gt;
&lt;div&gt;We can move toward a better solution by noting that in this case, &lt;i&gt;the test itself&lt;/i&gt; (which you can think of as client code of the Customer class) is not following the Law of Demeter: It is talking to the OrderList, which is not an "immediate friend". Yet, from the perspective of the test, the Customer class doesn't leave it a lot of choice.&lt;/div&gt;&lt;br&gt;
&lt;div&gt;Fortunately, we can refactor the Customer and CustomerController classes to make them more mock-testable. We can add a postOrders() helper method to the Customer class:&lt;/div&gt;&lt;br&gt;
&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public class Customer {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;private OrdersList ordersList = new OrdersList();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public void postOrders() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ordersList.postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;And we can change CustomerController to call it:&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public class CustomerController {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;private Customer customer;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public CustomerController(Customer customer) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.customer = customer;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;public void postOrders() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;customer.postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Then we can easily write a working test that uses the mock framework to verify that the controller does, in fact, post the orders:&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;@Test&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;public void testGoodPostOrders() {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;Customer customer = EasyMock.createMock(Customer.class);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;customer.postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;EasyMock.expectLastCall();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;EasyMock.replay(customer);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;CustomerController controller = new CustomerController(customer);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;controller.postOrders();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;&amp;nbsp;&amp;nbsp;EasyMock.verify(customer);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:courier;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;br&gt;
&lt;div&gt;And again, as many others have pointed out, good designs and good tests feed back into one another.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-3297547926349437393?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/3297547926349437393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=3297547926349437393' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3297547926349437393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3297547926349437393'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2011/04/law-of-demeter-makes-you-create-mock.html' title='The Law of Demeter Makes You Create Mock-Friendly Objects'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-3107015922660279870</id><published>2011-04-16T20:43:00.001-07:00</published><updated>2011-04-16T20:48:58.363-07:00</updated><title type='text'>Back to Blogging</title><content type='html'>Wow, it has been over a year since my last post. Clearly I have been very busy. But still, I really do want to be here sharing my thoughts about development and testing. So I'm going to try and get back into the swing. I have a couple of ideas for posts queued up. I am planning to get them done over the next couple of weeks, and we'll go from there.&lt;div&gt;
&lt;/div&gt;&lt;div&gt;One thing that I will do differently from now on is that whenever I have a useful link, recommendation, or brief thought to share, I will do it on Twitter. If you're interested, follow me there -- I am &lt;a href="http://twitter.com/#!/sdmcmaster"&gt;@sdmcmaster&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Until next time...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-3107015922660279870?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/3107015922660279870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=3107015922660279870' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3107015922660279870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3107015922660279870'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2011/04/back-to-blogging.html' title='Back to Blogging'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-7161370167755018204</id><published>2010-01-18T14:58:00.000-08:00</published><updated>2010-01-18T15:18:04.369-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Using Java to Download Links to Picasa Web Albums</title><content type='html'>I mentioned a couple of weeks ago that I &lt;a href="http://www.scottmcmaster365.com/2009/12/moving-to-cloud-part-1-photos.html"&gt;moved all of my digital photos to Picasa Web Albums&lt;/a&gt;.  It turns out that my wife would like access to these photos.  I uploaded the albums as "Unlisted", so the web interface provides no way to get links for sharing in bulk.  I have somewhere around 150 albums, so going into each one to grab a sharing URL would be a really tedious way to spend the afternoon on my vacation day.  I would rather write a program.  &lt;a href="http://code.google.com/apis/picasaweb/code.html"&gt;Picasa Web Albums Data API&lt;/a&gt; to the rescue.
&lt;br&gt;&lt;br&gt;
Since this was my first program using Google Data APIs, it took me longer to set up a working environment than it did to actually write the Java code.  The getting-started guide is &lt;a href="http://code.google.com/apis/gdata/articles/java_client_lib.html"&gt;here&lt;/a&gt;.  For my case, I needed as external dependencies 1) JavaMail and 2) the JavaBeansActivation Framework, both of which most open-source-Java developers already have sitting around somewhere convenient.  Then I &lt;a href="http://code.google.com/p/gdata-java-client/downloads/list"&gt;grabbed the latest Google Data Java Client libraries&lt;/a&gt;, unzipped, and put all of the jars in &lt;span style="font-family: courier new;"&gt;gdata/java/lib&lt;/span&gt; and &lt;span style="font-family: courier new;"&gt;gdata/java/deps&lt;/span&gt; on the classpath of my project.
&lt;br&gt;&lt;br&gt;
From there, the code was very straightforward.  Note the placeholders for Google username and password.
&lt;br&gt;&lt;br&gt;
&lt;pre&gt;
import java.net.URL;
import java.util.List;

import com.google.gdata.client.photos.PicasawebService;
import com.google.gdata.data.photos.GphotoEntry;
import com.google.gdata.data.photos.UserFeed;

/**
 * Short script to download links to all of my Picasa
 * Web Albums.  This includes private/public/protected
 * access levels.
 * 
 * @author Scott McMaster (smcmaster@acm.org)
 *
 */
public class DownloadPicasaWebAlbumLinks {
 private static final String USERNAME = "NOTMYREALUSERNAME";
 private static final String PASSWORD = "NOTMYREALPASSWORD";

 @SuppressWarnings("unchecked")
 public static void main(String[] args) throws Exception {
  PicasawebService myService = new PicasawebService(
    "Download Album Links");
  myService.setUserCredentials(USERNAME, PASSWORD);

  URL albumFeedUrl = new URL(
    "http://picasaweb.google.com/data/feed/api/user/" + USERNAME
      + "?kind=album");
  UserFeed albumFeed = myService.getFeed(albumFeedUrl, UserFeed.class);
  List&lt;GphotoEntry&gt; albums = albumFeed.getEntries();
  for (GphotoEntry album : albums) {
   System.out.println(album.getHtmlLink().getHref());
  }
 }
}
&lt;/pre&gt;
For "unlisted" albums, this will print out, for each album, the same link you get if you click "Link to this album" in the UI.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-7161370167755018204?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/7161370167755018204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=7161370167755018204' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7161370167755018204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7161370167755018204'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2010/01/using-java-to-download-links-to-picasa.html' title='Using Java to Download Links to Picasa Web Albums'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-7864501102918390951</id><published>2009-12-31T13:05:00.000-08:00</published><updated>2009-12-31T13:21:38.776-08:00</updated><title type='text'>Moving to the Cloud Part 2:  Email</title><content type='html'>In my last post, I mentioned my goal of moving all of my important data out of local storage and into the cloud.  First, I dealt with my photos.  My next order of business was email.
&lt;Br&gt;&lt;br&gt;
Traditionally, I used Outlook to organize my email and contacts.  So I had years' worth of messages organized into personal folders on a local hard drive.  Whenever I wanted to move to a new machine, I would export all of these as .pst files and re-import them in a new location.  That was annoying, risky, and error-prone.
&lt;Br&gt;&lt;br&gt;
Now that I am paying for all of this Google storage (as discussed in my last post), it seemed obvious that Gmail was the place to move all of my old email.  I wasn't sure off the top of my head exactly how to do that, but a quick search turned up many variations on a &lt;a href="http://www.mahalo.com/how-to-import-outlook-archives-into-gmail"&gt;simple trick&lt;/a&gt; -- enable IMAP in Gmail settings, bring the Gmail account into Outlook via IMAP, and drag-and-drop folders and messages.  Perfect!
&lt;Br&gt;&lt;br&gt;
As for my contacts, I imported them into the Google space (via Google Voice) a long time ago.  And I never did a lot of personal calendaring with Outlook.  (Online solutions are far better for that now anyway.)
&lt;Br&gt;&lt;br&gt;
Overall, I was very pleased at how easy it was to migrate away from Outlook.  There are two more things that I care about enough to want to move to the cloud -- my music and my schoolwork/teaching/research files.  I will cover those in future posts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-7864501102918390951?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/7864501102918390951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=7864501102918390951' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7864501102918390951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7864501102918390951'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/12/moving-to-cloud-part-2-email.html' title='Moving to the Cloud Part 2:  Email'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-4249458267024798470</id><published>2009-12-30T21:17:00.000-08:00</published><updated>2009-12-30T21:29:27.914-08:00</updated><title type='text'>Moving to the Cloud Part 1:  Photos</title><content type='html'>Well, it's been a long time since I've posted.  Without going into a lot of details, I've had a number of life changes in the last few months.  Hopefully I can get back into blogging a bit.
&lt;Br&gt;&lt;br&gt;
One goal I've made for 2010 is to care a lot less about what, exactly, is on the fleet of machines and disk drives sitting around my house.  That means moving content into the cloud.  I decided to start with what is most important to me, namely, my family photos.
&lt;Br&gt;&lt;br&gt;
I was a pretty early adopter of digital photography, getting my first camera in 1997.  So I have a lot of pictures.  I've used &lt;a href="http://picasa.google.com/"&gt;Picasa &lt;/a&gt;for a long time (well before Google bought it), so it seemed like a natural fit to upload everything into &lt;a href="http://picasaweb.google.com/"&gt;Picasa Web Albums&lt;/a&gt;.  And of course, there is a web API, so in the future, I can manage my photos with custom scripts and applications.
&lt;Br&gt;&lt;br&gt;
The Picasa fat client application makes the upload process incredibly easy.  (I used the beta version for Linux, which now runs via the WINE emulator.)  One thing I had to make sure to do was force it to upload the full-size images for each album, lest I lose some JPEG quality for more recent years' photos.
&lt;Br&gt;&lt;br&gt;
Another thing I had to do was upgrade my Google Account storage, because the free space wasn't going to be nearly enough.  Conveniently, there is currently &lt;a href="http://www.eye.fi/google"&gt;a deal to get a free Eye-Fi card&lt;/a&gt; with a year's worth of 200G of storage.  I went for that.  I don't really need anywhere near 200G just for my photos, but I will use some more of that space in future blog posts.  And I can't wait for my new Eye-Fi card to arrive in the mail.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-4249458267024798470?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/4249458267024798470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=4249458267024798470' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4249458267024798470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4249458267024798470'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/12/moving-to-cloud-part-1-photos.html' title='Moving to the Cloud Part 1:  Photos'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-8527478831522716468</id><published>2009-10-24T14:22:00.000-07:00</published><updated>2009-10-24T14:30:38.385-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='unit_testing'/><category scheme='http://www.blogger.com/atom/ns#' term='junit'/><title type='text'>Testing Exception Messages, JUnit 4.7 Edition</title><content type='html'>Historically, JUnit has not elegantly supported verification of expected-exception messages in unit tests.  Some time ago, I wrote &lt;a href="http://softwaredevscott.spaces.live.com/blog/cns!1A9E939F7373F3B7!524.entry"&gt;a post about techniques for handling this situation&lt;/a&gt;.  Brian Brooks (clearly a diligent web searcher) found that post and was kind enough to send me a message pointing out that the latest version of JUnit, 4.7, finally includes &lt;a href="http://github.com/KentBeck/junit/raw/23ffc6baf5768057e366e183e53f4dfa86fbb005/doc/ReleaseNotes4.7.txt"&gt;support&lt;/a&gt; for this incredibly important and common testing scenario.
&lt;br&gt;&lt;br&gt;
In honor of this exciting occasion, I wanted to post an example.  Here is the class I'm going to test:

&lt;pre&gt;
public class PositiveInteger {
 private int num;

 public PositiveInteger(int num) {
  if(num &lt;= 0) {
   throw new IllegalArgumentException("Number must be positive");
  }
  this.num = num;
 }
}
&lt;/pre&gt;

Let's test -- hmmm -- the constructor, I guess.  I created two test cases -- one which passes, and one which intentionally fails (just to show the resulting output):

&lt;pre&gt;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;


public class TestPositiveNumber {

 @Rule
 public ExpectedException thrown = ExpectedException.none();

 @Test
 public void testConstructorInvalid_PASS() {
  thrown.expect(IllegalArgumentException.class);
  thrown.expectMessage("Number must be positive");
  new PositiveInteger(-5);
 }

 /**
  * Intentionally-failing test to demonstrate the results
  * when checking an exception message.
  */
 @Test
 public void testConstructorInvalid_FAIL() {
  thrown.expect(IllegalArgumentException.class);
  thrown.expectMessage("Not gonna pass");
  new PositiveInteger(-5);
 }
}
&lt;/pre&gt;

JUnit now supports a style embodied in &lt;a href="http://www.threeriversinstitute.org/blog/?p=155"&gt;"rules"&lt;/a&gt;, which get marked with the @Rule annotation and provide a means by which the test execution behavior can be modified.  You can see that here with the "thrown" field, of type "ExpectedException".  It would be great if a) that field didn't have to be public, and b) the functionality could be implemented using a local variable, but unfortunately the Java language won't currently support such an implementation.  So a public field it is.  In a test, you can configure the rule.  Here I do that with calls to the "expect" and "expectMessage" methods.  When JUnit executes the test case, it checks the result against the rule and fails if the rule is not matched.  In the test case that does not pass, the failure takes this form:

&lt;pre&gt;
java.lang.AssertionError: 
Expected: (exception with message a string containing "Not gonna pass" and an instance of java.lang.IllegalArgumentException)
     got: &lt;java.lang.IllegalArgumentException: Number must be positive&gt;

 at org.junit.Assert.assertThat(Assert.java:778)
 at org.junit.Assert.assertThat(Assert.java:736)
&lt;/pre&gt;

Very useful.  Thanks to Kent Beck for adding this support.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-8527478831522716468?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/8527478831522716468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=8527478831522716468' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8527478831522716468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8527478831522716468'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/10/testing-exception-messages-junit-47.html' title='Testing Exception Messages, JUnit 4.7 Edition'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-4694331889085194720</id><published>2009-09-26T19:53:00.000-07:00</published><updated>2009-09-29T20:23:07.031-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>URLEncoder Demonstrates API Design Hazards</title><content type='html'>I had to use &lt;a href="http://java.sun.com/javase/6/docs/api/java/net/URLEncoder.html"&gt;java.net.URLEncoder&lt;/a&gt; yesterday.  This experience was a great reminder of how annoying this API is.  It demonstrates a couple of issues, so I thought I would comment on it here.
&lt;br&gt;&lt;br&gt;
The first thing you can see here is how important it is to get the API right the first time, lest things go downhill from there.  In its original form, URLEncoder provided only one static method which encoded a string as "application/x-www-form-urlencoded" (suitable for issuing an HTTP request). This method has the signature &lt;span style="font-family:courier new;"&gt;encode(String)&lt;/span&gt;, and it used the platform's default encoding.  Unfortunately, this is technically hazardous, because URL encodings are supposed to always be UTF-8.  Therefore, wiser heads built and used replacement classes (such as &lt;a href="http://commons.apache.org/codec/apidocs/org/apache/commons/codec/net/URLCodec.html"&gt;URLCodec from Apache Commons&lt;/a&gt;).
&lt;br&gt;&lt;br&gt;
Java 1.4 finally "fixed" this deficiency in the platform with a new &lt;span style="font-family:courier new;"&gt;encode(String,String)&lt;/span&gt; overload, the second parameter being the name of a character encoding to use.  The old &lt;span style="font-family:courier new;"&gt;encode(String)&lt;/span&gt; method is marked as deprecated.  The documentation itself states that:
&lt;span style="font-style: italic;"&gt;
Note: The  World Wide Web Consortium Recommendation states that UTF-8 should be used. Not doing so may introduce incompatibilites.&lt;/span&gt;
&lt;br&gt;&lt;br&gt;
I don't know, but I'm guessing that the powers-that-be would have &lt;span style="font-style: italic;"&gt;liked &lt;/span&gt;to make &lt;span style="font-family:courier new;"&gt;encode(String)&lt;/span&gt; do UTF-8 encoding.  But they felt constrained by the legacy decision to use the platform default, in the name of not breaking any existing code.  Never mind the substantial odds that any such existing code is &lt;span style="font-style: italic;"&gt;already broken&lt;/span&gt; in this case wherever the default encoding is &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; UTF-8.
&lt;br&gt;&lt;br&gt;
But wait, it gets worse.  The &lt;span style="font-family: courier new;"&gt;encode(String,String)&lt;/span&gt; method also throws &lt;a href="http://java.sun.com/javase/6/docs/api/java/io/UnsupportedEncodingException.html"&gt;UnsupportedEncodingException &lt;/a&gt;whenever (surprise!) the second parameter does not refer to a valid encoding.  I'm no rocket scientist, but it seems to me that if you're following the W3C recommendation and always passing UTF-8, you don't have to worry about this exception being thrown.  And yet because the exception is checked, you have no choice but to handle it and clutter up your code, perhaps with a catch block that just does &lt;span style="font-family: courier new;"&gt;assert(false)&lt;/span&gt;.
&lt;br&gt;&lt;br&gt;
This all begs the question, if the second parameter should always be "UTF-8" for some definition of "always", why should the API require a second parameter at all?  I submit that a better approach would have been to create, instead of or in addition to &lt;span style="font-family: courier new;"&gt;encode(String,String)&lt;/span&gt;, an &lt;span style="font-family:courier new;"&gt;encodeUTF8(String)&lt;/span&gt; method that &lt;span style="font-style: italic;"&gt;always &lt;/span&gt;did UTF-8 encoding without the added hassle of passing a (constant) parameter and dealing with a checked exception...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-4694331889085194720?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/4694331889085194720/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=4694331889085194720' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4694331889085194720'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4694331889085194720'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/09/urlencoder-demonstrates-api-design.html' title='URLEncoder Demonstrates API Design Hazards'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-7799624582658128050</id><published>2009-09-13T15:59:00.000-07:00</published><updated>2009-09-13T16:15:22.964-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>"How to Think About Algorithms" Book</title><content type='html'>I have been gradually making my way through "How to Think About Algorithms" by Jeff Edmonds.
&lt;Br&gt;
&lt;iframe style="WIDTH: 120px; HEIGHT: 240px" marginheight="0" src="http://rcm.amazon.com/e/cm?lt1=_blank&amp;amp;bc1=000000&amp;amp;IS2=1&amp;amp;bg1=FFFFFF&amp;amp;fc1=000000&amp;amp;lc1=0000FF&amp;amp;t=softwaredev04-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;m=amazon&amp;amp;f=ifr&amp;amp;md=10FE9736YVPPT7A0FBG2&amp;amp;asins=0521614104" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;
&lt;Br&gt;
I have recently recommended this book to several people, and I wanted to mention it here, too. Edmonds uses what I'll call a more intuitive approach to algorithms that I think would be appealing to those with an engineering mindset. Here I'm constrasting it with the texts that I've used as a student in algorithms courses -- the classic &lt;a href="http://www.amazon.com/gp/product/0768682339?ie=UTF8&amp;amp;tag=softwaredev04-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0768682339"&gt;Sedgewick &lt;/a&gt;and &lt;a href="http://www.amazon.com/gp/product/0262032937?ie=UTF8&amp;amp;tag=softwaredev04-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0262032937"&gt;CLRS &lt;/a&gt;books.
&lt;br&gt;
(Maybe more importantly in this economy, though, the subject matter and exercises in this book are a rich source of review for those preparing for tech interviews!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-7799624582658128050?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/7799624582658128050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=7799624582658128050' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7799624582658128050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7799624582658128050'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/09/how-to-think-about-algorithms-book.html' title='&quot;How to Think About Algorithms&quot; Book'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-5013389407861772483</id><published>2009-09-02T20:23:00.000-07:00</published><updated>2009-09-02T20:30:50.679-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><title type='text'>Tailing Inside Eclipse</title><content type='html'>I'm currently working on a project that writes to a log file inside my workspace when I test-execute it. Obviously it's useful to &lt;em&gt;actually view&lt;/em&gt; that log file once in awhile, but to do so inside Eclipse requires regularly F5-refreshing it in the Project Explorer. I started to wish that Eclipse would prompt me to automatically reload the file when it detected modification, like some text editors do. But then it occurred to me how convenient it would be if I could actually &lt;strong&gt;tail &lt;/strong&gt;the log from inside Eclipse itself. After all,
&lt;br&gt;&lt;br&gt;
&lt;em&gt;The less I leave the IDE,&lt;/em&gt;
&lt;em&gt;The more productive I can be.&lt;/em&gt;
&lt;br&gt;&lt;br&gt;
(That rhymes.)
&lt;br&gt;&lt;br&gt;
This seemed like an obvious enough desire that somebody would have built a plugin for it already, and sure enough, a search turned up a couple of different examples. The one that I went with is called &lt;a href="http://www.certiv.net/products/ntail.html"&gt;NTail&lt;/a&gt;. It works like a champ. It also has a few extra nice features, like the ability to highlight lines in the file based on a regular expression. If this sounds useful to you, I'd encourage you to check it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-5013389407861772483?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/5013389407861772483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=5013389407861772483' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5013389407861772483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5013389407861772483'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/09/tailing-inside-eclipse.html' title='Tailing Inside Eclipse'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-3376997056169032531</id><published>2009-08-08T17:28:00.000-07:00</published><updated>2009-08-08T18:16:48.357-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='error_handling'/><category scheme='http://www.blogger.com/atom/ns#' term='aspectj'/><title type='text'>Changing Return Values to Exceptions, Java/AspectJ Edition</title><content type='html'>In the &lt;a href="http://www.scottmcmaster365.com/2009/08/exceptions-versus-return-values.html"&gt;last couple&lt;/a&gt; &lt;a href="http://www.scottmcmaster365.com/2009/08/changing-return-values-to-exceptions-c.html"&gt;of posts&lt;/a&gt;, I've been talking about APIs that communicate errors via return values, and how to map those to exceptions.  As we saw most recently, one reasonably effective approach in C++ makes use of macros, but it requires a degree of development discipline, and the resulting code remains somewhat unsatisfactory.
&lt;br&gt;&lt;br&gt;
In this post, I will present an approach for Java that mitigates these issues.  Specifically, &lt;strong&gt;you can neatly map return values to exceptions using &lt;a href="http://www.eclipse.org/aspectj/"&gt;AspectJ&lt;/a&gt;&lt;/strong&gt;.
&lt;br&gt;&lt;br&gt;
&lt;i&gt;(Note that I am using Eclipse for this work.  It's great that AspectJ is now an Eclipse project, because it can be incredibly frustrating to use without solid IDE support.)&lt;/i&gt;
&lt;br&gt;&lt;br&gt;
I'll start with some Java math utility routines that are roughly equivalent to what I was working with in C++.  One difference is that in Java, I don't need a custom sqrt() wrapper because the &lt;a href="http://www.j2ee.me/javase/6/docs/api/java/lang/Math.html#sqrt(double)"&gt;Math.sqrt()&lt;/a&gt; method already uses a return value, Double.NaN, to indicate error.
&lt;pre&gt;
package com.scottmcmaster365.math;

public class MyMathUtils
{
  public static double radiusFromArea(double area)
  {
    return Math.sqrt(area / 3.14);
  }

  public static double circumferenceFromRadius(double radius)
  {
    return 2 * 3.14 * radius;
  }
}
&lt;/pre&gt;
I want to wire up an aspect that runs when these functions are returning, checks for NaN, and throws an exception.  With a bit of understanding of AspectJ, this is quite simple:
&lt;pre&gt;
package com.scottmcmaster365.aspects;

import com.scottmcmaster365.math.MyMathUtils;

/**
 * This aspect maps double NaN return values to runtime exceptions.
 * (For instructional purposes only.)
 * @author Scott McMaster
 *
 */
public aspect MapNaNResultToExceptionAspect
{
  pointcut mathClass() : within(MyMathUtils);
 
  pointcut mathMethods() : mathClass() &amp;&amp; execution(* *(..));
 
  after () returning (double d) : mathMethods()
  {
    if( Double.isNaN(d) )
    {
      throw new RuntimeException("Result is NaN");
    }
  }
}
&lt;/pre&gt;
Hopefully even if you're not familiar with AspectJ, that makes at least a little sense to you.  Otherwise, I suggest checking out a good tutorial.  But in a nutshell, the pointcuts pick out the methods in the MyMathUtils class, and the after advice grabs the return value and allows us to inject code at compile-time.
&lt;br&gt;&lt;br&gt;
With this aspect in effect, the client for finding the circumference given the area of a circle will throw a RuntimeException during the call to radiusFromArea():
&lt;pre&gt;
final double TESTAREA = -1.0;
double radius = MyMathUtils.radiusFromArea(TESTAREA);
double circumference = MyMathUtils.circumferenceFromRadius(radius);
System.out.println(circumference);
&lt;/pre&gt;
That's exactly what I was after:  Clean code that doesn't have to check return values explicitly or via macros.  Score one for AspectJ.  In fact, by making a very minor change to the pointcuts and advice spec, I can even fire the aspect at the Math.sqrt() call site, which (in a more complicated example) narrow the defect down even further.  I'll leave this as an exercise for the reader unless someone comments that they want to see it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-3376997056169032531?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/3376997056169032531/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=3376997056169032531' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3376997056169032531'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3376997056169032531'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/08/changing-return-values-to-exceptions.html' title='Changing Return Values to Exceptions, Java/AspectJ Edition'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-7541110452285645428</id><published>2009-08-06T19:44:00.000-07:00</published><updated>2009-08-06T20:44:34.612-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cplusplus'/><category scheme='http://www.blogger.com/atom/ns#' term='error_handling'/><title type='text'>Changing Return Values to Exceptions, C++ Edition</title><content type='html'>In my &lt;a href="http://www.scottmcmaster365.com/2009/08/exceptions-versus-return-values.html"&gt;last post&lt;/a&gt;, I discussed one reason why exceptions are superior to return values from the perspective of an API client.  Despite this, you might still run into APIs (albeit hopefully legacy ones) that heavily use status codes and magic return values to indicate errors, and it may not be within your sphere of influence to change.  A friend of mine asked me how to deal with this situation.
&lt;br&gt;&lt;br&gt;
Ideally we would like to map error results to exceptions.  &lt;strong&gt;One approach that works in C++ is to leverage the preprocessor via a "failure-checking" macro.&lt;/strong&gt;
&lt;br&gt;&lt;br&gt;
Consider my example from the last post which uses a couple of simple functions to calculate the circumference of a circle given the area.  Here is the original client code:

&lt;pre&gt;
float area = TEST_AREA;
float radius = radiusFromArea(area);
float circumference = circumferenceFromRadius(radius);
cout &lt;&lt; circumference &lt;&lt; endl;
&lt;/pre&gt;

Let's say that "radiusFromArea" uses the original version of the "mysqrt" function which returns -1 when it receives invalid input, and let's also stipulate that we cannot change the API for any of these functions.  We'd like to present an exception to our callers as soon as we detect a bad value returned from one of the functions.  We could do that with some spaghetti-ish code:

&lt;pre&gt;
float area = TEST_AREA;
float radius = radiusFromArea(area);
if( radius &lt; 0 )
{
  throw runtime_error("cannot be negative");
}
float circumference = circumferenceFromRadius(radius);
if( circumference &lt; 0 )
{
  throw runtime_error("cannot be negative");
}
cout &lt;&lt; circumference &lt;&lt; endl;
&lt;/pre&gt;

&lt;i&gt;(Ideally we would get the exception from "mysqrt" like I demonstrated last time, but recall that for the purposes of this post, we're pretending that we cannot change that function.  And yes, in this contrived situation, we could easily check the negativeness of "area", but that would defeat the instructional value of the example.)&lt;/i&gt;
&lt;br&gt;&lt;br&gt;
As far as isolating where a negative value might be coming from, that works.  But it's annoying and ugly and it takes too much typing.  That's why C/C++ programmers traditionally tend to be pretty bad about this type of error-checking.  We can clean it up considerably with a macro:

&lt;pre&gt;
#define IfNegativeThrow(x) { \
    if(x &lt; 0 ) \
      throw runtime_error("cannot be negative"); \
    }
&lt;/pre&gt;

The new client code becomes:

&lt;pre&gt;
float area = TEST_AREA;
float radius;
IfNegativeThrow(radius = radiusFromArea(area));
float circumference;
IfNegativeThrow(circumference = circumferenceFromRadius(radius));
cout &lt;&lt; circumference &lt;&lt; endl;
&lt;/pre&gt;

This is functionally equivalent to the immediately-preceding example, but it is more concise and (in my opinion) cleaner.  You can potentially improve on it by adding an extra macro parameter for the exception message.  Macros similar to IfNegativeThrow() can be found in many industrial-strength C++ codebases.  This pattern is especially prevalent in old-school COM programming, where it is often used to deal with the infamous &lt;a href="http://en.wikipedia.org/wiki/HRESULT"&gt;HRESULT&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
&lt;i&gt;(Shout-out to anybody who still does COM programming like that on a regular basis.  Last time I did, I think Bill Clinton was still President.)&lt;/i&gt;
&lt;br&gt;&lt;br&gt;
There remain some significant drawbacks here.  Doing those assignments inside macros still leaves you with less-than-aesthetic code.  You still have to maintain discipline about using the macros.  And if you are in a language that doesn't support the preprocessor, things get more difficult and/or annoying.  (You may have to implement IfNegativeThrow() as a full-fledged method, for example, and incur the requisite overhead.)  In a later post, I'll address these issues.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-7541110452285645428?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/7541110452285645428/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=7541110452285645428' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7541110452285645428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7541110452285645428'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/08/changing-return-values-to-exceptions-c.html' title='Changing Return Values to Exceptions, C++ Edition'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-4593022337677920999</id><published>2009-08-04T21:06:00.000-07:00</published><updated>2009-08-04T22:05:44.614-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='error_handling'/><title type='text'>Exceptions Versus Return Values</title><content type='html'>I'm alarmed by the number of programmers I've been talking to lately -- not just C/C++ programmers, but also Java and C# programmers -- who are inclined to forgo the throwing of exceptions and instead use magic return values and/or status codes to indicate an API failure.
&lt;br&gt;&lt;br&gt;
Consequenty I sat down to write a blog post about this. As a general rule, before I write something, I check to see if someone else has done it already, and sure enough, a number of people have taken on this issue competently. So instead of imitating their work, I wanted to point you to the best example, namely &lt;a href="http://nedbatchelder.com/text/exceptions-vs-status.html"&gt;Ned Batchelder's&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
One slight addition I wanted to make was to point out that Ned's article is primarily focused on the use of return values as &lt;i&gt;status codes&lt;/i&gt;. I'm actually more concerned about &lt;i&gt;overloading the return value with a sentinel value&lt;/i&gt; that is intended to indicate failure (a practice that Ned covers in the section on "Valuable channels"). In my opinion, this is far more insidious than the correct application of the status code idiom because it can time- and space-shift errors far from where they originate, which can unnecessarily complicate debugging.
&lt;br&gt;&lt;br&gt;
Take the following square-root function as an example. Let's use C++.

&lt;pre&gt;
float mysqrt(float n)
{
  if( n &lt; 0 )
  {
    return -1.0;
  }
  return sqrt(n);
}
&lt;/pre&gt;

Here, -1 is being used as a sentinel value to indicate what any high school math student knows, namely, that you can't take the square root of a negative number.  Consider what a client of this function must go through.  Say I'm starting with the area of a circle, and I want to calculate the circumference using a couple of convenient helper methods:

&lt;pre&gt;
float radiusFromArea(float area)
{
  return mysqrt(area / 3.14);
}

float circumferenceFromRadius(float radius)
{
  return 2 * 3.14 * radius;
}
&lt;/pre&gt;

Here is the client code:

&lt;pre&gt;
float area = TEST_AREA;
float radius = radiusFromArea(area);
float circumference = circumferenceFromRadius(radius);
cout &lt;&lt; circumference &lt;&lt; endl;
&lt;/pre&gt;

If TEST_AREA is negative, we notice that the circumference is whacked (-6.28) when we are all done.  That is, hopefully we notice and don't propagate the bad value forward, but I'll give the author of this code the benefit of the doubt for now.  Even so, we still have no idea if the error is in radiusFromArea, circumferenceFromRadius, or some unknown function that one of them calls.

Now condider what happens if mysqrt is written to throw an exception rather than return a "magic" error value:

&lt;pre&gt;
float mysqrt(float n)
{
  if( n &lt; 0 )
  {
    throw invalid_argument("can't take sqrt of negative number");
  }
  return sqrt(n);
}
&lt;/pre&gt;

When &lt;em&gt;this &lt;/em&gt;code runs on a negative area for input, you see the error &lt;em&gt;immediately &lt;/em&gt;in the form of an unhandled exception, with a stack trace pointing right to where mysqrt is called with the bogus value.  This is much preferable for debugging, even in this simplistic example.

Hopefully this will help convince you that exceptions are superior to the alternatives.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-4593022337677920999?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/4593022337677920999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=4593022337677920999' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4593022337677920999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4593022337677920999'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/08/exceptions-versus-return-values.html' title='Exceptions Versus Return Values'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-8696182563686178035</id><published>2009-06-24T19:56:00.000-07:00</published><updated>2009-06-24T20:04:28.886-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='aws'/><title type='text'>Download Eclipse 3.5 Galileo With Amazon S3 and CloudFront</title><content type='html'>One piece of exciting news today is that &lt;a href="http://www.eclipse.org/"&gt;Eclipse 3.5 (Galileo)&lt;/a&gt; is available. But the best part is that &lt;a href="http://aws.amazon.com/solutions/featured-partners/eclipse-aws/"&gt;you can download it via Amazon S3 and CloudFront&lt;/a&gt;. As of earlier today at least, the "old-fashioned" mirrors were dragging, but the S3 download was extremely snappy.
&lt;br&gt;&lt;br&gt;
Unfortunately, the S3 link isn't very well-advertised on the download page, so I wanted to mention it here. When you get to the download page for the distro you want, look down the page for "Download from Amazon Web Services". I bet you'll be impressed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-8696182563686178035?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/8696182563686178035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=8696182563686178035' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8696182563686178035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8696182563686178035'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/06/download-eclipse-35-galileo-with-amazon.html' title='Download Eclipse 3.5 Galileo With Amazon S3 and CloudFront'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-1432662388770701903</id><published>2009-06-20T10:23:00.000-07:00</published><updated>2009-06-20T10:34:20.725-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web_services'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='soa'/><title type='text'>Mock Responses with Optional Elements in soapUI</title><content type='html'>&lt;a href="http://www.soapui.org/"&gt;SoapUI&lt;/a&gt; will probably always be one of my favorite development and testing tools.  Recently I had the opportunity to explore its &lt;a href="http://www.soapui.org/gettingstarted/mocking.html"&gt;web service simulation and mocking capabilities&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
I needed to create some fairly-detailed mock responses for a service where pretty much every element in schema for the response was optional.  By default, soapUI does not add optional elements to the mock response XML.  This was causing me a significant amount of pain.  I noticed in the Preferences, WSDL Settings, that there was an "Include Optional" option that says "always include optional schema elements when creating requests":

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7rL00WKmJ1k/Sj0dMbFzMII/AAAAAAAAACA/SB6szGcLCHQ/s1600-h/SoapUIPrefs.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 475px; height: 327px;" src="http://4.bp.blogspot.com/_7rL00WKmJ1k/Sj0dMbFzMII/AAAAAAAAACA/SB6szGcLCHQ/s320/SoapUIPrefs.png" alt="" id="BLOGGER_PHOTO_ID_5349464031653736578" border="0" /&gt;&lt;/a&gt;
I discovered that this option not only applies to creating requests, but also to creating mock responses.  Problem solved.  But I couldn't find this documented anywhere, so I wanted to point it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-1432662388770701903?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/1432662388770701903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=1432662388770701903' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1432662388770701903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1432662388770701903'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/06/mock-responses-with-optional-elements.html' title='Mock Responses with Optional Elements in soapUI'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_7rL00WKmJ1k/Sj0dMbFzMII/AAAAAAAAACA/SB6szGcLCHQ/s72-c/SoapUIPrefs.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-8860687284081845214</id><published>2009-06-17T21:42:00.000-07:00</published><updated>2009-06-18T06:35:50.138-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>An Example of Why You Shouldn't Test in Production</title><content type='html'>If you ever need a concrete example of why it is worthwhile to set up a proper testing environment for your online or data-based applications, consider &lt;a href="http://www.chicagotribune.com/business/autocorner/chi-getting-around_15jun15,0,1875650.column"&gt;this case&lt;/a&gt;. According to the Chicago Tribune, Illinois resident Tom &lt;span id="SPELLING_ERROR_0" class="blsp-spelling-error"&gt;Feddor&lt;/span&gt;, &lt;span id="SPELLING_ERROR_1" class="blsp-spelling-error"&gt;pround&lt;/span&gt; owner of license plate number of "0" has received some 170 bogus parking tickets because

&lt;blockquote&gt;&lt;p&gt;A glitch occurred at the Chicago Department of Revenue involving &lt;span id="SPELLING_ERROR_2" class="blsp-spelling-error"&gt;Feddor's&lt;/span&gt; 0
plates being used during tests of ticketing equipment.&lt;/p&gt;&lt;/blockquote&gt;
The article continues:

&lt;blockquote&gt;&lt;p&gt;It turned out that some city parking-enforcement aides punched in 0 when testing
their electronic ticket-issuing devices, Revenue Department spokesman Ed
Walsh said. Officials weren't aware there was a 0 plate or that &lt;span id="SPELLING_ERROR_3" class="blsp-spelling-error"&gt;Feddor&lt;/span&gt; was
receiving tickets, Walsh said in response to the Tribune inquiry.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;But thank goodness:&lt;/p&gt;&lt;blockquote&gt;"But we are taking steps to rectify the situation so in the future an actual
registered plate number will not be used to do the testing," Walsh said.&lt;/blockquote&gt;&lt;p&gt;Before you laugh too &lt;span id="SPELLING_ERROR_4" class="blsp-spelling-error"&gt;hard&lt;/span&gt;, be honest: How many of you have run "test" transactions through your live/production systems exactly in this manner? And what precautions did you have in place?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-8860687284081845214?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/8860687284081845214/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=8860687284081845214' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8860687284081845214'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8860687284081845214'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/06/example-of-why-you-shouldnt-test-in.html' title='An Example of Why You Shouldn&apos;t Test in Production'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-2232139532520194794</id><published>2009-06-11T11:59:00.000-07:00</published><updated>2009-06-11T15:35:11.554-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Why We Automate Tests</title><content type='html'>James Whittaker, who is now blogging at the &lt;a href="http://googletesting.blogspot.com/"&gt;Google Testing Blog&lt;/a&gt;, talked a bit about manual versus automated testing.  First he implied that testers are unjustifiably infatuated with their test automation frameworks:
&lt;blockquote&gt;Too often we are solution focused and marry ourselves to our automation rather than the product we are testing. Whenever I see testers that seem more vested in their automation than in the product they are shipping I worry the pendulum has swung too far.&lt;/blockquote&gt;He also tried to relate this question back to good test design.
&lt;blockquote&gt;Let's shift the debate to good test design.  Whether those tests ultimately end up being executed manually or via a tool is a moot point, let's just make them good ones that solve real testing problems.&lt;/blockquote&gt;I'm all for good test design and solving real testing problems, but how tests are run is &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; a moot point, and I'd like to give all of those "solution-focused" testers a little more credit than that.  Let me start with some simple logic:

&lt;ol&gt;&lt;li&gt;Any series of steps that you perform more than once on a computer should be automated if at all possible.
&lt;/li&gt;&lt;li&gt;A test case is (at least in some sense) a series of steps that you perform on a computer.&lt;/li&gt;&lt;li&gt;You want to run your test cases more than once (i.e. to perform &lt;a href="http://en.wikipedia.org/wiki/Regression_testing"&gt;regression testing&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;Therefore, you should automate your test cases.&lt;/li&gt;&lt;/ol&gt;Occasionally, I'll meet an engineer that seems satisfied to autonomically do the same things over and over, day after day, but for the most part, testers understand my line of reasoning better than most people.
&lt;br&gt;&lt;br&gt;
If automating a test case was as simple as running that test case in the first place, there would be nothing more to say, and this post would be finished.  Of course, that's not reality, and therein lies the rub.  But I would argue that you're still better off automating far more often than not.  Automation yields significant  benefits to both cost and quality.
&lt;br&gt;&lt;br&gt;
Imagine that you design a test case and execute it the first time for a cost of &lt;span style="font-style: italic;"&gt;x&lt;/span&gt;.  In my experience, if a test case is &lt;span style="font-style: italic;"&gt;ever &lt;/span&gt;going to detect a bug, there is a &gt;90% chance that will occur the first time you run it.  But if it &lt;span style="font-style: italic;"&gt;really&lt;/span&gt; is a good test case like James is arguing for, you want to run it again and again, ideally with each new build of the software to look for regressions and related bugs.
&lt;br&gt;&lt;br&gt;
Let's say that the cost to automate a test case is 10&lt;span style="font-style: italic;"&gt;x&lt;/span&gt; what it takes to run it manually.  If you only expect to run that test case five times over the life of the software, you should stick with manual testing.  But if you expect to run it more than ten times over the life of the software, it is in your long-term financial interest to automate it.
&lt;br&gt;&lt;br&gt;
Now let's say that you &lt;span style="font-style: italic;"&gt;do &lt;/span&gt;expect to run the test case ten times, and you decide to forgo automation.  If you want achieve the same quality assurances with your testing, you are committing to nine additional manual executions of that test.  This is where quality risks start to creep into the process.  How likely is it that you will &lt;span style="font-style: italic;"&gt;really &lt;/span&gt;get all nine of those executions in given your busy schedule?  How timely will they be?  Will you perform them consistently and correctly, or will you make some mistakes and miss some bugs?  Automation solves those problems.
&lt;br&gt;&lt;br&gt;
Finally, what if you can build a tool that drives the cost differential between manual and automated testing from 10&lt;span style="font-style: italic;"&gt;x&lt;/span&gt; down to 5&lt;span style="font-style: italic;"&gt;x&lt;/span&gt;?  And you can leverage that tool over the long-term not for one but rather &lt;span style="font-style: italic;"&gt;n &lt;/span&gt;test cases?  Granted, I am ignoring a couple of important factors, like the cost of automated-test-case maintenance and the magnitude of &lt;span style="font-style: italic;"&gt;x &lt;/span&gt;relative to the release schedule.  But I submit that the gist of the reasoning still holds.  In many cases, we &lt;span style="font-style: italic;"&gt;can &lt;/span&gt;build tools that work this well or better, maybe even for less than the 5&lt;span style="font-style: italic;"&gt;x &lt;/span&gt;cost in this example.  &lt;span style="font-style: italic;"&gt;That &lt;/span&gt;is why testers work so hard at building test automation tools.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-2232139532520194794?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/2232139532520194794/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=2232139532520194794' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/2232139532520194794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/2232139532520194794'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/06/why-we-automate-tests.html' title='Why We Automate Tests'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-7734050853491269985</id><published>2009-05-24T15:43:00.000-07:00</published><updated>2009-05-24T16:05:28.030-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='office'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Streaming Office 2007 Documents Requires New Content Types</title><content type='html'>Streaming out content that opens in Microsoft Office applications is a fairly common feature in web applications. This involves setting a Content-Type header to the appropriate MIME type. The new XML/ZIP-based file formats in Office 2007 bring with them new MIME types. There is a complete list of those &lt;a href="http://blogs.msdn.com/vsofficedeveloper/pages/Office-2007-Open-XML-MIME-Types.aspx"&gt;here&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
Another common technique is to stream XML or HTML and basically lie about the content type to get data presented in Word or (especially) Excel. After all, generating true native Office-formatted files on a web server generally involves third-party components, and depending on your platform and specific needs, those can get expensive. But Excel 2007 creates a gotcha for this use case called "&lt;a href="http://blogs.msdn.com/vsofficedeveloper/pages/Excel-2007-Extension-Warning.aspx"&gt;extension hardening&lt;/a&gt;", where Excel actually verifies that a file format matches the extension it claims to be before opening it, and if not, gives an annoying, scary prompt to the user.
&lt;br&gt;&lt;br&gt;
I first experienced this last week, with Excel 2007 trying to open an Excel 2007 document from a web site which set the content type to "application/vnd.ms-excel" -- i.e. an Excel 2007 document claiming to be an Excel 2003 document. This is bothersome, but at least it's easy to work around by changing the content type to "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet". (That's a mouthful.) It doesn't do much for the HTML case, however, as described in the above link. No good workaround...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-7734050853491269985?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/7734050853491269985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=7734050853491269985' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7734050853491269985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/7734050853491269985'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/05/streaming-office-2007-documents.html' title='Streaming Office 2007 Documents Requires New Content Types'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-5719327272626963333</id><published>2009-04-12T21:28:00.000-07:00</published><updated>2009-04-12T21:36:19.075-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><title type='text'>Latest Google Toolbar Globally Hijacks Ctrl-Space, Causes Massive Eclipse Irritation</title><content type='html'>I installed the latest version of the Google Toolbar a couple of days ago. Shortly thereafter, I did a bit of work in Eclipse and noticed that Ctrl-Space no longer invoked Eclipse Content Assist, but rather brought up a "helpful" Google search window. I have since learned that Google calls this the "quick search box", and the behavior is system-wide, at least on Windows. More importantly, I discovered how to turn it off: There is an option for it in the Google Toolbar Options dialog as shown below.

 &lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 373px; DISPLAY: block; HEIGHT: 201px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5324030008236885122" border="0" alt="" src="http://4.bp.blogspot.com/_7rL00WKmJ1k/SeLBFaZ6xII/AAAAAAAAAB4/KtipgfcC-fE/s320/googletoolbaroptions.JPG" /&gt;

Certainly they could have come up with something better than Ctrl-Space, couldn't they?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-5719327272626963333?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/5719327272626963333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=5719327272626963333' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5719327272626963333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5719327272626963333'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/04/latest-google-toolbar-globally-hijacks.html' title='Latest Google Toolbar Globally Hijacks Ctrl-Space, Causes Massive Eclipse Irritation'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_7rL00WKmJ1k/SeLBFaZ6xII/AAAAAAAAAB4/KtipgfcC-fE/s72-c/googletoolbaroptions.JPG' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-1965941558721158783</id><published>2009-03-23T20:34:00.000-07:00</published><updated>2009-03-23T20:51:02.232-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Perils of (Ab)using the java.ext.dirs Property</title><content type='html'>I frequently use the java.ext.dirs system property as a shortcut for setting the classpath for a command-line Java program, i.e.:

&lt;br&gt;&lt;br&gt;

&lt;span style="font-family:courier new;"&gt;java -Djava.ext.dirs=lib com.scottmcmaster365.MyMainProgram&lt;/span&gt;

&lt;br&gt;&lt;br&gt;

No, this isn't great for production use, but for quick-and-dirty testing, it's much more convenient than creating a launcher batch file / shell script or manually typing a ginormous claspath.

&lt;br&gt;&lt;br&gt;

Well, this practice jumped up and bit me the other day.  After the requisite amount of pain and suffering, I observed that setting java.ext.dirs does not extend but rather &lt;span style="font-style: italic;"&gt;overrides &lt;/span&gt;the &lt;span style="font-weight: bold;"&gt;real &lt;/span&gt;optional-extension location (at least with Sun's JVM), as sort-of-alluded-to &lt;a href="http://java.sun.com/j2se/1.4.2/docs/guide/extensions/spec.html"&gt;here&lt;/a&gt;.  As a consequence in this situation, Java will, at runtime, lose track of your optional packages.  In my case, this manifested itself as an inability to load the &lt;a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html"&gt;JCA crypto providers&lt;/a&gt; (i.e. &lt;a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassNotFoundException.html"&gt;ClassNotFoundException&lt;/a&gt;).

&lt;br&gt;&lt;br&gt;

If you're not using any of the standard optional packages, setting your own extensions path doesn't seem to matter.  But I guess the moral of the story is that you shouldn't count on the extension mechanism to stand in for the classpath mechanism, even in all expedient situations.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-1965941558721158783?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/1965941558721158783/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=1965941558721158783' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1965941558721158783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1965941558721158783'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/03/perils-of-abusing-javaextdirs-property.html' title='Perils of (Ab)using the java.ext.dirs Property'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-5843579699001821461</id><published>2009-03-11T21:03:00.000-07:00</published><updated>2009-03-11T21:40:29.227-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='openarchitectureware'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 4: Generating Java Classes</title><content type='html'>In the fourth part of this series, I'm finally read to generate strongly-typed wrappers for Java .properties files.  If you would like to review how I got to this point, check out the earlier installments &lt;a href="http://www.scottmcmaster365.com/2009/02/generating-strongly-typed-properties.html"&gt;here&lt;/a&gt;, &lt;a href="http://www.scottmcmaster365.com/2009/03/generating-strongly-typed-properties_01.html"&gt;here&lt;/a&gt;, and &lt;a href="http://www.scottmcmaster365.com/2009/03/generating-strongly-typed-properties_10.html"&gt;here&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
First, a little bit about my code generation philosophy.  I prefer to only generate specializations -- the things that will actually vary from model to model -- when possible.  Inheritance works well for this:  I can put common functionality in base classes and generate subclasses.  This cuts down on the amount of code that I have to work with in the template.  As an added bonus, the base class can be tested (and perhaps even used) independently of the code generation process.
&lt;br&gt;&lt;br&gt;
With that in mind, I will start by creating base classes.  Recall that my PropertySet is defined as supporting two different "load types" -- resource bundle and classloader resource.  That means two base classes, which I will call ResourceBundleProperties and ClassLoaderResourceProperties.  Since my goal is to demonstrate openArchitectureWare, model-driven development, and code generation, and not to write the be-all-and-end-all of .properties file loaders, this code won't support everything you can imagine.  Generally speaking, I'm following the tried-and-true approaches described &lt;a href="http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html?page=2"&gt;here&lt;/a&gt;.
&lt;br&gt;
&lt;h2&gt;ResourceBundleProperties.java&lt;/h2&gt;
&lt;blockquote&gt;
&lt;pre&gt;
package com.scottmcmaster365.properties;

import java.util.Properties;

/**
 * Load a properties file from a resource bundle
 * following the approach described here:
 *  http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html?page=2
 * @author scottmcm
 *
 */
public class ResourceBundleProperties
{
 protected java.util.Properties properties;
 
 /**
  * Load the properties from the resource bundle with the given name.
  * @param name
  */
 public ResourceBundleProperties(String name)
 {
  ClassLoader loader = Thread.currentThread ().getContextClassLoader();

  final java.util.ResourceBundle rb = java.util.ResourceBundle
    .getBundle(name, java.util.Locale.getDefault(), loader);

  properties = new java.util.Properties();
  for (java.util.Enumeration&amp;lt;String&amp;gt; keys = rb.getKeys(); keys
    .hasMoreElements();) {
   final String key = (String) keys.nextElement();
   final String value = rb.getString(key);

   properties.put(key, value);
  } 
  
  if (properties == null) {
   throw new IllegalArgumentException("Could not load " + name + " as resource bundle");
  }
 }
 
 public Properties getProperties() {
  return properties;
 }
}

&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;h2&gt;ClassLoaderResourceProperties.java&lt;/h2&gt;
&lt;blockquote&gt;
&lt;pre&gt;
package com.scottmcmaster365.properties;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * Load a properties file from a resource bundle
 * following the approach described here:
 *  http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html?page=2
 * @author scottmcm
 *
 */
public class ClassLoaderResourceProperties
{
 protected java.util.Properties properties;
 
 private static final String SUFFIX = ".properties";

 /**
  * Load the properties from the resource bundle with the given name.
  * @param name
  * @throws IOException 
  */
 public ClassLoaderResourceProperties(String name) throws IOException
 {
  ClassLoader loader = Thread.currentThread ().getContextClassLoader();

  InputStream in = null;
  try {
   if (!name.endsWith(SUFFIX)) {
    name = name.concat(SUFFIX);
   }

   in = loader.getResourceAsStream(name);
   if (in != null) {
    properties = new java.util.Properties();
    properties.load(in);
   }
  } finally {
   if (in != null)
    try {
     in.close();
    } catch (Throwable ignore) {
    }
  }
  
  if (properties == null) {
   throw new IllegalArgumentException("Could not load " + name + " as classloader resource");
  }
 }
 
 public Properties getProperties() {
  return properties;
 }
}

&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;br&gt;
Next I create a code generation template (an .xpt file, written in the oAW Xpand template language) to create a subclass based on the information in PropertySet models.
&lt;br&gt;
&lt;h2&gt;JavaWrapperTemplate.xpt&lt;/h2&gt;
&lt;blockquote&gt;
&lt;pre&gt;
«IMPORT metamodel»

«DEFINE javaClass FOR properties::PropertySet»
 «FILE Package.replaceAll("\\.", "/") + "/" + Name + ".java"»
  package «Package»;
  
  /**
   * Load a properties file based on the approach described here:
   *   http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html?page=2
   */
  public class «Name-»
  «IF LoadAs == properties::LoadType::CLASSLOADER_RESOURCE-»
   extends com.scottmcmaster365.properties.ClassLoaderResourceProperties
  «ELSE-»
   extends com.scottmcmaster365.properties.ResourceBundleProperties
  «ENDIF-»
  {
   public «Name»() 
    «IF LoadAs == properties::LoadType::CLASSLOADER_RESOURCE»
     throws java.io.IOException
    «ENDIF»
   {
    super("«ResourceName»");
   }
   
   «EXPAND property FOREACH Properties»
   «EXPAND propertyWithDefault FOREACH Properties»
  }
 «ENDFILE»
«ENDDEFINE»

«DEFINE property FOR properties::Property»
 public «Type» get«Name»() {
  «IF Type == properties::PropertyType::Integer»
   return Integer.parseInt(properties.getProperty("«Name»"));
  «ELSE»
   return properties.getProperty("«Name»");
  «ENDIF»
 }
«ENDDEFINE»

«DEFINE propertyWithDefault FOR properties::Property»
 public «Type» get«Name»(String defaultValue) {
  «IF Type == properties::PropertyType::Integer»
   return Integer.parseInt(properties.getProperty("«Name»", defaultValue));
  «ELSE»
   return properties.getProperty("«Name»", defaultValue);
  «ENDIF»
 }
«ENDDEFINE»
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;br&gt;
While not quite as simple as the template I presented last time to generate sample .properties file, this is still pretty straightforward.  Notice how I determine which base class to extend using the value of LoadAs defined in the input model.  The last two Xpand templates for Property generate overloaded getProperty() methods, with and without the default value.
&lt;br&gt;&lt;br&gt;
Before I can use this template, I need to wire it into my oAW workflow.  So I make the following addition to generator.oaw, right after checking the model with the .chk file:
&lt;br&gt;
&lt;blockquote&gt;
&lt;pre&gt;
&amp;lt;!--  generate code --&amp;gt;
&amp;lt;component class="org.openarchitectureware.xpand2.Generator"&amp;gt;
 &amp;lt;metaModel idRef="mm"/&amp;gt;
 &amp;lt;expand
  value="template::JavaWrapperTemplate::javaClass FOR model" /&amp;gt;
 &amp;lt;outlet path="${src-gen}" &amp;gt;
  &amp;lt;postprocessor class="org.openarchitectureware.xpand2.output.JavaBeautifier" /&amp;gt;
 &amp;lt;/outlet&amp;gt;
&amp;lt;/component&amp;gt;

&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;br&gt;
Now when I invoke generator.oaw as an oAW workflow using my sample PropertySet model, I get the following code generated into my project's src-gen directory:
&lt;br&gt;
&lt;h2&gt;MyProperties.java&lt;/h2&gt;
&lt;blockquote&gt;
&lt;pre&gt;
package com.scottmcmaster365.propertiessample;

/**
 * Load a properties file based on the approach described here:
 *   http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html?page=2
 */
public class MyProperties
  extends
   com.scottmcmaster365.properties.ClassLoaderResourceProperties {
 public MyProperties()

 throws java.io.IOException

 {
  super("MyProperties.properties");
 }

 public Integer getMyIntegerProperty() {

  return Integer.parseInt(properties.getProperty("MyIntegerProperty"));

 }

 public String getMyStringProperty() {

  return properties.getProperty("MyStringProperty");

 }

 public Integer getMyIntegerProperty(String defaultValue) {

  return Integer.parseInt(properties.getProperty("MyIntegerProperty",
    defaultValue));

 }

 public String getMyStringProperty(String defaultValue) {

  return properties.getProperty("MyStringProperty", defaultValue);

 }

}
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;br&gt;
The benefit of having a strongly-typed wrapper can be seen in this unit test example:
&lt;blockquote&gt;
&lt;pre&gt;
@Test
public void testLoadMyProperties() throws IOException
{
 MyProperties properties = new MyProperties();
 assertEquals(2, properties.getMyIntegerProperty());
 assertEquals("hello world", properties.getMyStringProperty());
}

&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;br&gt;
And that's just about it.  If I want to use my new properties wrapper in a real project, I will need to pull in all of the files from this project and add some sort of "generate" target to my Ant build which uses the oAW Ant task to invoke generator.oaw.
&lt;br&gt;&lt;br&gt;
I should point out that there is a notable overhead involved in spinning up the oAW code generation pipeline, and if you have to do it multiple times per build, your build process can slow down considerably.  It is therefore beneficial to consolidate into as few model files (.xmi's) as possible.  So a possible enhancement to what I have done here would be to metamodel a class which is a set of PropertySets and then enhance my Xpand templates to generate multiple files, so that I can cram more than one PropertySet into a model.
&lt;br&gt;&lt;br&gt;
I hope that after reading this series, you're ready to run out and EMF, oAW, modeling, and code generation.  If you do, please share your experiences with me, because I'm always looking out for new techniques and best practices.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-5843579699001821461?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/5843579699001821461/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=5843579699001821461' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5843579699001821461'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5843579699001821461'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/03/generating-strongly-typed-properties_11.html' title='Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 4: Generating Java Classes'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-6983549412343631262</id><published>2009-03-10T19:21:00.000-07:00</published><updated>2009-03-10T21:06:42.689-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='openarchitectureware'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 3: Checks, Workflow, and a Template</title><content type='html'>&lt;a href="http://www.scottmcmaster365.com/2009/03/generating-strongly-typed-properties_01.html"&gt;Last time&lt;/a&gt; I created a sample model using the Properties metamodel that I built in the &lt;a href="http://www.scottmcmaster365.com/2009/02/generating-strongly-typed-properties.html"&gt;first post&lt;/a&gt; of this series.  Now I'm going to put it all together and actually generate something with &lt;a href="http://www.openarchitectureware.org/"&gt;openArchitectureWare&lt;/a&gt; -- not Java code just yet, but rather, a sample .properties file which can be customized and loaded using the Java code that I'll create in the next entry.
&lt;br&gt;&lt;br&gt;
&lt;span style="font-weight: bold;font-size:130%;" &gt;Checks&lt;/span&gt;&lt;br&gt;
openArchitectureWare provides &lt;a href="http://www.openarchitectureware.org/pub/documentation/4.3.1/html/contents/core_reference.html#Check_language"&gt;&lt;span style="font-style: italic;"&gt;Check &lt;/span&gt;constraints&lt;/a&gt; for validating models.  For the Properties scheme that I have designed, it makes sense to require models to conform to three constraints:
&lt;ol&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;PropertySet must have a name.&lt;/span&gt;  I will use this to generate the Java class name.
&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;PropertySet must have a resource name. &lt;/span&gt; This will tell me what file to load at runtime.&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;Properties must have a name.&lt;/span&gt;  This one is pretty obvious.&lt;/li&gt;&lt;/ol&gt;Here is how to encode these rules in the &lt;span style="font-style: italic;"&gt;Check &lt;/span&gt;language (file &lt;span style="font-style: italic;"&gt;Checks.chk&lt;/span&gt;):
&lt;pre&gt;
import Properties;

context properties::PropertySet ERROR "PropertySet must have a name" :
Name != null &amp;amp;&amp;amp; Name.length &gt; 0;

context properties::PropertySet ERROR "PropertySet must have a resource name" :
ResourceName != null &amp;amp;&amp;amp; ResourceName.length &gt; 0;
&lt;br&gt;&lt;br&gt;
context properties::Property ERROR "Properties must have a name" :
Name != null &amp;amp;&amp;amp; Name.length &gt; 0;
&lt;/pre&gt;The "import" line pulls in the Properties metamodel.  Marking these constraints as "ERROR" stops oAW processing with the specified message when such a condition is encountered.  The rest of the code is pretty intuitive.  Later, when I talk about oAW Workflows, you'll see how this gets wired into the generation process.
&lt;br&gt;&lt;br&gt;
&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Simple Template&lt;/span&gt;&lt;br&gt;
&lt;/span&gt;It's time to write an oAW template to generate a sample .properties file.  This is done with the oAW &lt;a href="http://www.openarchitectureware.org/pub/documentation/4.3.1/html/contents/core_reference.html#xpand_reference_introduction"&gt;Xpand language&lt;/a&gt;, whose most visible feature is the cute little «chevrons» that mark off the directives.  I won't attempt to detail the features of Xpand, but rather point you to the documentation.  Suffice to say that it's very easy to get started.  Here is a template (PropertiesFileTemplate.xpt) that generates a .properties file from a model:
&lt;pre&gt;
«IMPORT metamodel»

«DEFINE propertiesFile FOR properties::PropertySet»
«FILE ResourceName + ".SAMPLE"»
«EXPAND property FOREACH Properties-»
«ENDFILE»
«ENDDEFINE»

«DEFINE property FOR properties::Property»
«Name» = «DefaultValue-»
«ENDDEFINE»
&lt;/pre&gt;When I finally run this in the next section, it will generate a file in .properties format with each property in the model.  The file is named based on the value of the ResourceName attribute of the PropertySet.  It is suffixed with ".SAMPLE" because presumably it would be customized in a real application.  In my next post, I'll use Xpand to achieve the ultimate goal of this project, namely, to generate some Java code to load and wrap these properties.  First, though, I need to tie everything together so that the oAW generator can run.
&lt;br&gt;&lt;br&gt;
&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Workflow&lt;/span&gt;&lt;/span&gt;&lt;br&gt;
"Workflows" are essentially build scripts for openArchitectureWare-based artifacts.  They generally end with an extention of ".oaw", and they can be launched directly from Eclipse in an openArchitectureWare project using a "Run As | openArchitectureWare Workflow" context menu item.  There are a handful of things that you can do in a workflow, but in my experience, they tend to be pretty boilerplate.  Here is one (generator.oaw) that runs Checks and invokes the generator on the PropertiesFileTemplate for our model:
&lt;blockquote&gt;
&lt;pre&gt;
&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;workflow&amp;gt;
 &amp;lt;property name="model" value="PropertiesGenerator/src/SamplePropertySet.xmi" /&amp;gt;
 &amp;lt;property name="src-gen" value="src-gen" /&amp;gt;
 
 &amp;lt;!-- set up EMF for standalone execution --&amp;gt;
 &amp;lt;bean class="org.eclipse.mwe.emf.StandaloneSetup" &amp;gt;
  &amp;lt;platformUri value=".."/&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;!-- load model and store it in slot 'model' --&amp;gt;
 &amp;lt;component class="org.eclipse.mwe.emf.Reader"&amp;gt;
  &amp;lt;uri value="platform:/resource/${model}" /&amp;gt;
  &amp;lt;modelSlot value="model" /&amp;gt;
 &amp;lt;/component&amp;gt;

 &amp;lt;!-- check model --&amp;gt;
 &amp;lt;component class="oaw.check.CheckComponent"&amp;gt;
  &amp;lt;metaModel id="mm"
   class="org.eclipse.m2t.type.emf.EmfRegistryMetaModel"/&amp;gt;
  &amp;lt;checkFile value="metamodel::Checks" /&amp;gt;
  &amp;lt;emfAllChildrenSlot value="model" /&amp;gt;
 &amp;lt;/component&amp;gt;

 &amp;lt;!--  generate sample .properties file --&amp;gt;
 &amp;lt;component class="org.openarchitectureware.xpand2.Generator"&amp;gt;
  &amp;lt;metaModel idRef="mm"/&amp;gt;
  &amp;lt;expand
   value="template::PropertiesFileTemplate::propertiesFile FOR model" /&amp;gt;
  &amp;lt;outlet path="." /&amp;gt;
 &amp;lt;/component&amp;gt;
&amp;lt;/workflow&amp;gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
In a more serious implementation, the execution of the .oaw file would be integrated in an Ant build using oAW's custom Ant task.  For this project, I'll just use the oAW Eclipse runner.
&lt;br&gt;&lt;br&gt;
The model to work with is declared as a property at the top of the file.  The key sections are the last two -- "check model" and "generate sample .properties file".  The only potentially-tricky part is getting the package references right.  So here is a snapshot of my project for you to compare to:
&lt;br&gt;&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7rL00WKmJ1k/Sbc3fCsdoEI/AAAAAAAAABw/z8tQ78cFxU8/s1600-h/PropertiesProjectExplorer"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 238px; height: 320px;" src="http://3.bp.blogspot.com/_7rL00WKmJ1k/Sbc3fCsdoEI/AAAAAAAAABw/z8tQ78cFxU8/s320/PropertiesProjectExplorer" border="0" alt=""id="BLOGGER_PHOTO_ID_5311775291945623618" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
Now I actually have a working code generation pipeline set up.  To conclude in my next post, I'll write a slightly more sophisticated template to generate Java code, and maybe talk a little bit about code generation philosophy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-6983549412343631262?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/6983549412343631262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=6983549412343631262' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/6983549412343631262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/6983549412343631262'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/03/generating-strongly-typed-properties_10.html' title='Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 3: Checks, Workflow, and a Template'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_7rL00WKmJ1k/Sbc3fCsdoEI/AAAAAAAAABw/z8tQ78cFxU8/s72-c/PropertiesProjectExplorer' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-8408744183870562045</id><published>2009-03-01T13:31:00.000-08:00</published><updated>2009-03-02T19:22:31.330-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='openarchitectureware'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 2:  Model</title><content type='html'>&lt;a href="http://www.scottmcmaster365.com/2009/02/generating-strongly-typed-properties.html"&gt;Previously&lt;/a&gt; I demonstrated how to create an Ecore metamodel describing Java .properties files, with the ultimate goal of using openArchitectureWare to build strongly-typed accessor classes for my properties.  Before I start writing oAW templates, though, I want to have a sample model that I can work with which is relatively simple but still exercises most of the breadth of my metamodel.
&lt;br&gt;&lt;br&gt;
The oAW sample project contains a model already called "Model.xmi".  The "xmi" is "&lt;a href="http://en.wikipedia.org/wiki/XML_Metadata_Interchange"&gt;XML Metadata Interchange&lt;/a&gt;", an OMG specification concerned with interoperable models, and the default format for models in Ecore/EMF.  I will delete the existing Model.xmi file and create a new model called "SamplePropertySet.xmi".  One way to do this is to open the Properties.ecore metamodel, right-click on the class that I want at the "root" of my model (namely, PropertySet), and select "Create Dynamic Instance".  Choosing a location in my workspace, away I go.
&lt;br&gt;&lt;br&gt;
The .xmi file can be edited with the "Sample Reflective Ecore Model Editor", which is essentially the same as the tree-node-properties editor I used on the .ecore file, except now the entities that I edit on my model are instances of the classes and attributes defined in my metamodel.  In other words, the "root" of my model is a PropertySet, and to that I can add children which are Property instances.  When I select one of these and open the Eclipse Properties grid, the properties are the class attributes defined in the metamodel.  If you have been following along and are still a little fuzzy on the relationship between metamodel and model, the following illustration might be helpful:
&lt;br&gt;&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_7rL00WKmJ1k/SayS4Vm4crI/AAAAAAAAABg/Ecr-KMwET48/s1600-h/MetamodelModel.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 294px; height: 320px;" src="http://4.bp.blogspot.com/_7rL00WKmJ1k/SayS4Vm4crI/AAAAAAAAABg/Ecr-KMwET48/s320/MetamodelModel.png" alt="" id="BLOGGER_PHOTO_ID_5308779557333529266" border="0" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
I'm going to test my openArchitectureWare code generation templates with a couple of properties -- one of type "Integer" and another of type "String".  My "completed" model looks like this:
&lt;br&gt;&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_7rL00WKmJ1k/SayUcRv2ppI/AAAAAAAAABo/5IDCgx3DFIY/s1600-h/PropertiesWindow2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 207px;" src="http://2.bp.blogspot.com/_7rL00WKmJ1k/SayUcRv2ppI/AAAAAAAAABo/5IDCgx3DFIY/s320/PropertiesWindow2.png" alt="" id="BLOGGER_PHOTO_ID_5308781274284336786" border="0" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
There are a number of other things that I could do with EMF at this point, such as generating a Java object model and "starter" Eclipse plugins for working with my models.  But I don't need to do any of that for this project:  Starting next time, everything going forward will be in the Land of openArchitectureWare.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-8408744183870562045?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/8408744183870562045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=8408744183870562045' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8408744183870562045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8408744183870562045'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/03/generating-strongly-typed-properties_01.html' title='Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 2:  Model'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_7rL00WKmJ1k/SayS4Vm4crI/AAAAAAAAABg/Ecr-KMwET48/s72-c/MetamodelModel.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-8031361349326461893</id><published>2009-03-01T11:55:00.000-08:00</published><updated>2009-03-01T12:13:43.267-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 1 Addendum: Graphical Ecore Editor</title><content type='html'>&lt;a href="http://www.scottmcmaster365.com/2009/02/generating-strongly-typed-properties.html"&gt;Last time&lt;/a&gt; I showed how to build an Ecore metamodel in Eclipse using the Sample Ecore Model Editor.  &lt;a href="http://www.macromodeling.com/"&gt;Ed Merks&lt;/a&gt; from the EMF team (whose &lt;a href="http://www.amazon.com/gp/product/0321331885?ie=UTF8&amp;amp;tag=softwaredev04-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0321331885"&gt;EMF book&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=softwaredev04-20&amp;amp;l=as2&amp;amp;o=1&amp;amp;a=0321331885" alt="" style="border: medium none  ! important; margin: 0px ! important; display: none;" border="0" width="1" height="1" /&gt; is out in a new edition, by the way) commented that I could be using the Graphical Ecore Editor, which is a part of the &lt;a href="http://wiki.eclipse.org/index.php/Ecore_Tools"&gt;Ecore Tools subproject&lt;/a&gt;.  He is absolutely correct.  (Thanks Ed.)  There are a couple of steps to go through to do that, so I'll talk about those here.
&lt;br&gt;&lt;br&gt;
First, you need to install the Ecore Tools package, which you can find on the Ganymede Update Site (assuming you're using Eclipse 3.4) under "Models and Model Development".  After you do that, you can select File | New | Other in Eclipse, and there will be an "Ecore Tools" folder available.  Underneath that, choose "Ecore Diagram".  A wizard appears, and you can select your .ecore file as the domain model.
&lt;br&gt;&lt;br&gt;
This will add a file to your project which has the extension ".ecorediag", and when you open it up, you'll get a Rose-like boxes-lines-and-properties editor which is far more user-friendly than the sample editor.  Here is what my Properties Generator metamodel looks like in diagram form after some slight rearrangement:
&lt;br&gt;&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7rL00WKmJ1k/Sarr6J7KjcI/AAAAAAAAABY/Dhm2Riph1Sg/s1600-h/PropertiesMetamodelGraphical"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 210px;" src="http://3.bp.blogspot.com/_7rL00WKmJ1k/Sarr6J7KjcI/AAAAAAAAABY/Dhm2Riph1Sg/s320/PropertiesMetamodelGraphical" alt="" id="BLOGGER_PHOTO_ID_5308314495138565570" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-8031361349326461893?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/8031361349326461893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=8031361349326461893' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8031361349326461893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8031361349326461893'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/03/generating-strongly-typed-properties.html' title='Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 1 Addendum: Graphical Ecore Editor'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_7rL00WKmJ1k/Sarr6J7KjcI/AAAAAAAAABY/Dhm2Riph1Sg/s72-c/PropertiesMetamodelGraphical' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-5731002289658962853</id><published>2009-02-28T17:16:00.000-08:00</published><updated>2009-02-28T20:32:54.475-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='openarchitectureware'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 1:  Metamodel</title><content type='html'>I do a lot of model-driven software engineering and code generation.  But you might not know that, because I've never written about it at all in my blog.  In the next couple of posts, I'll go about changing that.
&lt;br&gt;&lt;br&gt;
First, a bit about the application that I'm going to build.  I have &lt;a href="http://www.codeproject.com/KB/dotnet/StringClassGen.aspx"&gt;long believed in &lt;/a&gt;having wrapper classes for resources and properties.  Visual Studio and .NET now do a lot of this for you automatically.  In Java, we're still pretty much on our own.  What I would like to do is automatically generate a Java class which exposes a strongly-typed getter for each property in a &lt;a href="http://en.wikipedia.org/wiki/.properties"&gt;.properties file&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
To accomplish this, I'm going to use one of my favorite model-driven development tools, &lt;a href="http://www.openarchitectureware.org/"&gt;openArchitectureWare (oAW)&lt;/a&gt;.  oAW is comprised of a pretty extensive toolchain for model transformation, validation, and code generation, and of course, it's open-source.  As you will see, it integrates well with the &lt;a href="http://www.eclipse.org/modeling/emf/"&gt;Eclipse Modeling Framework (EMF)&lt;/a&gt;.  If you would like to follow along, &lt;a href="http://www.openarchitectureware.org/staticpages/index.php/download?menu=Download"&gt;install the openArchitecture Eclipse plugin&lt;/a&gt;.  While you're at it, check out the &lt;a href="http://www.openarchitectureware.org/pub/documentation/4.3.1/html/contents/"&gt;oAW documentation&lt;/a&gt;, particularly the &lt;a href="http://www.openarchitectureware.org/pub/documentation/4.3.1/html/contents/emf_tutorial.html#emf_tutorial_overview"&gt;oAW-EMF tutorial&lt;/a&gt; (but note that we're going to take a simpler approach that what is described there).
&lt;br&gt;&lt;br&gt;
In Eclipse, I start by creating a new "openArchitectureWare Project", which is available courtesy of the oAW plugin.  I took the option to "Create a sample", because it creates a number of EMF and oAW files that I need.  The sample project looks like this:
&lt;br&gt;&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_7rL00WKmJ1k/San-ja6R2vI/AAAAAAAAABA/aqEaTwXYzRo/s1600-h/oAWSampleProject"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 246px; height: 320px;" src="http://2.bp.blogspot.com/_7rL00WKmJ1k/San-ja6R2vI/AAAAAAAAABA/aqEaTwXYzRo/s320/oAWSampleProject" alt="" id="BLOGGER_PHOTO_ID_5308053520305478386" border="0" /&gt;&lt;/a&gt;
Right now, it's possible to right-click the "generator.oaw" file and select "Run As...oAW Workflow" and see some sample Java code get generated into the src-gen folder from Model.xmi.
&lt;br&gt;&lt;br&gt;
Rather than stop right now and discuss what each of these files does, I'll proceed through the development of my application and hopefully you'll pick things up intuitively.  For starters, I'm not going to use extensions, so I can go ahead and delete Extensions.ext and GeneratorExtensions.ext.  This will break the generator.oaw build for now, but I'll put it back together as I proceed.
&lt;br&gt;&lt;br&gt;
One of the most important files, and the focus of this post (finally!) is the .ecore file, which I will rename to "Properties.ecore".  This is where the &lt;a href="http://en.wikipedia.org/wiki/Metamodeling"&gt;metamodel&lt;/a&gt; is defined.  In this example, I want to create &lt;span style="font-style: italic;"&gt;models &lt;/span&gt;for properties, so my &lt;span style="font-style: italic;"&gt;metamodel &lt;/span&gt;needs to describe what a "property" is.  In the Java .properties file sense, it is basically a "name", a "type", and a "value".  Additionally, I want to bundle individual properties into "property sets" which are stored in .properties files.  The property sets themselves need a "name" and, optionally, a Java package to live in.  Finally, for illustrative purposes, I'll support loading them either as Java classloader resources or resource bundles.
&lt;br&gt;&lt;br&gt;
When I double-click to edit the .ecore file, it opens in the "Sample Ecore Model Editor" by default.  This editor is nothing to write home about, and in fact I suspect they call it "Sample" because the idea behind EMF is that tool builders will write (and sell you) their own higher-end Ecore editors, probably in the form of Rational-Rose-like graphical modeling environments.  No matter -- this editor will work fine for my purposes here.  The upside of the simple Ecore editor is that it is quite intuitive to use -- just right-click nodes to add children (classes, enums, attributes, references to other classes, etc.), and use the Properties view to configure them.  My metamodel ends up looking like this:
&lt;br&gt;&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_7rL00WKmJ1k/SaoOQ6sPzoI/AAAAAAAAABI/i4IKO1lzIhc/s1600-h/PropertiesMetamodel"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 306px; height: 320px;" src="http://3.bp.blogspot.com/_7rL00WKmJ1k/SaoOQ6sPzoI/AAAAAAAAABI/i4IKO1lzIhc/s320/PropertiesMetamodel" alt="" id="BLOGGER_PHOTO_ID_5308070794605088386" border="0" /&gt;&lt;/a&gt;The only trick I want to point out here is how to configure the "Properties" EReference on the PropertySet EClass.  First, I want to have multiple Property instances in a PropertySet, which I achieve by setting the "Upper Bound" on Properties to be "-1".  Second, a PropertySet logically "contains" its Properties, so I set the "Containment" property to "true".  (If you don't do this last step, you will experience perhaps-confusing difficulties when you actually go to create a Property while modeling in the Eclipse EMF model editor.)
&lt;br&gt;&lt;br&gt;
Now I need to use Eclipse to create a sample model based on this metamodel to use for testing while I develop code generation templates.  I'll demonstrate that in my next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-5731002289658962853?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/5731002289658962853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=5731002289658962853' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5731002289658962853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5731002289658962853'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/02/generating-strongly-typed-properties.html' title='Generating Strongly-Typed Properties Classes With openArchitectureWare, Part 1:  Metamodel'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_7rL00WKmJ1k/San-ja6R2vI/AAAAAAAAABA/aqEaTwXYzRo/s72-c/oAWSampleProject' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-3095679875713473145</id><published>2009-02-14T12:36:00.000-08:00</published><updated>2009-02-14T12:47:02.675-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Getting Integer and Long System Properties in Java</title><content type='html'>In the learn-something-new-every-day department, in a code review this week for one of the developers on my team, I noticed that he was calling &lt;a style="font-family: courier new;" href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html#getInteger%28java.lang.String%29"&gt;Integer.getInteger()&lt;/a&gt;.  I had never seen this before.  Turns out that this method and its overloads get system properties as Integers, handling the numeric conversion for you and returning null if it fails.  There is also a &lt;a style="font-family: courier new;" href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Long.html#getLong%28java.lang.String%29"&gt;Long.getLong() &lt;/a&gt;that does the same thing for (you guessed it) Longs.  So all of those times I called &lt;a style="font-family: courier new;" href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#getProperty%28java.lang.String%29"&gt;System.getProperty()&lt;/a&gt; followed by &lt;span style="font-family: courier new;"&gt;Integer.parseInt()&lt;/span&gt; with a catch of &lt;span style="font-family: courier new;"&gt;NumberFormatException&lt;/span&gt;, I was doing WAY too much work.  Good to know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-3095679875713473145?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/3095679875713473145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=3095679875713473145' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3095679875713473145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3095679875713473145'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/02/getting-integer-and-long-system.html' title='Getting Integer and Long System Properties in Java'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-1898207509488381712</id><published>2009-01-24T15:04:00.000-08:00</published><updated>2009-01-24T17:23:32.681-08:00</updated><title type='text'>Programming Culture</title><content type='html'>I was asked recently how long I thought it should take someone to become "proficient in a new programming language", assuming said individual is already proficient in a different language.  In various contexts, people will throw out numbers like six weeks, six months, a year, or more.  Obviously the answer depends greatly on what the questioner means by "proficient", as well as what languages are involved.  But I think it's the wrong question.
&lt;br&gt;&lt;br&gt;
There are several good analogies between spoken languages and programming languages.  I've heard it said that a good (spoken) language learner can become conversational new languages very quickly, but the hard part (and the part that takes the most time) is figuring out how to apply that language within its native &lt;span style="font-style: italic;"&gt;culture&lt;/span&gt;.
&lt;br&gt;&lt;br&gt;
I have picked up a (very) little bit of Chinese here and there, and I know that "&lt;a href="http://mandarin.about.com/od/dailymandarin/a/xiaojie.htm"&gt;xiao jie&lt;/a&gt;" translates into English as "miss" and is used as a term of address for women.  But I'm afraid to use it in practice (at least until I discuss the matter further with one of my Chinese friends), because I have heard that its appropriateness might vary depending on the specific context, as well as from where in China the listener hails, and first and foremost I do not want to offend anyone.
&lt;br&gt;&lt;br&gt;
Similarly, a competent software engineer can certainly become comfortable with the syntax and mechanics of using a programming language in a relatively short time.  Learning how to apply those concepts in such a way that experienced practitioners of the language feel comfortable reading and working with the code, though, will take more time.
&lt;br&gt;&lt;br&gt;
Back in the .com heydey, I was working on desktop applications at Microsoft and looking for an entry into the web world in my spare time.  A friend of mine was working at a web startup in DC, and they were doing Perl.  I won't name names to protect the innocent, but my friend was and is a True Perl Expert.  So while going through the process of learning Perl using (among other things) the Larry Wall book, I would periodically send him my code, usually quite impressed with myself, and ask him how I was doing.  The reply always went something like:
&lt;br&gt;&lt;br&gt;
&lt;span style="font-style: italic;"&gt;"Well, yes, that &lt;/span&gt;would&lt;span style="font-style: italic;"&gt; work, but it looks too much like C++.  A Perl programmer would never do it that way."&lt;/span&gt;
&lt;br&gt;&lt;br&gt;
&lt;span style="font-style: italic;"&gt;&lt;/span&gt;Perl, it turns out, has a very distinct culture.  Although I had learned the Perl language, I had not learned how to apply it within the context of that culture.  I wasn't correctly using the language's idioms, shortcuts, and libraries, and the code I was producing was "turning off" a more experienced Perl hand.  Framing the situation in terms of &lt;a href="http://www.merriam-webster.com/dictionary/culture"&gt;Webster's definition of "culture"&lt;/a&gt;, I was not respecting the "shared attitudes, values, goals, and practices" of the Perl programmers' community.
&lt;br&gt;&lt;br&gt;
I know how my friend felt.  Since that time, I've had the opportunity to look at a lot of Java and C# code written by unreformed C/C++ hands, and it is sometimes quite amusing.  My personal favorite -- and the deadest giveaway -- is consistent use of:
&lt;br&gt;&lt;br&gt;
&lt;span style="font-family:courier new;"&gt;String s = new String("Hello");&lt;/span&gt;
&lt;br&gt;&lt;br&gt;
But there are many more subtle signs, ranging from code formatting and indentation to how one writes a simple conditional expression, and, perhaps most importantly in this age of large platforms, not knowing which libraries and APIs are available.  Finally, there is the issue of context:  Using a language and its libraries to write toy programs to reverse strings is one thing, but using it to put together a scalable web application is an entirely different matter.
&lt;br&gt;&lt;br&gt;
Before a programmer can become truly "proficient" in a team environment, (s)he must internalize this knowledge.  There is really no substitute for time spent reading code and observing more experienced developers.  To borrow again from the world of spoken-language learning, &lt;a href="http://en.wikipedia.org/wiki/Language_immersion"&gt;immersion&lt;/a&gt; will yield the best results.
&lt;br&gt;&lt;br&gt;
Joel Spolsky has an interesting &lt;a href="http://http//www.joelonsoftware.com/articles/Biculturalism.html"&gt;essay on the cultural differences between Unix and Windows&lt;/a&gt;.  If we wanted to, we could do similar analyses of Java vs. C++ vs. .NET, C# vs. VB, and others.  To take one hypothetical, a C# programmer can start writing syntatically (and even stylistically) correct Java code in short order.  But to ask that programmer to immediately and independently stand up a web application using Spring, Hibernate, and Struts, with an automated Maven build, unit tests, and one-touch deployment, &lt;span style="font-style: italic;"&gt;and make it follow all of the best practices of the Java culture so that others can effectively carry the application forward&lt;/span&gt; -- well, that doesn't strike me as reasonable regardless of how experienced that person was in C# and .NET.
&lt;br&gt;&lt;br&gt;
In this time where many software developers are reevaluating their careers and techology portfolios (often not by choice), I'm not saying that folks should avoid putting themselves or other developers in positions where they have to adapt to new programming languages and cultures.  Quite the contrary -- throughout my career, I have observed that there is much to be gained from a "cross-polination" of ideas between different parts of the software development world.  I would suggest, however, that when we do go this route, we need to set realistic expectations for people making these changes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-1898207509488381712?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/1898207509488381712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=1898207509488381712' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1898207509488381712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1898207509488381712'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/01/programming-culture.html' title='Programming Culture'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-3362872932839407513</id><published>2009-01-18T09:53:00.000-08:00</published><updated>2009-01-18T09:57:50.916-08:00</updated><title type='text'>.NET FileTime Struct Revisited</title><content type='html'>On my old blog, I wrote about &lt;a href="http://softwaredevscott.spaces.live.com/blog/cns!1A9E939F7373F3B7!146.entry"&gt;the problems with the .NET FileTime struct&lt;/a&gt; and gave an approach to working around them.  Since then, a couple of people have sent me Spaces messages suggesting a way to simplify at least a portion of my solution.  Unfortunately, because of all of the recent changes on Spaces, and because I do not check my old space on anything like a regular basis, I have "misplaced" those messages.  If anyone having problems with FileTime lands here with improvements to share, please do so and I will follow up ASAP.  Thanks!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-3362872932839407513?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/3362872932839407513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=3362872932839407513' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3362872932839407513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3362872932839407513'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/01/net-filetime-struct-revisited.html' title='.NET FileTime Struct Revisited'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-3652733441304674081</id><published>2009-01-11T14:38:00.000-08:00</published><updated>2009-01-11T14:48:39.105-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Library Thing</title><content type='html'>Last fall I wrote about &lt;a href="http://softwaredevscott.spaces.live.com/blog/cns%211A9E939F7373F3B7%21482.entry?wa=wsignin1.0&amp;amp;sa=643754714"&gt;using an Amazon Widget to track my library of technical books&lt;/a&gt;.  Well, I just got turned on to &lt;a href="http://www.librarything.com/"&gt;Library Thing&lt;/a&gt;, which seems to be a FAR better approach to recording and sharing my interest in books.

I won't go through all of Library Thing's features here, but in a nutshell it provides a means to "socially catalog" your books, in the same sense that del.icio.us provides for bookmarking.  My first pass at entering my library is &lt;a href="http://www.librarything.com/catalog/scott_mcmaster"&gt;here&lt;/a&gt;.

Perhaps the best part is that after I entered these books, I was able to get book recommendations based on the catalogs of others with similar interests, and I can attest that they are amazingly good.  In fact, of the top seven recommendations that it gave me, two were books that I already owned but had forgotten to enter.

Finally, with the Library Thing blog widget, you can now see random books from my library in the sidebar of my blog.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-3652733441304674081?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/3652733441304674081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=3652733441304674081' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3652733441304674081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/3652733441304674081'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2009/01/library-thing.html' title='Library Thing'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-1534587767135138829</id><published>2008-12-31T21:00:00.000-08:00</published><updated>2009-01-01T16:06:23.551-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Why to Consider Using an Application Server Tier</title><content type='html'>Toward the end of &lt;a href="http://www.dotnetrocks.com/"&gt;.NET Rocks!&lt;/a&gt;' latest &lt;a href="http://www.dotnetrocks.com/default.aspx?showNum=402"&gt;interview with Oren Eini&lt;/a&gt; (which I enjoyed), &lt;a href="http://ayende.com/Blog/"&gt;Oren &lt;/a&gt;mentioned that he was thinking about &lt;em&gt;"how to kill three-tier architecture"&lt;/em&gt;, going on to say that the one reason you wanted to have an application server was &lt;em&gt;"connection pooling"&lt;/em&gt;. Richard Cambell went on to say that you might use an application server if you have &lt;em&gt;"application resources or some kind of a set of objects that are processor intensive or long running that are independent, the execution of that is somewhat independent with what the web server has to do"&lt;/em&gt;.
&lt;br&gt;&lt;br&gt;
&lt;em&gt;(It's great that .NET Rocks! has transcripts, which makes it extremely easy to quote this stuff. Thanks Carl.)&lt;/em&gt;
&lt;br&gt;&lt;br&gt;
I think the discussion above is an incomplete view which sells an independent application server tier far short. No, you don't always need application servers, but they do have practical uses beyond the scenarios mentioned above, and they are often borderline-essential for a robust web application architecture.
&lt;br&gt;&lt;br&gt;
The rest of this post is primarily intended for my .NET brethren. Three-tier server architecture discussions get rather confusing in the Microsoft world because a) Microsoft's tooling doesn't really support it well out of the box (and in fact much of it pushes you &lt;em&gt;very&lt;/em&gt; hard in the two-tier direction), and b) both the web server and application server typically end up being IIS. Java and LAMP developers are quite familiar with using Apache to front for Tomcat, JBoss, or their company's favorite expensive commercial JEE server.
&lt;br&gt;&lt;br&gt;
Perhaps most importantly, having separate application servers is critical to the proper implementation of a &lt;a href="http://en.wikipedia.org/wiki/Demilitarized_zone_(computing)"&gt;DMZ&lt;/a&gt;, which you most likely want if you are running on the capital-I Internet. In a nutshell, this architecture allows you to better protect your database if your externally-facing web server tier is compromised. See &lt;a href="http://en.wikipedia.org/wiki/Demilitarized_zone_(computing)#Web_servers"&gt;this link&lt;/a&gt; into the Wikipedia article for a better explanation.
&lt;br&gt;&lt;br&gt;
Additionally, separate web and application tiers allow for limitless options in independent provisioning, scaling, and tuning. The power here should not be underestimated. You can tune your web servers for I/O throughput and your application servers for raw processing horsepower if that is what your application demands. If you are serving a lot of static content, you can move it forward to the web server tier and take the load off your more expensive application servers. You can use different load-balancing and encryption strategies. You can also choose to use a cheaper computing platform on one tier (typically, the web tier) to save on hardware costs and licensing fees.
&lt;br&gt;&lt;br&gt;
I have a book that I like to refer people to when this subject comes up -- &lt;a href="http://www.amazon.com/gp/product/0470856122?ie=UTF8&amp;tag=softwaredev04-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0470856122"&gt;Architecting Enterprise Solutions: Patterns for High-Capability Internet-based Systems&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=softwaredev04-20&amp;l=as2&amp;o=1&amp;a=0470856122" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;, by Paul Dyson and Andrew Longshaw. It's a patterns book that discusses the tradeoffs involved in robust Internet architectures. I'm not aware of anything else quite like it, so if you're interested (or if you would simply like to read something that will probably expand your engineering horizons), get it.
&lt;br&gt;&lt;br&gt;
&lt;iframe style="WIDTH: 120px; HEIGHT: 240px" marginwidth="0" marginheight="0" src="http://rcm.amazon.com/e/cm?t=softwaredev04-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;asins=0470856122&amp;amp;md=10FE9736YVPPT7A0FBG2&amp;amp;fc1=000000&amp;amp;IS2=1&amp;amp;lt1=_blank&amp;amp;m=amazon&amp;amp;lc1=0000FF&amp;amp;bc1=000000&amp;amp;bg1=FFFFFF&amp;amp;f=ifr" frameborder="0" scrolling="no"&gt;&lt;/iframe&gt;
&lt;br&gt;
I've been known to use the title of this post, "Why to Consider Using an Application Server Tier", as an interview question. I guess I might not be able to do that anymore. But it's a fun blog discussion topic, which is worth it in my opinion.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-1534587767135138829?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/1534587767135138829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=1534587767135138829' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1534587767135138829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1534587767135138829'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/12/why-to-consider-using-application.html' title='Why to Consider Using an Application Server Tier'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-4104784378617238780</id><published>2008-12-23T20:10:00.000-08:00</published><updated>2008-12-23T20:43:27.516-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='workflow'/><category scheme='http://www.blogger.com/atom/ns#' term='log4net'/><category scheme='http://www.blogger.com/atom/ns#' term='dotnet'/><title type='text'>Integrating Log4Net and WF</title><content type='html'>Like many people, I've been trying to take greater advantage of the public library lately. Of course, the supply of technical books is fairly limited, so I basically get whatever they happen to have on the shelf on a given day that looks relevant. (That's saying nothing about the great digital offerings that most libraries now have, but they're a little hard to read on the bus.)
&lt;br&gt;&lt;br&gt;
One of the books that I have now is &lt;a href="http://www.amazon.com/gp/product/1430209755?ie=UTF8&amp;amp;tag=softwaredev04-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1430209755"&gt;Pro WF&lt;/a&gt;.  In the process of working through this book and improving my core Workflow Foundation skills, I've been trying to build some useful tools, such as &lt;a href="http://www.scottmcmaster365.com/2008/12/unit-testing-wf-workflows-with-nunit.html"&gt;the NUnit test fixture class&lt;/a&gt; that I wrote about recently.
&lt;br&gt;&lt;br&gt;
I've grown a little tired of writing CodeActivities here and there just to print out the current value of some variable, so I started thinking about something more general and with a wider applicability. Put that together with the fact that I've never been particularly satisfied with Microsoft-supplied logging solutions (which would be the subject of another post), and what I decided to do was create a custom WF activity that logs using &lt;a href="http://logging.apache.org/log4net/index.html"&gt;Log4Net&lt;/a&gt; and takes advantage of dependency properties to configure the variables to log. The source code is on my Code Gallery page &lt;a href="https://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=softwaredevscott&amp;amp;ReleaseId=1966"&gt;here&lt;/a&gt;.
&lt;br&gt;&lt;br&gt;
Using the Log4NetActivity is very straightforward. It does assume that you configure Log4Net prior to use -- that should be handled in the startup of your host application. Then you just need to give it the name of a logger along with a message.
&lt;br&gt;&lt;br&gt;
The message can -- and probably will -- use the syntax of a format string, which can pull from up to ten dependency properties (Arg0 to Arg9) configured on the activity. That's the only remotely-interesting thing about the design itself. Originally I wanted to allow a dynamic set of dependency properties, but in the end I didn't see any way to make that work. The closest thing I could find was &lt;a href="http://blogs.microsoft.co.il/blogs/bursteg/archive/2006/10/29/DynamicWorkflowBindingParameters.aspx"&gt;this excellent post and sample code by Guy Burstein&lt;/a&gt;, but had I gone that route, I believe the best I would have been able to do was force you to declare each argument to log before binding, and that would have been annoying.
&lt;br&gt;&lt;br&gt;
The activity can also use the Exception-logging capability of the Log4Net APIs, but the Exception itself is not configurable via a dependency property, limiting its usefulness. (I have no better ideas here.)
&lt;br&gt;&lt;br&gt;
Certainly nothing revolutionary, but hopefully somewhat useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-4104784378617238780?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/4104784378617238780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=4104784378617238780' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4104784378617238780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/4104784378617238780'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/12/integrating-log4net-and-wf.html' title='Integrating Log4Net and WF'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-6070282050312662826</id><published>2008-12-19T15:47:00.000-08:00</published><updated>2008-12-19T16:02:20.716-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='dozer'/><title type='text'>Dozer Custom Converter for Joda DateTime</title><content type='html'>I mentioned in &lt;a href="http://www.scottmcmaster365.com/2008/12/dozer-can-help-me-manage-parallel.html"&gt;my last post&lt;/a&gt; how excited I was to try out &lt;a href="http://dozer.sourceforge.net/"&gt;Dozer&lt;/a&gt;.  Today I got the chance to play around with it a little bit.  As a proof-of-concept for myself, I wrote a &lt;a href="http://dozer.sourceforge.net/documentation/customconverter.html"&gt;custom converter&lt;/a&gt; to handle mapping of &lt;a href="http://joda-time.sourceforge.net/"&gt;Joda-Time&lt;/a&gt; DateTime's to and from Java Calendars.  I thought that this should be very easy, and I was happy to find out that it was.

The first step was to actually implement the CustomConverter interface:

&lt;pre name="code" class="java"&gt;
package com.scottmcmaster365.dozerdemo;

import java.util.Calendar;

import net.sf.dozer.util.mapping.MappingException;
import net.sf.dozer.util.mapping.converters.CustomConverter;

import org.joda.time.DateTime;

/**
* Custom converter for Joda DateTime's.
* @author Scott McMaster
* http://www.scotmcmaster365.com/
*
*/
public class DateTimeConverter implements CustomConverter {

 @SuppressWarnings("unchecked")
 @Override
 public Object convert(Object existingDestinationFieldValue,
   Object sourceFieldValue, Class destinationClass, Class sourceClass)
 {
  if( sourceFieldValue == null )
  {
   return null;
  }
  
  if( sourceFieldValue instanceof Calendar )
  {
   // Note that DateTime is immutable, so
   // we can't do much with the existingDestinationFieldValue.
   return new DateTime(sourceFieldValue);
  }
  else if( sourceFieldValue instanceof DateTime )
  {
   Calendar result;
   if( existingDestinationFieldValue == null )
   {
    result = Calendar.getInstance();
   }
   else
   {
    result = (Calendar) existingDestinationFieldValue;
   }
   result.setTimeInMillis(((DateTime) sourceFieldValue).getMillis());
   return result;
  }
  
  throw new MappingException("Misconfigured/unsupported mapping");
 }

}

&lt;/pre&gt;The logic here is very straightforward, although you could imagine creating many more Dozer converters for DateTime's.  (One that accepts a formatted String comes immediately to mind.)  You can wire this up with a little bit of XML as so:

&lt;textarea name="code" class="xml" cols="60" rows="13"&gt;
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE mappings PUBLIC "-//DOZER//DTD MAPPINGS//EN"
   "http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd"&gt;
&lt;mappings&gt;
  &lt;configuration&gt;
    &lt;custom-converters&gt; &lt;!-- these are always bi-directional --&gt;
      &lt;converter type="com.scottmcmaster365.dozerdemo.DateTimeConverter" &gt;
        &lt;class-a&gt;org.joda.time.DateTime&lt;/class-a&gt;
        &lt;class-b&gt;java.util.Calendar&lt;/class-b&gt;
      &lt;/converter&gt;
    &lt;/custom-converters&gt;     
  &lt;/configuration&gt;
&lt;/mappings&gt;
&lt;/textarea&gt;

Here are a couple of unit tests that demonstrate the capability.  Not shown are a couple of little classes, TypeWithCalendar and TypeWithDateTime, which have bean properties called "value" of type java.util.Calendar and org.joda.DateTime, respectively.


&lt;pre name="code" class="java"&gt;
/**
 * Test mapping from Calendar &lt;-&gt; DateTime
 * using Dozer.
 * @author Scott McMaster
 *
 */
public class TestDozerDateTimeConverter
{
 private DozerBeanMapper mapper;

 @Before
 public void setUp()
 {
  List&lt;String&gt; mappingFiles = new ArrayList&lt;String&gt;();
  mappingFiles.add("dozermappings.xml");
  mapper = new DozerBeanMapper();
  mapper.setMappingFiles(mappingFiles);
 }
 
 @Test
 public void testToCalendarType()
 {
  DateTime then = new DateTime(1995, 11, 27, 0, 0, 0, 0);
  TypeWithDateTime joda = new TypeWithDateTime();
  joda.setValue(then);
  
  TypeWithCalendar calendar = 
    (TypeWithCalendar) mapper.map(joda, TypeWithCalendar.class);
  
  assertEquals((Long) then.getMillis(), (Long) calendar.getValue().getTimeInMillis());
 }
 
 @Test
 public void toJodaType()
 {
  Calendar then = Calendar.getInstance();
  then.set(1995, 10, 27);
  TypeWithCalendar calendar = new TypeWithCalendar();
  calendar.setValue(then);
  
  TypeWithDateTime joda =
   (TypeWithDateTime) mapper.map(calendar, TypeWithDateTime.class);
  
  assertEquals((Long) then.getTimeInMillis(), (Long) joda.getValue().getMillis());
 }
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-6070282050312662826?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/6070282050312662826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=6070282050312662826' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/6070282050312662826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/6070282050312662826'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/12/dozer-custom-converter-for-joda.html' title='Dozer Custom Converter for Joda DateTime'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-2890604681517817482</id><published>2008-12-16T20:15:00.000-08:00</published><updated>2008-12-19T16:02:47.096-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='dozer'/><title type='text'>Dozer Can Help Me Manage Parallel Object Hierarchies!</title><content type='html'>Some time ago, I wrote a post called &lt;a href="http://softwaredevscott.spaces.live.com/blog/cns!1A9E939F7373F3B7!349.entry"&gt;"Robust Architecture for Object-Relational Mapping"&lt;/a&gt;, in which I discussed and diagrammed a separation of "Business/Domain" and "Persistence" objects into separate, parallel object hierarchies.  The idea is to isolate lazy loading and other ORM features (as well as any database schema oddities) away from the actual objects that implement the business logic.  The main drawback to this approach is that it requires you to implement potentially-tedious and error-prone logic to transfer data back and forth between the layers.

Taking this further, I think it's perfectly reasonable for a modern enterprise application to have up to &lt;em&gt;four&lt;/em&gt; parallel object hierarchies:
&lt;ol&gt;&lt;li&gt;&lt;em&gt;Persistence objects&lt;/em&gt;, which can closely mirror the database schema if necessary and guarantee that "transparent" ORM stays that way.&lt;/li&gt;&lt;li&gt;&lt;em&gt;Business/Domain objects&lt;/em&gt;, which reflect the application's core entities and the business logic that manipulates them.&lt;/li&gt;&lt;li&gt;&lt;em&gt;View objects&lt;/em&gt;, which are data objects optimized to serve the View layer.&lt;/li&gt;&lt;li&gt;&lt;em&gt;Message objects&lt;/em&gt;, which are exposed to the outside world via services and need to remain stable in the face of changes to the business layer.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;In anything other than a trivial application and given current technologies, it is very difficult to use a single object model to serve all of the needs described above.  (Trust me, I've tried.)  So in reality, we spend a lot of time in the transfer implementation, moving values back and forth between the different flavors of objects, either with tedious set/get code, a hand-rolled reflection-based framework, or (more likely) a combination of both.&lt;/p&gt;&lt;p&gt;Clearly this is annoying.  So I was excited today when one of my friends told me about &lt;a href="http://dozer.sourceforge.net/"&gt;Dozer&lt;/a&gt;, which purports to be a "powerful, yet simple Java Bean to Java Bean mapper".  I haven't tried it yet -- just spent a lot of time going through the documentation.  But it appears that Dozer will allow me to wire up hierarchies like those I describe above in a minimal-code, declarative fashion.  I'm looking forward to trying it out and reporting my experiences.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-2890604681517817482?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/2890604681517817482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=2890604681517817482' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/2890604681517817482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/2890604681517817482'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/12/dozer-can-help-me-manage-parallel.html' title='Dozer Can Help Me Manage Parallel Object Hierarchies!'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-1928955372792544549</id><published>2008-12-07T16:56:00.000-08:00</published><updated>2008-12-19T16:03:07.331-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='unit_testing'/><category scheme='http://www.blogger.com/atom/ns#' term='workflow'/><category scheme='http://www.blogger.com/atom/ns#' term='nunit'/><category scheme='http://www.blogger.com/atom/ns#' term='dotnet'/><title type='text'>Unit Testing WF Workflows with NUnit</title><content type='html'>When I looked around for approaches to testing Windows Workflow Foundation workflows, I was surprised at how few examples I found. There is a fair amount of buzz around testing custom activities, but not much on entire workflows. Maybe that's because folks consider testing a complete workflow to be an "integration testing" task rather than a "unit testing" one. As an eternal pragmatist, that is a distinction that I could not possibly care less about if I tried. I mean, you really &lt;i&gt;should&lt;/i&gt; be testing your complete workflows to the extent possible, and if your unit testing tools work for that, why not use them? So I set out to create a base class using NUnit which could easily be extended by test fixtures that want to run workflows.

&lt;br&gt;&lt;br&gt;

My first cut at this is &lt;a href="https://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=softwaredevscott&amp;amp;ReleaseId=1902"&gt;on my Code Gallery page&lt;/a&gt;. The base class is called &lt;span style="font-family:courier new;"&gt;WorkflowTestFixture&lt;/span&gt;. It takes a generic type parameter which is the (sequential) workflow that you want to execute. The resulting test fixture code comes out looking like this:

&lt;br&gt;&lt;br&gt;

&lt;div class="code"&gt;
&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:black;"&gt;[TestFixture]
&lt;/span&gt;&lt;br&gt;
&lt;span style="color:blue;"&gt;public class &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:black;"&gt;TestMyWorkflow : ScottMcMaster365.WorkflowTestFixture&amp;lt;MyWorkflow&amp;gt;
&lt;br&gt;
{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="code"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:black;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
[Test]
&lt;/span&gt;&lt;br&gt;
&lt;span style="color:blue;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:black;"&gt;TestNormalExecution()&lt;/span&gt;&lt;br&gt;
&lt;span style="color:black;"&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&lt;/span&gt;&lt;/br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AddArgument(&lt;/span&gt;&lt;span style="color:#808080;"&gt;"AccountNumber"&lt;/span&gt;&lt;span style="color:black;"&gt;, &lt;/span&gt;&lt;span style="color:maroon;"&gt;60005&lt;/span&gt;&lt;span style="color:black;"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:blue;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="code"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:blue;"&gt;
&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Run()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:blue;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="code"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:blue;"&gt;
&lt;/span&gt;&lt;span style="color:black;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Assert.AreEqual(&lt;/span&gt;&lt;span style="color:maroon;"&gt;20000&lt;/span&gt;&lt;span style="color:black;"&gt;.00M, GetOutput(&lt;/span&gt;&lt;span style="color:#808080;"&gt;"AvailableCredit"&lt;/span&gt;&lt;span style="color:black;"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:courier new;font-size:85%;color:blue;"&gt;;&lt;/span&gt;&lt;/div&gt;&lt;div class="code"&gt;&lt;span style="font-family:courier new;font-size:85%;color:blue;"&gt;
&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:black;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="code"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:black;"&gt;
}&lt;/span&gt;
&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;

&lt;br&gt;&lt;br&gt;

&lt;span style="font-family:courier new;"&gt;AddArgument&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;Run&lt;/span&gt;, and &lt;span style="font-family:courier new;"&gt;GetOutput&lt;/span&gt; are base class methods which set up the workflow's inputs, synchronously execute the workflow, and retrieve output parameters, respectively. The actual implementation of &lt;span style="font-family:courier new;"&gt;WorkflowTestFixtue&lt;&gt;&lt;/span&gt; is nothing special -- in fact, it comes out looking a lot like some of the &lt;a href="http://odetocode.com/Blogs/scott/archive/2006/08/02/5492.aspx"&gt;code that K. Scott Allen posted for testing custom activities&lt;/a&gt;. (I'm not sure why Scott didn't create a base class for the stuff that he finds overly verbose. Maybe he did that in a later post that doesn't Google as well.)

&lt;br&gt;&lt;br&gt;

Clearly there are some workflows and styles of workflow for which this approach will not work well. Right now I see it as primarily useful for sequential workflows which can execute directly to completion without a lot of external integrations with databases, services, etc. That said, I believe that still constitutes an important class of workflow. As for the limitations, I hope to remove some them in the future. Suggestions are welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-1928955372792544549?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/1928955372792544549/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=1928955372792544549' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1928955372792544549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1928955372792544549'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/12/unit-testing-wf-workflows-with-nunit.html' title='Unit Testing WF Workflows with NUnit'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-1318078287339208018</id><published>2008-11-25T20:52:00.001-08:00</published><updated>2008-11-25T20:56:02.610-08:00</updated><title type='text'>Looking for Tips on Formatting Code in Blogger</title><content type='html'>OK, if you read my last post, you probably noticed that the formatting of the Java and (especially) XML code was, well, not so great.  And it took me some time just to get it &lt;em&gt;that &lt;/em&gt;good.  I have some code-to-highlighted-HTML tools that I like to use, but when I push it into Blogger, it gets mangled in various ways.  For all its many faults, Spaces at least didn't give me a lot of trouble in this department.

I searched around a bit for techniques and tools to improve this situation.  Offhand, I didn't find anything that really impressed me as a good solution.  I'll keep looking and trying new things, but in the meantime, if you have some good tips here, please share them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-1318078287339208018?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/1318078287339208018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=1318078287339208018' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1318078287339208018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/1318078287339208018'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/11/looking-for-tips-on-formatting-code-in.html' title='Looking for Tips on Formatting Code in Blogger'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-8843595564565565769</id><published>2008-11-25T19:59:00.000-08:00</published><updated>2008-12-19T16:03:30.717-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='hsqldb'/><category scheme='http://www.blogger.com/atom/ns#' term='dbunit'/><title type='text'>dbUnit and HSQLDB Databases with Schema Names</title><content type='html'>There are several ways to manage unit tests that involve a data layer. Someday I'll enumerate them in another post. But when I'm doing Java, one of my favorites is using &lt;a href="http://hsqldb.org/"&gt;HSQLDB&lt;/a&gt; as a fast, in-memory stand-in for a more heavyweight database. I also like &lt;a href="http://dbunit.sourceforge.net/"&gt;dbUnit&lt;/a&gt; to manage setting and resetting the data in the in-memory database.

I ran into an interesting problem when trying to use dbUnit with an HSQLDB database that employed named schemas (like, say, Oracle). In case you've never had to create your own schemas in DDL before, you can create the schema "SCOTTSCHEMA" like this:

&lt;div class="java" align="left"&gt;&lt;table cellspacing="0" cellpadding="3" border="0"  style="color:#ffffff;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;!-- start source code --&gt;&lt;td valign="top" align="left"&gt;&lt;code&gt;&lt;span style="color:#000000;"&gt;Statement ddl = conn.createStatement&lt;/span&gt;&lt;span style="color:#000000;"&gt;()&lt;/span&gt;&lt;span style="color:#000000;"&gt;;&lt;/span&gt;
&lt;span style="color:#000000;"&gt;ddl.executeUpdate&lt;/span&gt;&lt;span style="color:#000000;"&gt;(&lt;/span&gt;&lt;span style="color:#2a00ff;"&gt;"CREATE SCHEMA SCOTTSCHEMA AUTHORIZATION DBA"&lt;/span&gt;&lt;span style="color:#000000;"&gt;)&lt;/span&gt;&lt;span style="color:#000000;"&gt;;&lt;/span&gt;&lt;/code&gt; &lt;/td&gt;&lt;!-- end source code --&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Now, when you get ready to use dbUnit and create your IDatabaseConnection, you need to pass the schema name:&lt;/p&gt;&lt;div class="java" align="left"&gt;&lt;table cellspacing="0" cellpadding="3" border="0"  style="color:#ffffff;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;!-- start source code --&gt;&lt;td valign="top" align="left"&gt;&lt;code&gt;&lt;span style="color:#000000;"&gt;IDatabaseConnection dbUnitConn = &lt;/span&gt;&lt;span style="color:#7f0055;"&gt;&lt;b&gt;new &lt;/b&gt;&lt;/span&gt;&lt;span style="color:#000000;"&gt;DatabaseConnection&lt;/span&gt;&lt;span style="color:#000000;"&gt;(&lt;/span&gt;&lt;span style="color:#000000;"&gt;conn, &lt;/span&gt;&lt;span style="color:#2a00ff;"&gt;"SCOTTSCHEMA"&lt;/span&gt;&lt;span style="color:#000000;"&gt;)&lt;/span&gt;&lt;span style="color:#000000;"&gt;;&lt;/span&gt;&lt;/code&gt; &lt;/td&gt;&lt;!-- end source code --&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Now I should be able to load up XML files using dbUnit that look like this for a hypothetical "CONTACTS" table:
&lt;/p&gt;&lt;p&gt;
&lt;span style="font-size:78%;"&gt;&lt;span style="color:blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;?xml&lt;/span&gt;&lt;span style="color:red;"&gt; version&lt;/span&gt;&lt;span style="color:blue;"&gt;="1.0"&lt;/span&gt;&lt;span style="color:red;"&gt; encoding&lt;/span&gt;&lt;span style="color:blue;"&gt;="UTF-8"?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color:black;"&gt;
&lt;/span&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="color:blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;dataset&lt;/span&gt;&lt;span style="color:blue;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color:black;"&gt;
&lt;span style="font-size:78%;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="color:blue;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;SCOTTSCHEMA.CONTACTS&lt;/span&gt;&lt;span style="color:red;"&gt; FIRST_NAME&lt;/span&gt;&lt;span style="color:blue;"&gt;="Scott"&lt;/span&gt;&lt;/span&gt;&lt;span style="color:red;"&gt;
&lt;span style="font-size:78%;"&gt;LAST_NAME&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="color:blue;"&gt;="McMaster"/&gt;

&amp;lt;&lt;/span&gt;&lt;span style="color:maroon;"&gt;SCOTTSCHEMA.CONTACTS&lt;/span&gt;&lt;span style="color:red;"&gt; FIRST_NAME&lt;/span&gt;&lt;span style="color:blue;"&gt;="Tracy"&lt;/span&gt;&lt;/span&gt;&lt;span style="color:red;"&gt;
&lt;span style="font-size:78%;"&gt;LAST_NAME&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="color:blue;"&gt;="McMaster"/&gt;
&amp;lt;/&lt;/span&gt;&lt;span style="color:maroon;"&gt;dataset&lt;/span&gt;&lt;span style="color:blue;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:black;"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/p&gt;&lt;p&gt;
It turns out that passing the schema name in the second parameter is necessary, but not sufficient: If you try to run a database operation against dbUnitConn at this point, you'll get the following exception:&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;&lt;strong&gt;org.dbunit.dataset.NoSuchTableException&lt;/strong&gt;: SCOTTSCHEMA.CONTACTS at org.dbunit.database.DatabaseDataSet.getTableMetaData(DatabaseDataSet.java:222) at org.dbunit.operation.DeleteAllOperation.execute(DeleteAllOperation.java:109) at org.dbunit.operation.CompositeOperation.execute(CompositeOperation.java:79) at ...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;When I first hit this, it was easier to figure out what was going on from the dbUnit code rather than the documentation. I discovered rather quickly that you also need to set FEATURE_QUALIFIED_TABLE_NAMES in the IDatabaseConnection's config:&lt;/p&gt;&lt;div class="java" align="left"&gt;&lt;table cellspacing="0" cellpadding="3" border="0"  style="color:#ffffff;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;!-- start source code --&gt;&lt;td valign="top" align="left"&gt;&lt;code&gt;&lt;span style="color:#000000;"&gt;dbUnitConn.getConfig&lt;/span&gt;&lt;span style="color:#000000;"&gt;()&lt;/span&gt;&lt;span style="color:#000000;"&gt;.setFeature&lt;/span&gt;&lt;span style="color:#000000;"&gt;(&lt;/span&gt;&lt;span style="color:#000000;"&gt;DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, &lt;/span&gt;&lt;span style="color:#7f0055;"&gt;&lt;b&gt;true&lt;/b&gt;&lt;/span&gt;&lt;span style="color:#000000;"&gt;)&lt;/span&gt;&lt;span style="color:#000000;"&gt;;&lt;/span&gt;&lt;/code&gt; &lt;/td&gt;&lt;!-- end source code --&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Now dbUnit will build its internal map of database tables prefixed with the schema name and will be able to find the table metadata when you execute an operation. It seems like it would make sense to default this feature to "true" when you create a database connection with a schema name...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-8843595564565565769?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/8843595564565565769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=8843595564565565769' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8843595564565565769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/8843595564565565769'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/11/dbunit-and-hsqldb-databases-with-schema.html' title='dbUnit and HSQLDB Databases with Schema Names'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-443704514548190823.post-5033134596064650055</id><published>2008-11-22T12:18:00.000-08:00</published><updated>2008-11-22T12:31:26.712-08:00</updated><title type='text'>My New Blog Location</title><content type='html'>Well, I finally did it.  My old buddy &lt;a href="http://eitheror.com/"&gt;Chad &lt;/a&gt;has been on me about moving my blog to a different platform.   I was reluctant, because I feel like I've built up a pretty good body of content on my old blog, and I didn't want to risk losing regular readers or anyone who found me while looking for solutions to their technical problems on Google.  But some time ago, I turned off comments due to an endless stream of spam appearing on ancient posts.  I'm tired of not having comments.  I want comments.  And I want a whole lot of other features that Windows Live Spaces does not provide, even after all these years.  So here I am on Blogger.

I plan to keep the content the same -- book and article reviews and suggestions, discussion of the software development tools that I use, sample code in Java, .NET, Ruby, and anything else that I find interesting.  So I'm glad you found this and hope you'll stick with me while I get this blog going.  Of course, you can always find my previous work &lt;a href="http://softwaredevscott.spaces.live.com/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/443704514548190823-5033134596064650055?l=www.scottmcmaster365.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.scottmcmaster365.com/feeds/5033134596064650055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=443704514548190823&amp;postID=5033134596064650055' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5033134596064650055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/443704514548190823/posts/default/5033134596064650055'/><link rel='alternate' type='text/html' href='http://www.scottmcmaster365.com/2008/11/my-new-blog-location.html' title='My New Blog Location'/><author><name>Scott McMaster</name><uri>http://www.blogger.com/profile/02359288908304220088</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://4.bp.blogspot.com/-wBgcaBCwnxU/TwDlZ3uKLCI/AAAAAAAAsBM/nwUxKHDd7Co/s220/DSC_0475-1.JPG'/></author><thr:total>0</thr:total></entry></feed>
