tag:blogger.com,1999:blog-83210771094445303172024-03-14T01:12:00.332-07:00A Coder's LogDon Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.comBlogger43125tag:blogger.com,1999:blog-8321077109444530317.post-12711376539122709902011-05-18T15:47:00.000-07:002011-05-22T12:49:21.394-07:00This Greek Tragedy<div style="background-color: transparent; font-family: 'Times New Roman'; font-size: medium; "><span id="internal-source-marker_0.6492583993822336" style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Grigoris was a man who lived a good life, made a solid name for himself, and was well-respected by his community. But now, beset by the consequences of some unfortunate, perhaps even foolish, spending habits, he found himself in a great deal of debt in his middle years.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">He might have thought to change his ways and begin the ascent from debt, but as we often find, old habits are hard to break. Fortunately for Grigoris, he came from a very large family. Counting himself, there were twenty-seven siblings, amazingly, all boys and just one girl, Francesca. Grigoris appealed to his family, but they were reluctant to help, since many of them were struggling to pay their debts as well.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">After a long time of appealing to his siblings and offering promises that he would change his ways, Grigoris’ brother Gregor reluctantly agreed to give Grigoris enough money to settle his debts and make a new start. Gregor hoped that someday he would be repaid, but didn’t expect it.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">To no one’s surprise, Grigoris accepted the money but did not change his ways. Within a couple years, he was back in debt and once again appealing to his family for help.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Although Grigoris spent much of his money on foolishness and temporary pleasures, he managed somehow to retain a large store of family heirlooms that had great value. The family lived in constant concern that he would sell these so some unsavory neighbor, but even in the depths of his flurry of spending, Grigoris held these back.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Once again, he appealed to his family. They were no idiots, and chose not to repeat the mistake of loaning him the money. He sustained his appeal, even while the debts continued to grow. But none of his family would part with their money.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">One cold spring morning a thin beam of sunlight drilled through the curtains of the room where Grigoris slept, and settled directly on his left eye, awaking him with a start, a dream still fresh in his mind. In this dream was the spark, the answer that he had desired.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">He immediately came to his senses and decided what he must do. He must choose one of the heirlooms and sell it to pay off his debts. “This is what all sensible people do,” he said aloud as the light of the morning enveloped him. “Selling some precious item from my collection will be a constant reminder that I must not fall again into debt, that I must change my ways and never again shame my family in this fashion.”</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">And so he set out to choose one precious item, and there were many to consider. Finally, he settled on one, thinking, it must be the most precious of all. Many generations back, he knew not exactly how many, a forefather had constructed a lavish gazebo on a small piece of land on a hill from the finest marble drawn from quarries at great cost. This gazebo was a delight to the family, and many would often return home just for the purpose of seeing it once more, and spending time there. Even visitors from various places would pass by just to behold its beauty.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">He must sell this.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Though wracked and distressed, he set out with determination to find a buyer. He quickly found one, a foreign man who wanted to make a tourist destination of it, a veritable circus. Should Grigoris sell to this man? Would that not compound his grief? He thought that surely none of his twenty-six siblings would ever speak to him again. Perhaps they would even disown him, sending him out from the family in disgrace.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">In the end, he made the only sensible decision - he would approach his family, and ask them to buy it. To his relief, Gregor and Francesca were willing to pool their resources and purchase the prized gazebo from Grigoris. Though they were hard-pressed to afford it, they recouped their cost through reasonable and respectful marketing as a modest tourist destination.</span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Grigoris reformed his old foolish habits, and to his dying day, never again found himself in debt. After a long and satisfying life, he passed out of this world having won the respect of his family and all who knew him.</span></div><div style="background-color: transparent; font-family: 'Times New Roman'; font-size: medium; "><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><br /></span></div><div style="background-color: transparent; font-family: 'Times New Roman'; font-size: medium; "><span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><a href="http://penwag.com/story.html/88">Read this at PenWag</a></span></div>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com1tag:blogger.com,1999:blog-8321077109444530317.post-126731121531827672011-04-25T16:40:00.000-07:002011-04-26T06:31:59.594-07:00Using JUnit To Generate Mockito Custom ArgumentMatchers DescriptionsI'm a huge fan of Mockito. It has made our unit testing lives a veritable cakewalk for years now. There is something I'd like to be different, and that's what it provides for failure descriptions in custom argument matchers. It seems like a lot of work (well, relatively speaking) to provide a description then return false, assuming a test fails. It works out to about four lines of code for each value to be tested, if you use it something like this:<div><br /></div><div><div><div style="background-color: transparent; "><div style="background-color: transparent; "><div style="background-color: transparent; "><div style="background-color: transparent; "><span class="Apple-style-span"><span class="Apple-style-span" style="font-size: 12px; white-space: pre-wrap;"><div style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal; font-size: medium; "><span id="internal-source-marker_0.23101179534569383" style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">class IsPopulatedDataHolder extends ArgumentMatcher<DataHolder> {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>private final int expectedValue;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>private String failure;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>public IsPopulatedDataHolder(int expectedValue) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>this.expectedValue = expectedValue;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>@Override</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>public boolean matches(Object argument) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>DataHolder holder = (DataHolder) argument;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>if(holder.getValue() != expectedValue) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>failure = "Held value does not match. Expected " + expectedValue + " but was " + holder.getValue();</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>return false;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>return true;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>@Override</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>public void describeTo(Description description) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>description.appendText(":" + failure);</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">}</span></div><div style="background-color: transparent; font-family: 'Times New Roman'; white-space: normal; font-size: medium; "><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><br /></span></div></span></span></div></div></div></div></div></div><div>Look at the description it builds. It looks suspiciously like a JUnit assertion message. We should let JUnit build the message for us. You can just throw the AssertionError out of matches(), but then you lose the nice Mockito-provided stack trace that hooks in with your IDE.</div><div><br /></div><div>Have a look at this extension of the ArgumentMatcher. We'll catch the AssertionError, and stuff the message into Mockito's failure description:</div><div><br /></div><div><div style="background-color: transparent; font-family: 'Times New Roman'; font-size: medium; "><span id="internal-source-marker_0.23101179534569383" style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">public abstract class AssertConvertingArgumentMatcher<T> extends ArgumentMatcher<T> {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>private String failure = null;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>@Override</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>public void describeTo(Description description) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>description.appendText(failure);</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>@SuppressWarnings("unchecked")</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>@Override</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>public boolean matches(Object argument) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>try {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>verify((T) argument);</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>} catch (AssertionError e) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>failure = e.getMessage();</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>return false;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>return true;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>protected abstract void verify(T argument);</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">}</span></div></div><div><br /></div><div>Given this class, we can now use a JUnit Assert to build the string for us with a one-liner:</div><div><br /></div><div><div style="background-color: transparent; font-family: 'Times New Roman'; font-size: medium; "><span id="internal-source-marker_0.23101179534569383" style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">class IsPopulatedDataHolder extends AssertConvertingArgumentMatcher<DataHolder> {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>private final int expectedValue;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>public IsPopulatedDataHolder(int expectedValue) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>this.expectedValue = expectedValue;</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "></span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>@Override</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>protected void verify(DataHolder argument) {</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span><span class="Apple-tab-span" style="white-space: pre; "> </span>Assert.assertEquals("Held value", expectedValue, argument.getValue());</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span class="Apple-tab-span" style="white-space: pre; "> </span>}</span><br /><span style="font-size: 9pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">}</span><br /></div></div><div><br /></div><div>Normally if we use an Assert in matches(), we lose the failure stack that Mockito so nicely provides for us. AssertConvertingArgumentMatcher:<div><div><div><ul><li><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">Ties together the descriptive JUnit error with the Mockito custom matcher</span></li><li><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">Reduces lines to test the target code</span></li><li><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">Keeps intact the standard </span><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">mockito stack trace (double-click drill-into-test, double-click drill-into-target)</span></li><li><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">Uses JUnit to build "Line width expected: <2.0> but was <1.0>," and this means no hand-coding of descriptions. JUnit builds the description for us.</span></li><li><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">Assert is one line instead of if(this) etc,etc</span></li></ul></div><div>I've put some code samples here:</div><div><br /></div><div>git://github.com/DonBranson/MockitoMatcherExamples.git</div><div><br /></div></div></div></div>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-31753721287536620762010-06-29T06:01:00.001-07:002010-07-14T14:06:16.300-07:00Favorite Project Series - The Data Federation Service<div face="Georgia" size="10pt" style="margin-top: 6px; margin-right: 6px; margin-bottom: 6px; margin-left: 6px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); min-height: 1100px; counter-reset: __goog_page__ 0; line-height: normal; "><span class="Apple-style-span" style="font-size: medium;">The Data Federation Service (DFS) is a project that we did (in 2006-2007?) for a client as part of a much larger project. The goal of DFS was to provide a framework for distributing documents to users around the globe, many of whom would be on very slow and/or very unreliable networks. A document in this context is any kind of file that contains published information - it might be a text document, a spreadsheet, an image, a diagram, or anything else. To the DFS, they're just files.<br /><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">The system had to run on hardware that was already largely in place. There was a primary node with failover at the highest layer, an intermediate layer, and layer of regional nodes distributed around the world. The regional nodes acted as connection points for mobile devices.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">The goals of the system were to 1) provide a web-based façade for document authors to publish documents that might be of global or regional interest, 2) distribute global documents to all the regional servers and regional documents to a regional server plus an alternate server that would act as that server's backup, 3) provide redundancy alternate paths for document delivery in case of server failures in the middle layer, 4) a mechanism to deliver annotations to documents back to the author for vetting and addition to the document, 5) provide pluggable reachback to external data sources, 6) allow users of mobile devices to flag documents that they wanted pushed all the way to their mobile devices whenever updates are available, 7) store metadata about the documents that describe the document and the source, and can be used for filtering, 8) provide a complete list of available documents to all of the regional servers, so that mobile users could search for and retrieve documents that are targeted to other regions, and 9) provide command-line and service interfaces for programmatic interaction with the DFS.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">The system required 99.9% availability for document publication. The mobile devices existed in an environment where network connections might be sporadically unavailable, or even unavailable for weeks before connectivity was restored. They needed to get whatever updates were available when the reconnected. Furthermore, the actual NICs as exposed on the mobile devices by the O/S to the application could come and go, and the application needed to be able to tell when a NIC became available. The application had to detect network availability and retrieve new documents.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">The team varied over time, but included five (and more, at brief intervals) developers over the course of the project. Working on a distributed system like this creates some complexities that a lot of developers wouldn't necessarily consider. For example, we wanted to be able to delete documents. No problem, right? Well, what about annotations that might come in weeks or months after a document is deleted? The answer was to simply mark documents for deletion, and not physically delete them. This allows then, that an author can be notified of an annotations to a deleted document, and see if the document needs to be re-activated, or if the annotation provides information needs to be attached to another document. Fortunately, the folks on the team were able to spend the time that it takes to dig into it and understand the distributed ramifications of code changes and design decisions.</span></div><span class="Apple-style-span" style="font-size: medium;"><br />Some of the key technologies involved:<br /><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">JmDNS - Used to detect server presence. This was where we learned that Java's ConcurrentHashMap can sometimes throw a ConcurrentModificationException, requiring us to handle it and recover from it, so that server availability was reliably detected.</span></div><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;"><br />Servlets - WebServices were considered and rejected because we needed to stream data through a series of nodes so that the latency hits would overlap and reduce overall delivery time from the primaries or external sources all the way to the regional servers and mobile devices. The required servlets were mostly hosted on Tomcat engines. I originally scoffed at the idea of running them under Jetty - but I had a wrong view of what Jetty could do for us, and we could have replaced all the Tomcats with Jetty.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">JMS - document publication triggers multiple notification messages to the intermediate layer. A regional document for, say, the North America region, would trigger two notifications to the North America regional node and two to its backup node, which might be the Europe node. Two (or more) messages are triggered, and each goes through a different node in the intermediate layer, providing redundancy for the message delivery. If a node in the intermediate layer is down, delaying delivery of one of the messages, the alternate message can still go through.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">One of the key attributes of the DFS was the ability to live in a highly unreliable network environment. Connections could come and go, which means that we needed a JMS provider that would reliably reconnect. We started initially with ActiveMQ. It's easy to configure via Spring, and has some nice automatic features that reduced the amount of manual configuration needed. During endurance testing where I ran a series of integration tests over a period of about two days, I found ActiveMQ would reconnect after a server failure/recovery between 60-70% of the time. We tried a number of different solutions, but nothing worked to raise that percentage. I tried Joram as an alternative and found that it was well-suited to behave reliably on an unreliable network, and would reconnect 100% of the time. We did have problems with Joram's distributed JNDI at the time (I think that's since been resolved), so I used local JNDI and wrote a bridge to move messages between nodes, meaning that the rest of the DFS only had to perform local JNDI queries. Joram stood up well in short tests of a couple days and in longer endurance tests of up to two or three weeks.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">The architecture relies on the guaranteed delivery that JMS provides to ensure that each node will receive the message if connectivity is available. If a node is down, it will receive the message when it comes up. Each message provides the document's MD5 and server location for the identification and retrieval of the document. When a node receives a message, it turns around and attempts to fetch the document from the originating server. If retrieval fails, the message remains on the JMS queue and an attempt is made later. When the alternate message comes through, the regional node can determine from the MD5 that it's already pulled the document, and can discard the second message. This is the basic reason for using notification messages instead of document delivery messages - to avoid the unnecessary duplication of the document on the wire.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">Each layer is like the next. That is, there's nothing special about how one layer retrieves documents from the next layer up. So, additional layers can be added if desired. Mobile users have a complete directory, which means they can request documents that are not on there regional server, and therefore have to be fetched from the primary server or from another regional server. Fetching from other regional servers is tried first to distribute the load.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">A portlet provided the front-end for authors. It was, unfortunately, kind of clunky, but served as a starting point where a better wrapper could be written over the underlying framework. The only thing really interesting here is that we discovered that the way we used the file upload utility caused three copies of documents to be held in memory during the upload process. Yuck.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">As it turned out, this solution ended up sitting on a shelf. Why? Because the three or so applications that were targeted to use this framework for document distribution were delayed and eventually cancelled.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">After that, we began to look for opportunities to apply this architecture elsewhere. Given that it's a lot easier to sell something that has a GUI than it is for something that's just a bunch of wiring with a clunky portlet, I took an OpenMap earthquake application that I wrote at home and modified it to fetch its earthquake section data from the DFS. The application displays a world map, pulls NEIC data that we published to the DFS, and displays the earthquake events on the map.</span></div><span class="Apple-style-span" style="font-size: medium;"><br /></span><div style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size: medium;">Why is this a favorite project, despite that fact that it's now collecting dust? Because of the extra challenges presented when designing applications that are distributed. It's an extra dimension of complexity above and beyond what a basic web app or gui app requires, and that makes a project like this quite a bit of fun. Another reason is the people on the team. We had a small, competent group that was dedicated to code quality and more importantly, to product quality.</span></div><div><br /></div></div>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-51868930332800865162010-01-30T02:37:00.000-08:002010-01-30T04:12:07.095-08:00Weblogic JMS, The PointBase 30MB Limit, And Switching To MySQL<h4><span class="Apple-style-span" style="font-size:small;">Overview</span></h4><span class="Apple-style-span" style="font-size:small;">I've been running into this problem lately that occurs when dumping messages into Weblogic JMS queues on a developer's workstation. The underlying problem is that the PointBase database provided with Weblogic has a hard-and-fast size limit of 30MB. The last thing the company I work for wants to spend money on is database licenses on developers' boxes, especially when there are plenty of free options available.<br /><br />I'm pretty comfortable with MySQL, and always have it installed on any developer box I'm using. But, Weblogic supports a </span><span style="font-weight:bold;"><span class="Apple-style-span" style="font-size:small;">long</span></span><span class="Apple-style-span" style="font-size:small;"> list of alternative DB types, so pick one you like: Adabase, Cloudscape, DB2, Derby, EnterpriseDB, FirstSQl, MS/DB, Informix, Ingres, MS SQL, MaxDB, Oracle, PostgresSQL, Progress, and Sybase.<br /><br />My original goal was to rip out PointBase entirely, and use MySQL exclusively. There may be a way to do that, but it seems that it's a matter of going through and replacing each configured PointBase datasource with a MySQL datasource, then switching over. In the meantime, I just replaced the one datasource I needed to store JMS messages. Assuming you already have Weblogic and your database of choice installed, it boils down to about four steps: Creating the database, creating the datasource, creating the JDBC store, and creating a JMS JDBC store. Most of the info is from links I hunted down and pulled together to make this list, so links back to the original docs are included.<br /><br /></span><h4><span class="Apple-style-span" style="font-size:small;">Creating The Database</span></h4><span class="Apple-style-span" style="font-size:small;">Okay, this one's really hard. ;) Here's the MySQL command:</span><pre><span class="Apple-style-span" style="font-size:small;">mysql -uroot -e 'create database wls'<br /></span></pre><span class="Apple-style-span" style="font-size:small;">I just picked the DB name. We'll use it later. The name doesn't matter, just be consistent.<br /><br /></span><h4><span class="Apple-style-span" style="font-size:small;">Creating The JDBC Data Source</span></h4><div><span class="Apple-style-span" style="font-family:Verdana;"><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Navigate Services->JDBC->DataSources->New. The name and JDNI name don't matter, again, be consistent.</span></span></span></li><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Name = mysql-wls</span></span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">JNDI name = </span></span></span><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">jdbc/mysql/wls</span></span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Database type = mysql</span></span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Driver = com.mysql.jdbc.Driver</span></span></span></li></ul></ul><div><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Click Next. I took the defaults:</span></span></span></li><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Supports Global checked</span></span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">One-phase commit</span></span></span></li></ul></ul><div><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Click Next.</span></span></span></li><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Database name = wls</span></span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Host = localhost</span></span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">User = root</span></span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Password = <your root's pswd></span></span></span></li></ul></ul><div><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Services->JDBC->DataSources->mysql-wls->Targets</span></span></span></li><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-family:Arial;"><span class="Apple-style-span" style="font-size:small;">Servers=examplesServer</span></span></span></li></ul></ul></span></div><div><b><span class="Apple-style-span" style="font-size:small;"><br /></span></b></div><div><b><span class="Apple-style-span" style="font-size:small;"><br /></span></b></div><div><span class="Apple-style-span" style="font-family:arial;"><span class="Apple-style-span" style=" font-weight: bold; font-family:Georgia, serif;"><span class="Apple-style-span" style="font-size:small;">Creating The JDBC Store</span></span><span class="Apple-style-span" style="font-size:small;"><br /></span></span><span class="Apple-style-span" style="font-size:small;"><br /></span><span class="Apple-style-span" style="font-family:Verdana;"><div style="margin-top: 0px; margin-bottom: 0px; "><a href="http://download.oracle.com/docs/cd/E12840_01/wls/docs103/ConsoleHelp/taskhelp/stores/CreateJDBCStores.html"><span><span class="Apple-style-span" style="font-size:small;">http://download.oracle.com/docs/cd/E12840_01/wls/docs103/ConsoleHelp/taskhelp/stores/CreateJDBCStores.html</span></span></a></div><div style="margin-top: 0px; margin-bottom: 0px; "><div><span class="Apple-style-span" style="font-size:small;"><br /></span></div><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-size:small;">Services->Persistent Stores->New->Create JDBC Store</span></span></li><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-size:small;">Name=</span></span><span><span class="Apple-style-span" style="font-size:small;">mysql-jms</span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-size:small;">Target=examplesServer</span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-size:small;">Datasource=mysql-wls</span></span></li><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-size:small;">Prefix=jms_</span></span></li></ul></ul></div></span><h4><br /></h4><h4><span class="Apple-style-span" style="font-size:small;">Creating The JMS JDBC Store</span></h4><div><br /></div><span class="Apple-style-span" style="font-family:Verdana;"><span><a href="http://download.oracle.com/docs/cd/E12840_01/wls/docs103/config_wls/store.html#wp1142690"><span class="Apple-style-span" style="font-size:small;">http://download.oracle.com/docs/cd/E12840_01/wls/docs103/config_wls/store.html#wp1142690</span></a></span></span></div><div><span class="Apple-style-span" style="font-family:Verdana;"><span class="Apple-style-span" style="font-size:small;"><br /></span></span></div><div><span><span class="Apple-style-span" style="font-size:small;"><a href="http://download.oracle.com/docs/cd/E12840_01/wls/docs103/config_wls/store.html#wp1142690"></a></span></span><span class="Apple-style-span" style="font-family:Verdana;"><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-size:small;">Navigate to Services->Messaging->JMS Servers->examplesJMSServer</span></span></li><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span><span class="Apple-style-span" style="font-size:small;">Persistent Store->mysql-jms</span></span></li></ul></ul></span><span class="Apple-style-span" style="font-size:small;"><br /></span></div><div><span class="Apple-style-span" style="font-size:small;">As a final thought, you may want to make your Queues persistent. ;) There's not much point setting this up if you're keeping your messages in memory:</span></div><div><span class="Apple-style-span" style="font-size:small;"><br /></span></div><div><span class="Apple-style-span" style="font-family:Verdana;"><div style="margin-top: 0px; margin-bottom: 0px; "><ul style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><span class="Apple-style-span" style="font-size:small;">Navigate to Services->Messaging->JMS Modules-><module>-><queue> ->Overrides,Delivery Mode Override=Persistent</span></li><div><span class="Apple-style-span" style="font-size:small;"><br /></span></div></ul></div></span></div><div><br /></div>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com2tag:blogger.com,1999:blog-8321077109444530317.post-55228408285528353262009-12-19T11:26:00.000-08:002009-12-19T11:27:00.235-08:00Adsense Ads and GWT - Making it work.It seems like a lot of people have had this same problem, but I haven't found anywhere on the net where someone has found a solution. Here's how I got it to work. If you find this post helpful, I would ask the favor that you check out <a href="http://penwag.com">http://penwag.com</a>, and ask your friends to do the same.<br /><br />I struggled for a long time trying to get an Adsense ad to appear in a <div>, but that seems to be the wrong approach. Divs are nice for styling reasons, but it seems that Adsense knows when it's in a div, and won't display.<br /><br />I avoided IFrames (which is what the GWT Frame object compiles to) because sizing isn't automatic. Eventually, though, it became apparent that IFrames were the way to go, since Adsense ads will load in them. I create an IFrame and point it at a static page that contains the necessary Adsense script. That just works. The content loads correctly, and ads will display.<br /><br />But here's the rub. IFrames need to be sized with custom javascript. I use this javascript: <a href="https://penwag.com/home/iframe.js">https://penwag.com/home/iframe.js</a>. This works easily for all browsers except - you guessed it - IE. Below is the GWT code that I use to bring it all together, including a work-around for IE.<br /><br /><pre><br /> public static native String getUserAgent() /*-{<br /> return navigator.userAgent.toLowerCase();<br /> }-*/;<br /><br /> private Widget buildMainPanel() {<br /> Widget mainPanel;<br /> if(getUserAgent().contains("msie")) {<br /> mainPanel = buildIEPanel();<br /> } else {<br /> mainPanel = buildNonIEPanel();<br /> }<br /><br /> mainPanel.getElement().setId(getPanelId());<br /> mainPanel.addStyleName(Styles.StaticPanel);<br /><br /> return mainPanel;<br /> }<br /><br /> private Widget buildNonIEPanel() {<br /> Frame mainPanel = new Frame();<br /> mainPanel.getElement().setAttribute("onLoad", "resizeCaller();");<br /> mainPanel.setUrl(getRootPage());<br /><br /> return mainPanel;<br /> }<br /><br /> private Panel buildIEPanel() {<br /> Panel mainPanel = new VerticalPanel();<br /><br /> HTML adBar = new HTML("Loading...");<br /> mainPanel.add(adBar);<br /><br /> try {<br /> RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, GWT.getModuleBaseURL() + "ie_ads/index.html");<br /> builder.sendRequest(null, new RequestHandler(adBar, null));<br /> } catch (RequestException e) {<br /> adBar.setHTML("");<br /> }<br /><br /> HTML content = new HTML("Loading...");<br /> mainPanel.add(content);<br /><br /> String errorMessage = "Failed to load content, please try again later.";<br /> try {<br /> RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, GWT.getModuleBaseURL() + getRootPage());<br /> builder.sendRequest(null, new RequestHandler(content, errorMessage));<br /> } catch (RequestException e) {<br /> content.setHTML(errorMessage);<br /> }<br /><br /> return mainPanel;<br /> }<br /></pre>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com3tag:blogger.com,1999:blog-8321077109444530317.post-56043155352874676412009-10-24T07:53:00.001-07:002009-10-31T12:06:20.108-07:00A New StarI'd like to introduce you all to an up-and-coming star in the universe of photography. Also, he's my son, Taylor. I may be biased.<br /><br />Please take a moment to visit his web site <a href="http://photoimageography.com/">PhotoImageOgraphy</a>. I think you'll be glad you did.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-64400902525370770082009-09-13T04:59:00.000-07:002009-09-13T05:11:05.499-07:00Ozark Trail Volunteer WorkWell, after a couple of years <span style="font-style:italic;">using</span> the Ozark Trail, I finally got around to helping to <span style="font-style:italic;">build</span> the Ozark Trail on the <a href="http://www.ozarktrail.com/courtois.php">Courtois Section</a>. (It's pronounced Code-Away.) What a tremendous experience. The people were great to work with, and great to hang out with after the work was done for the day. And the food, thanks to Jeff The Chef, really hit the spot. Nothing like a couple burgers and a few brats to make the day complete.<br /><br />The group included some tremendously hard workers on the rock-wall team, guys I couldn't even begin to keep up with energy-wise, including Scotty from the USFS, Russ, Gabe, Charles and others. Good work, guys.<br /><br />No injuries, either, which is good. Couple close calls, though - the hill was about a 25% grade, and a few of the boulders got away from us.<br /><br />What a great bunch of folks to hang out with. I'm looking forward to the next event.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com1tag:blogger.com,1999:blog-8321077109444530317.post-76461282333094683462009-07-14T04:27:00.001-07:002009-07-14T06:11:39.781-07:00The Value Of Pictures In Software DesignThere are some very good reasons why software engineers use visual communication to quickly and effectively transfer knowledge from one person to another.<br /><br />While people have <a href="http://www.learning-styles-online.com/inventory/">many different learning styles</a>, and while everyone employs all of the styles to a greater or lesser degree, most people, or at least, enough people to matter, are predominantly visual learners. Various sources claim that <a href="http://www.grapplearts.com/Learning-Styles-in-Grappling.htm">around 60% of us are visual learners</a>. Therefore, it's worthwhile to use visual techniques for this reason.<br /><br />Visual communication transfers information at a very high rate compared with aural and textual communication. You can tell with a glance a system's structure, or lack thereof. A verbal description takes longer.<br /><br />Visual communication helps the sender, too. That is, the person creating the graphical representation has to understand the system well enough to draw it. This applies to verbal and written communication as well, so visual does not necessarily hold an advantage over other forms, but it's certainly a valid approach.<br /><br />Visual communication helps a newcomer to the team come up to speed and become productive more rapidly.<br /><br />Furthermore, graphical representations of software systems can reveal flaws, voids, and redundancies that are not immediately obvious in verbal or written communication. How many times have you drawn a system diagram, only to see that there's something that can be cleaned up? If you have not done this, try it - it's a worthwhile exercise.<br /><br />To illustrate the value of pictures, let me point you at one that someone else drew, one that helped me to quickly understand a software framework's design and intended usage. I'm talking about the <a href="http://mina.apache.org/mina-based-application-architecture.html">design of Mina</a>, Apache's ongoing effort to wrap the basic Java NIO components. I think the graphics they provide are a great example of what we should be doing on our own projects.<br /><br />Why is it important to understand the need for visual communication in software development? I've noticed something unsettling on the last couple projects I've been on: no graphical representations of the systems. In each case, there was no perceived need or gain to having images. For those of us who have been on non-trivial projects and witnessed the indispensable benefits of this form of communication, this is a red flag.<br /><br />This red flag is a reliable indicator of a project in trouble. The particular dysfunctions might take one of several forms, but most likely is a combination of them. I'll try to name a few here, without trying to make an exhaustive list.<br /><br />First, it means that the system probably has no clear direction. The team doesn't know where it's headed, or at least doesn't have a common vision of the goal.A shiny new a idea comes along, it's legitimately cool, and we go down that track. And that's great - provided it complements the existing framework. We can't follow all of those cool ideas. Some we'll have to back-burner for another day or another project. A solid understanding of a system as a whole, bolstered by few good drawings, can help us stay disciplined and on the road toward our goal. The images help to remind us of the goal, and to keep us from working at cross-purposes.<br /><br />A lack of graphical communication might indicate that the team is <span style="font-style:italic;">unable</span> to create them. The system has grown disorganized and chaotic over time (that is separation of concerns is largely gone), and the team, however good they are, cannot walk up to a whiteboard or show on paper a cohesive, overall design. The discipline of striving to achieve the goal of always having something drawn (simple, not 200 pages), drives us to keep the system cohesive and well-organized.<br /><br />A lack of drawing might indicate something far worse - that the team refuses to draw them. This might be from fear that the drawings will become stale (a legitimate, but addressable concern), or because it takes time from coding, or from a misapplied development philosophy, from plain and simple laziness, or because of a lack of experience building complex systems. Even with current development paradigms that eschew grotesquely large architectural documents, some documentation is essential.<br /><br />It's this last statement that seems be key element on the last couple projects I've been on. Agile development philosophies encourage us to limit the amount of useless documentation that gets created. This is a worthy and noble goal. Sadly, some have twisted the intent of these goals, eliminating strong, time-tested tools from their arsenal, to the detriment of the projects and teams they represent.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-78220302748272485682009-07-09T05:14:00.000-07:002009-07-09T05:17:54.163-07:00John Roth, founder of the Ozark Trail Association, has diedThis is sad news, indeed. I only ever "met" John via email and discussion forums, but never had the privilege of meeting him in person. As I read the notes from the forum linked below, it's clear what a void his passing leaves.<br /><br /><a href="http://ozarktrail.com/">Ozark Trail home page</a><br /><a href="http://ozarktrail.com/forum/viewtopic.php?f=1&t=443&sid=c85d9b165617d3adf86bf0327dab3c96&start=15">Forum notes</a>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-7929146093272744902009-07-04T14:30:00.000-07:002009-07-04T14:52:28.534-07:00A Worser Mousetrap<tirade><br /><br />I have this, uh, friend, that has a rodent problem. Yes...a friend has this rodent problem.<br /><br />So anyway, in order to help this, uh, friend, with his rodent problem, I used to buy this <a href="http://www.victorpest.com/store/rodent-control/M150">Victor snap trap</a> at <a href="http://www.screwedcentral.com/ubb/Forum1/HTML/000484.html">Lowes</a>. Now, they don't sell those any more, they sell this <a href="http://www.tomcatbrand.com/product/19-wooden-mouse-trap">Tomcat snap trap</a> instead. They're very similar, except that the Tomcat is $0.78 instead of $1.10. There is one other minor difference - the Victors work. They actually catch mice. The Tomcats don't. They suck, actually. The mice come, take the bait, and leave. The traps don't trip. I even jammed the bait into the little holes so that they'd really have to apply a lot of force to get it - end the traps still don't trip. I would've been happy to spring for the extra 32 cents even, for a trap that just ^*(&^*&^%$%# works.<br /><br />I wouldn't even be griping here if I hadn't just come back from <a href="http://www.techdirt.com/articles/20070924/040616.shtml">Lowe's</a>, having returned a branch cutter that pulled apart during normal operation: I was cutting a redbud branch about the size of my thumb. Forty bucks and it lasted about an hour. Jeez, what a piece of crap.<br /><br />Next time you and the kids want to have fun, go play this game. It's called <a href="http://open.salon.com/blog/ardee/2009/06/17/buyer_beware_more_lead_from_china_now_at_lowes">Made in America</a>. Go into your local <a href="http://consumerist.com/171883/lowes-blows">Lowes</a>, and try to find a product that's made in America. Set a time limit, though. If you haven't found something after an hour, it's time to give up.<br /><br /></tirade>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-40916592898230931692009-06-13T13:31:00.000-07:002009-06-13T13:34:38.307-07:00Seeking Input For My Next Opensource ProjectFriends,<br /><br />I'm seeking your input to help me think about my next opensource project. I have two ideas, either one of which I'd like to do, probably using Java as the primary language, just as a matter of preference. I'd particularly like to know whether either is already being done, so that I don't duplicate work, and whether or not you think it might be something useful in work that you've done.<br /><br />The first is a SOA Directory Service alternative to UDDI. When I worked to help implement a middleware/SOA framework in the mid-90s, one of the pieces we built was a directory service. While it didn't offer the metadata storage capability that UDDI offers today, it had some advantages over UDDI. It was simple: easy to register services, do lookups, etc. It was very fast. Services registered using a lease mechanism, so you could get a list of matching service instances, knowing that the instances were probably still up. Next, it was replicated. Certainly, many UDDI implementations support replication. What I don't want to do is create another UDDI implementation, but rather to build an alternative Directory Service that is more like what we did in the 90s, consistent with today's framework needs, but more lightweight. To my mind, UDDI is far more heavyweight a solutions than most enterprises need, and a simpler solution might offer some appeal, provided that it integrated well with whatever framework they're already using. That is, that it would be easy to choose it as an alternative to UDDI.<br /><br />The second possibility is to do an opensource implementation of a data federation system. We built one for a client that was never used, but there were some good ideas in there. I'd like to do it again as an opensource project, because it offers some useful capabilities. It essentially allows users to publish documents to a master node, then replicate documents to regional servers, that is, to push the data close to where it would be used within the organization. For example, if a document were flagged as pertinent to an organization's European region, it would be be pushed to that region's server, and to its backup server in a neighboring region. Users in the region can then make annotations to the documents as needed, and push those back to the original author for consideration. A federation system such as this offers some availability and performance benefits relative to having a monolithic document server. When a user wants a document that not stored in his or her region, the system goes back to the master or another regional server to fetch it. As an added capability, the original system supported plugins that could fetch data from external sources, and that might be useful to include.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com3tag:blogger.com,1999:blog-8321077109444530317.post-14836282038386362142009-06-07T16:22:00.001-07:002009-06-07T16:26:47.553-07:00We Don't Have Time To Skip That StepIn the mid-90s I was privileged to be on a team building a service-oriented middleware architecture (Datagate), which I have mentioned before. We used an underlying library that implemented XDR, for which we had no unit tests. Since much of our software was built on this and a couple other core technologies, it was important that they be as solid as possible. Make the foundation solid, and the rest will follow. I decided to build a suite of unit tests against this software, suspecting that there were some bugs in there. Using a coverage tool, I wrote tests that covered the entire XDR library, and we found that the suite would not run on one of our twenty or so supported platforms. We fixed that bug in the library.<br /><br />After that, we found that one of our nagging bugs went away in our Directory Service. It turns out that the two were related. I took some time to build the test suite, but not that much. It saved us time in supporting the Directory Service. It probably saved application and service developers time, too, but we didn't research that.<br /><br />After that, when it came to the value of testing, I told everyone who would listen, "We don't have time to skip that step." And it's still true today. Can you afford the extra time it takes to skip testing?Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com3tag:blogger.com,1999:blog-8321077109444530317.post-12239878521283594642009-06-06T13:22:00.000-07:002009-06-06T13:25:51.739-07:00OT Courtois Section Trip Report - 5/30 and 5/31I hiked the part of this section from Hazel Creek to Bass' River Resort last weekend, camping after it crosses FR2265 the first time, before the trail cuts west there. What a great weekend to hike - a little on the warm side, but tolerable by watching my pace. Finished up Sunday morning about 11 before the heat really got going, and sat in the shade by the creek, enjoying a couple free beers from some new-found friends and skipping a few stones. Good times.<br /><br />If you hike this direction, you need to know that when it goes into the field near Harmon Spring, you need to head forward across the field, taking the trail that goes slightly to the right. There's a trail to the left and to the right, and I went left, getting about a half-mile up the road before convincing myself that that wasn't the right way.<br /><br />Also, if you're going this way, make sure you get plenty of water at the Beecher Artesian Well spring, especially in warm weather. The next decent water isn't until you're almost at Bass', and a long trek on the gravel road section will surely dry you out. There is some water before there, but I decided it wasn't for me, even purified, and I'm not too picky.<br /><br />South of Highway 8, there are about 4 or 5 deadfalls. North of 8, maybe a couple.<br /><br />The trail's pretty horsey in sections - plow through, and it gets better.<br /><br />The ticks were bad, too. Had a couple in new places from this trip. Both standard ticks and seed ticks were on the prowl.<br /><br />Not a lot of people out - just saw three guys in the Berryman section, but there were at least three people ahead of me, but making better time.<br /><br />I really like this section. At least on the part I hiked, the hills are pretty docile, and there's some pretty trails through some really impressive pine groves.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-62692724449576466042009-06-04T15:10:00.000-07:002009-06-08T05:58:29.664-07:00Manage Your Eclipse Install With A Local Git RepositoryI had something of an epiphany this morning. If it wasn't the real thing, it sure felt like it. With Eclipse, I'm often trying out new plugins that purport to do one other or another, and there are usually a few that do the same thing, but with different features. So the quandary is, what do I do when I pick one of the several, try it, and don't like it? Perhaps it's hard to use, perhaps my IDE crashes more often than it used to, or perhaps one of the other similar plugins suddenly seems more appealing.<br /><br />Traditionally, I've solved the problem using rsync. I <span style="font-style:italic;">rsync -a eclipse/ eclipse.beforeWonderfulPlugin</span>, then install the plugin. If the plugin turns out to be a flop, I go back to the old version, which takes a little while. First, delete eclipse, then <span style="font-style:italic;">rsync -a eclipse.beforeWonderfulPlugin/ eclipse</span>. It's slow, but it easier and faster than say, <span style="font-style:italic;">cp</span> or <span style="font-style:italic;">tar</span>. Also, I now have (at least) two entire copies of eclipse laying around.<br /><br />There are further issues, too, though they're perhaps less important. It's hard to remember what plugins I have installed, for example, above and beyond what's already included.<br /><br />Lately I've making the move to <span style="font-style:italic;">git</span> to manage source for various projects. Then it hit me - I should make my Eclipse install a git repository. So that's what I did. There's a <span style="font-style:italic;">master</span> branch - that's where the downloads from Eclipse go - ganymede->ganymedeSR1->ganymedeSR2, and so forth. Then, there's the <span style="font-style:italic;">working-branch</span>, which is where I'll normally run from. When I'm trying a new plugin, I'll branch off of working-branch, try the plugin for a while. If it's good, I'll merge it back into working-branch.<br /><br />Presumably, there will be an Eclipse Ganymede SR3 at some point, and I'll rebase there. When Galileo comes out, I'll put that on master, and probably start a new working-branch.<br /><br />If I merge a plugin into working-branch, and find out a week later that there's a problem, it's easy to re-branch from a point prior to the merge, and get rid of the plugin. Sometimes it's the case that there's some quirky behavior I didn't notice at first. It's a matter of a few minutes to go back to a previous version, try it out, and see if the quirky behavior was there all along, or started with some new plugin.<br /><br />Sure, Eclipse lets you uninstall plugins, most of the time, but there are often problems with that.<br /><br />Could this be done with CVS, Subversion, ClearCase or other revision control systems? Very possibly - but not in practical terms, simply because of the performance issues.<br /><br />If there are alternative plugins, say, Subclipse and Subversive, and I want to try them both, I can have them both on separate branches off of working-branch, and explore them both for a while before picking one. As an added bonus, <span style="font-style:italic;">gitk</span> lets me see where I've been, and when I started using which plugins.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com5tag:blogger.com,1999:blog-8321077109444530317.post-67582386821642353982009-05-24T05:43:00.000-07:002009-05-24T14:37:32.946-07:00Too Much Testing?I recently heard this question:<br /><br />"Is too much focus on testing benefits a bad thing overall?"<br /><br />TDD done well can improve readability. TDD done poorly, that is without consideration of other important principles, can reduce readability.<br /><br />A guy I worked with in the mid-90s would say "You can always make a system more flexible by adding a layer of indirection. You can always make a system simpler by removing a layer of indirection." Both flexibility and simplicity are important qualities of a system. The two principles can often live together in harmony, but often they work against each other. If you go too far towards one extreme or the other, you move away from the ideal that exists where these two principles are balanced.<br /><br />TDD is partly about testing, partly about design. TDD done poorly can tend too much towards either flexibility or simplicity. It can push towards too much flexibility. The objects become more testable, and often simpler, but the inherent complexity of the domain problem then is pushed out of the objects into the interaction of the objects. We gained flexibility, and to the naïve eye, it can look as though we've gained simplicity because our objects are simpler. The complexity, however, is still there. It's moved out of the objects, and into the object interaction, where it's harder to control. There are code smells that can act as red flags here - a system with hundreds of small objects and no larger objects is one, lots of objects with only one-line methods is another.<br /><br />TDD done poorly can move in the other direction as well, that is, towards too much simplicity. So, we do TDD by writing the test first, but it has little impact on our design. We still have long methods and huge objects, and those are code smells that can red-flag this problem.<br /><br />Now TDD will not by its nature knock you off-balance in either direction, provided it's well-applied. Use other practices to keep you on track. For example, draw pictures of what you're doing before you do it. Obviously, not all the time. Some things are far too simple for that. Some pictures are worth saving, some are just sketches that help us to visualize the problem, and we are, by varying degrees, mostly visual learners. If you can't draw a picture of the problem, you don't understand it.<br /><br />How will this help with TDD? It will help to keep a system from going too far on the flexibility side, away from the simplicity side. If you draw a picture and it's ugly, that's a red flag. Sometimes it's necessary, but often when you draw the picture, your mind will quickly see things that can be simplified. The solution becomes more elegant and simplified, easier to maintain, and more enjoyable to work on. If you can't or won't draw pictures of your system, you're losing this opportunity to make your software more solid, more elegant, more beautiful to see and easier to maintain.<br /><br />Applying this comes with experience, and some coders will never understand the value that a good balance provides. There's no metric that you can run that tells you you're in the right place. If someone gives you a prescribed method to arrive at that harmonious point, he's lying to you. More importantly, he's probably lying to himself without realizing it.<br /><br />So, my answer to his question is 'yes': test everything without forgetting the other good principles.<br /><br />Any good practice will throw you off-course if it's not balanced with other good practices.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-34064374363523790422009-05-23T05:35:00.000-07:002011-05-25T19:49:35.437-07:00Creating A Remote Branch With Git<div>If you're still doing this the way I suggested. Stop. Stop right now. Back away from the keyboard, and nobody gets hurt. Also, read the comments below, and do it the way Graham shows.</div><div><br /></div><div>Original post:</div><div><br /></div><div>----------------------------------------------------------------------</div><div><br /></div>So, I've seen this documented in a few places, and it seems like they all give you various hard ways to do it. Here's an easy way. But, given git, it's surely not the only easy way. I create remote branches as a way to self-collaborate on a new branch from the various boxes in my daily life. If you're not a computer geek, you probably don't know that by "box" I mean "machine." And by "machine" I mean "computer." But, if you're not a computer geek, you're probably not using git, in which case you're not reading this. In that case, stop now.<br /><br />Assume that you're in a clone of a remote repository. We'll create a remote branch named "mybranch." First create the branch locally:<br /><pre>>git branch mybranch</pre><br />Switch to that branch:<br /><pre>>git checkout mybranch<br />Switched to branch "mybranch"</pre><br />At this point, list all your branches:<br /><pre>>git branch -a<br /> master<br />* mybranch<br /> origin/HEAD<br /> origin/master</pre><br />Now, here's the command that creates the remote branch:<br /><pre>>git push --all<br />Total 0 (delta 0), reused 0 (delta 0)<br />To /tmp/remote.git<br />* [new branch] mybranch -> mybranch</pre><br />It's just that easy. Now, in another clone on another machine, I can self-collaborate by creating a local branch that mirrors the remote branch. First, list all the branches:<br /><pre>>git branch -a<br />* master<br /> origin/HEAD<br /> origin/master<br /> origin/mybranch</pre><br />And create a local branch that tracks with the remote branch:<br /><pre>>git checkout -tb mybranch origin/mybranch<br />Branch mybranch set up to track remote branch refs/remotes/origin/mybranch.<br />Switched to a new branch "mybranch"</pre><br />List the branches again to see the change:<br /><pre>>git branch -a<br /> master<br />* mybranch<br /> origin/HEAD<br /> origin/master<br /> origin/mybranch</pre><br />Run <span style="font-style:italic;">gitk</span> to see visually that your local branch is tracking the remote branch. Use git push and pull as you change machines to move the changes around. When the branch is done, you can merge it.<br /><br />I like git. I tried bazaar and mercurial, but git "just works" and is easier to get started with than bazaar. Plus, there's github. Visit me there at <a href="https://github.com/DonBranson">https://github.com/DonBranson</a>.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com24tag:blogger.com,1999:blog-8321077109444530317.post-31267860141353736432009-05-20T18:17:00.000-07:002009-05-20T18:23:46.419-07:00DonsProxy on githubI've been thinking about getting <a href="http://donsproxy.sourceforge.net/">DonsProxy</a> out on github for a bit now, but needed to break out the good stuff from the slough. So, it's finally out there. My next DonsProxy task is to get the SSL part working better - maybe by injecting Mina. Still trying some things out. You can clone DonsProxy from github at <a href="http://github.com/DonBranson/DonsProxy/">http://github.com/DonBranson/DonsProxy/</a>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-48736549870551043642009-05-07T17:17:00.000-07:002009-05-13T11:38:58.177-07:00Favorite Project Series - Datagate (14 Years of SOA)SOA is one of the big buzzwords these days, and a lot of people have started doing SOA for the first time in the past few years. I have some good memories of the "good old days of SOA," and now it seems like everybody's using the groundwork that was laid in the 80s and 90s. Now, people can just go download Weblogic and Systinet or JBoss, and and the various pieces, build a system using those frameworks, and they're "doing SOA." There are so many powerful tools; the IDEs such as Eclipse and Intellij do a lot of the tedious work for you.<br /><br />My first introduction to SOA was in 1995 on a project called Datagate at Southwestern Bell. I came on the project when it was about six months old. All of the things that people take for granted now, that they use and it "just works" (or not), we had to build our own. There was no app engine, no UDDI, no SOAP. The things that people download and use today, we <span style="font-style:italic;">built</span> and used back then. If you're a computer geek, this is the project of a lifetime. It was all message-based (that's a MOM for all you acronymophiles). Now I can look around and see SOA everywhere, but we were, in our own way, pioneers of the technology, because we did things no one had ever done before. Sure, not everything we did was new - Tuxedo had been around, so MOMs weren't exactly new.<br /><br />What we did in the mid-90s that was new was to make the technology more accessible. It was hard to write Tuxedo services back then. We made it accessible to every C, VB, and PowerBuilder coder out there. There are some ways in which today's technology is better. But, what we built was so smokin' fast, and there's nothing that SOA folks are doing today that even comes close to the performance we delivered on those old, slow machines. We had a great, usable middleware product that was the framework for developers to construct clients and reusable services on a variety of platforms - about 12 unixes, MVS running on IBM 390s, Windows 3.0, Windows NT, and Tandem. It's one of those experiences that you looked back on, and think, we did it before most anybody else. What people today <span style="font-style:italic;">use</span>, we <span style="font-style:italic;">built</span>.<br /><br />I wasn't one of the visionaries for that group. I was brought in to develop an interface so that VB and PB programmers could write clients on top of our C framework. In the end, I help design and architect that, the Directory Service Replicator, and the Dashboard for this system, and we did good work.<br /><br />There was no SOAP back then. The Datagate team developed its own protocol. It was message-based, so was much more performant, scalable, and simpler than the RPC-style calls are today. Yes, even the message-driven services are RPC under the covers, not the true MOMs like what we built. A true MOM lets you fire off multiple messages to services without waiting for the ack that the message was received, then process the responses as they come back in. All this with one thread, not multiple threads that RPC pushes you towards for this kind of processing.<br /><br />There was no UDDI then for service lookup back then. There was no LDAP, either. We built our own distributed, replicated X.500 directory service. Infrastruture and business services registered and reserved leases dynamically. If a service went down, its service entry went away, and clients would call one of the other instances that was still registered. And of course, it was fast - 10s of millis to do a service lookup, not 100s or 1000s like you see with UDDI. Smokin' fast.<br /><br />I architected and led the development of the Directory Replication Service along with my friend Dave C. This cell-based, scalable solution allowed us to run multiple Directory Services, and replicate data between them in a scalable, available fashion.<br /><br />We used XDR for marshalling. That's one piece we did download. I lead the charge to unit test the whole thing - and found only one bug. There was an endian issue on that affected one of the platforms, and we found it and fixed it.<br /><br />There were no application engines. What we built was a resource manager that would control service lifecycle and heartbeat services to quickly detect outages.<br /><br />There was no PKI, or even SSL. We bought licenses for the encryption libraries, then designed and built our own secure message protocol (with mutual authentication where needed), plus the infrastructure to support it. Certificate Revocation Service, Certificate Authentication Service, and al the APIs for services to use to load their certs, encrypt their messages. We built the services and the GUIs so that administrators could manage the PKI we built.<br /><br />There was monitoring software available - Tivoli - but it was far more than our budget would allow. We built our own Dashboard that allowed us to closely monitor infrastructure and business services running over much of the United States, including Missouri, Texas, California, and more.<br /><br />We did training to teach developers how to build reusable business in C, and how to build clients in C, in VB, and in PowerBuilder. I taught the VB and PowerBuilder classes.<br /><br />Our team consisted of three subteams - Infrastructure, which was the team I was on, the Service Writers team, and System Adminstration, which handled the care and feeding of production systems.<br /><br />At A.G. Edwards, we leveraged SOA frameworks for 6-1/2 years as part of a broker workstation, to develop the pretty scalable agedwards.com (which, last I heard some years back, had about 300,000 users signed up). I was a member of the Lead Architect Team that designed and developed agedwards.com. We used SOA for some other pretty cool stuff. One of my favorites was the BLServer, that used a modified version of what is now called the "Competing Consumers" pattern in a highly-available cluster of message-based services that self-allocated using leases to process roughly 1.5 million messages a day for about 4-1/2 years before it was mothballed in February of this year.<br /><br />The industry has come a long way since then. Like I said, what we built, you can now go download, and the SOA business has experience explosive growth. After 2-1/2 years at Bell on the Datagate project, I did 6-1/2 years of SOA work at A.G. Edwards, continuing to architect systems using new SOA technologies as they emerged - Tengah, (now Weblogic) Dynamo, Novell's LDAP server, SiteMinder, WSDs, Big/IP, and various JMS providers. Some of this stuff I had more exposure to than others, but that gives you an idea of how things have changed over the past 14 years. Now there's CXF and JAXB and other exciting new tools coming along.<br /><br />Every Tom, Dick and Harry does SOA now. Years after that work at Southwestern Bell, a small company hired a "SOA expert" (after I was already there!). This poor fellow didn't even know what LDAP is. When I described how it could be used for service lookup, he said you can't use it for that - it's for authentication. I explained to him that it's just a database that's optimized for reads. You can use it for anything that falls into that category. Now, he's an expert, downloading what other people have built and putting it all together. One boss at that company tried to get me to join a project where I would have been writing architecture documents all day for six months, and building nothing. His angle? That the SOA experience would look good on my resume. I kid you not.<br /><br />On Datagate, we built SOA sooner, faster, more scalable, and at least in some ways better than what exists today. The primary advantage I see in today's world is standards. Almost everything is standards-based, and that means vendors can provide tools to do many of the things we used vi for. It's a change for the better, in most every way, because SOA is much more accessible today that it was in 1995. Plus, now it's an acronym. ;)Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com1tag:blogger.com,1999:blog-8321077109444530317.post-29249672119696767062009-05-05T18:42:00.000-07:002009-05-05T19:30:11.175-07:00Migration from Subversion to GitI'm migrating from Subversion to Git. It's about time, of course - decentralized SCMs have some significant advantages over the old-style SCMS such as CVS, Clearcase, Subversion and the like. I looked at Bazaar, which touts "Bazaar is a distributed version control system that Just Works." Sadly, I couldn't get it to work. It needed a lot of python modules that I, not being a python guy, don't really know where to get. Found some, not others. Funny thing is, Git "just works." Sure, had some hiccups with converting from Subversion before realizing that I had made an incorrect assumption about the repository structure in one case. Looks cool, though, especially when you throw github in the mix. Good stuff.<br /><br />Recommended reading if you're thinking about converting from your current SCM to something new, whatever that may be: <a href="http://whygitisbetterthanx.com/">http://whygitisbetterthanx.com/</a>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-68094808790058504972009-05-05T18:40:00.000-07:002009-05-05T18:41:59.199-07:00Upgrade from GWT 1.5 to GWT 1.6Just a refer:<br /><br /><a href="http://penwag.blogspot.com/2009/05/upgrade-from-gwt-15-to-gwt-16.html">http://penwag.blogspot.com/2009/05/upgrade-from-gwt-15-to-gwt-16.html</a>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-6876281645534685322009-03-28T06:53:00.000-07:002009-03-28T09:56:30.705-07:00Tell Your Story!There's a web site that I'm building, <a href="https://penwag.com/">penwag.com</a>, that lets me enjoy two of my favorite interests: software development and story telling.<br /><span style="font-weight:bold;"><br />It lets me enjoy software development</span> in that I'm using two technologies that I will gain experience with: the Google Web Toolkit (GWT) and Hibernate. The GWT is a great tool from Google that provides a framework for building AJAX-style applications. The framework includes the necessary pieces for client-side and server-side development, and manages the communication between the layers. Add in Hibernate and a data layer, and you have everything you need for easily building AJAX web sites.<br /><span style="font-weight:bold;"><br />Then there's the story-telling side.</span> When I was very little, we visited my great-uncle Henry Shoop, who was my father's mother's brother. He was born near Cherryville, Missouri, but his family moved to California when he was six. Uncle Henry was a friend, a beekeeper, and one of the great storytellers, and I hung on his every word as he instilled in me a love for a well-told story. He was one of the best.<br /><br />Another favorite story-teller of mine was Earl Halbert, a family friend. You can see his picture here from <a href="http://photography.nationalgeographic.com/photography/enlarge/ozarks-man_pod_image.html">National Geographic Magazine</a>. He's the one on the left in the overalls. Earl was born in 1910 in rural Missouri, and lived through the Great Depression, Prohibition and numerous other hard times. He knew my great-grandfather Thomas Jefferson Branson, and had a few good stories to share about him as well.<br /><br />Both of these men, shall we say, <span style="font-style:italic;">infected</span> me with a love of a story told friend-to-friend, perhaps while sipping some sassafras tea, or around a campfire, or just hanging out in the living room. I miss both of them not just for their story-telling and their wit, but for their character as human beings.<br /><br />Certainly the art of a well-told story has not been lost, but is changing over time. How many people nowadays have had the chance to hear stories of hard times in the Great Depression, friends lost, good times, and funny times, from "way back when"? I'd hate to lose those stories. So join me, won't you, and let's tell each other those stories, like we're sitting over a beer or around a campfire, and let's enjoy a good laugh together, savor tales of clever dealings with shysters, and the occasional twist ending.<br /><br />Just head out to <a href="https://penwag.com">penwag.com</a>. You're getting in early at a site that will someday, hopefully, have many stories that we have shared with each other. But it's your stories that will make the site great. If you have even one story to share, come on out and put it up there.<br /><br />This site is in its early stages, but everything you need to add a story and read others' stories is already there. Since it's in its early stages, there's still a lot of work that I need to do add features. Some are pretty obvious - you can create a story, but can't edit the story to make corrections yet. I have a long list of features that I will add as the site grows. But the feature you want may not be on my list! So, click on the <a href="mailto:support@penwag.com">Contact Support</a> link, or just send an email to support@penwag.com, and give me a shout. Tell me what feature you want, and I'll add it to my list.<br /><br />Let me reiterate - this site's success depends on people like you who have a story to tell. As you add your stories, others will visit the site just to read a great story. The stories are short. Don't feel like you have to write a novel. Just write it how you would share it with your friends over a beer.<br /><br />Now, to get started, you have to register. For simplicity's sake, your email address is your user id, and you need to make up a password. Registration is quick and easy, and your email address will not be shared. If you still have questions or concerns, or if the site doesn't work right for you, use the Contact Support link and let me know!<br /><br />Thanks, and hope to see you out there. Come tell your story!Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-70754938859789273062009-03-25T05:00:00.001-07:002009-03-25T05:27:40.082-07:00OT Trip Report - Bell Mountain to Council Bluff LakeMy friend James and I hiked <a href="http://ozarktrail.com/planner/tripresultsoverview.php?id=214&activitytypeid=1">Council Bluff Lake to Bell Mountain</a> (reversed) March 22 and 23, in about 24 hours. It was our first hike of the year, and left me a little sore.<br /><br />The view from Bell Mountain is a must-see, and is easy to get to on foot. It's pretty level all the way from the North Bell Trailhead. It'd make a fine afternoon hike, out and back.<br /><br />Once you get past that, the rest of the trail has some pretty good hills, by our Midwest standards. There are long level parts along ridge-tops and in creek bottoms, but you'll have some climbs in the 200-400 foot range. There are some beautiful creeks along the way, too, so finding water was not a problem for us. Joes Creek is big enough that in wetter weather, it is probably hard to cross.<br /><br />We heard from other hikers that there are feral pigs in the area, so we hung our food out of there reach overnight. We really saw very little animal life - a couple squirrels and birds along the whole trip. We saw lots of deer scat from last fall - many were full of persimmon seeds.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-37778550175147475492009-03-24T14:12:00.000-07:002009-03-24T14:38:00.748-07:00AIG Bonus ScandalOkay, I haven't been political yet, but here goes...<br /><br />I've been thinking about the whole AIG bonus thing. Correct me if I'm wrong, but here's the way it looks to me. Congress rushed through this legislation. It's fairly complicated, and they were in a hurry to get this thing done. It seems like you can pick two of: complicated, fast, and right. They picked all three, and they screwed it up. The issue of bonuses was in the legislation in black and white, and they missed it because they voted on something without taking enough time to read it. To me, it really seems that simple.<br /><br />Now as far as AIG's role, maybe the folks deserved the bonuses, maybe not. I've heard the argument that things would have been a lot worse without the work of the executives that got the bonuses. That may or may not be true. It's a real possibility, but without knowing the inside info, I can't say. But it seems clear that AIG was contractually obligated to pay those bonuses, with Congress' backing per the legislation. If they <span style="font-style:italic;">didn't</span> pay the bonuses, they could have been sued, and lost the bonus money plus punitive damages and court costs. And they wouldn't have had a leg to stand on in court, so they probably would have lost.<br /><br />AIG was between a rock and a hard place. Congress put the rock there, and now they're whining about it. Congress should man up (if there are any there) and say "We screwed up. We know what we did wrong this time, and we won't make that mistake next time." Since they're not owning the problem, they'll probably screw it up next time, too.<br /><br />Don't misunderstand me - I am not defending AIG. But Congress is pretty quick to shift the blame here, because they know if we stop and think about it, we'll realize what idiots we've elected.<br /><br />Now, as complicated as the bailout is, it pales in comparison to what's coming down the pike with health care. If they can't even get bailouts right, can they be trusted to get health care right? At this point, I don't think they collectively have the skills to lance a blister, much less to do their jobs well.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0tag:blogger.com,1999:blog-8321077109444530317.post-86230298516877039372009-02-15T12:19:00.000-08:002009-02-15T12:47:54.108-08:00Testing Webapps That Send EmailI've been working on a GWT-based web application (<a href="https://penwag.com">https://penwag.com</a>) that relies on email for some of its functionality. For example, it sends email when you register, and email when you ask for your password. Standard stuff.<br /><br />I've been working towards having full-blown integration tests that hit the site to make sure everything works from end-to-end. There are really two tricky areas with this. First, we want the app to send the emails, and think it's sending them out, but without actually sending them to a real email address. Second, we do need to actually check those emails to make sure that the correct content went into them.<br /><br />This weekend, it finally all came together, and the solution was very straightforward. It depends on two opensource components, and adds about 100 lines to code to glue it all together.<br /><br />The first opensource component is <a href="http://quintanasoft.com/dumbster/">Dumbster</a>, a fake SMTP server that's controllable through java code. It listens on port 25, and acts like a normal SMTP server as far as the connecting app is concerned. The key difference is that it doesn't send out the emails, but collects them and makes them available via a Java API.<br /><br />The second component is the well-known Jetty, a Java web server that is easily embedded into other applications, which is the key for us.<br /><br />Here's how we pull it all together. When the test harness starts up, it completely initializes the database, readying it for the Selenium tests. It then starts Dumbster, and finally starts Jetty. The harness creates a handler that responds to a very limited set of HTTP requests:<br /><pre><br />/ - list all emails<br />/stop - calls System.exit(0)<br />/reset - restarts Dumpster, which throws away emails and restarts with an empty list<br />/<number> - returns the email at this index in the list.<br /></pre><br />Combined, these features allow Selenium tests to hit a URL and fetch a given email, validate the content of the email, and, in the case of my verification email, click on the link embedded in the email.<br /><br />I thought you might want the code. It's not tested, no guarantees, yada yada, but it works for me. Grab it here: <a href="http://moneybender.com/IntegrationTestHarness.java">http://moneybender.com/IntegrationTestHarness.java</a>Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com2tag:blogger.com,1999:blog-8321077109444530317.post-63256160002665926242009-01-11T14:30:00.000-08:002009-01-11T14:44:53.550-08:00First Chaco SaleSo, I think I just sold my first pair of shoes. ;)<br /><br />My wife and I were at the mall the other night, wandering around, waiting for <a href="http://www.imdb.com/title/tt1205489/">Gran Torino</a> to start. I noticed a couple of the shoe stores had sales on, so I headed over to <a href="http://tradehome.com/">Tradehome</a> shoe store, the one where I bought my Chaco Redrocks, to see if they have them marked down, seeing as how I'm very happy with mine. They did not, sadly. So, my wife's there with me, tries on a couple pairs of shoes. A couple Merrels, and the women's Chaco Redrocks. She definitely favored the Redrocks, and now she has a pair, too. My first shoe sale!<br /><br />My grandpa would be pleased, I think. He worked for Brown Shoe for 34 years, and while on the board of directors pushed to have a shoe plant built in <a href="http://en.wikipedia.org/wiki/Steelville,_MO">Steelville, Mo.</a>, and it lasted for quite some time before they shut it down.Don Bransonhttp://www.blogger.com/profile/08699972846989300948noreply@blogger.com0