<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2091477088771879991</id><updated>2012-02-15T22:26:43.336-08:00</updated><category term='cost'/><category term='work'/><category term='tech lead'/><category term='BDAS'/><category term='optimization'/><title type='text'>Agile Enterprise</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.xhalliday.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>8</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-230718835635510870</id><published>2010-01-13T18:45:00.000-08:00</published><updated>2010-04-06T18:33:37.748-07:00</updated><title type='text'>Spring 3 and AspectJ</title><content type='html'>Moving to Spring 3 (and dropping the uber jar), my dependencies look a bit different. Eclipse started refusing to run tests: org/aspectj/lang/Signature not found, but only when running JUnit tests in Eclipse - Maven builds were fine.&lt;br /&gt;&lt;br /&gt;After much searching, poking and prodding, I finally took the time to read the first line in the docs (http://maven.apache.org/plugins/maven-eclipse-plugin/examples/ajdt-projects.html): "The Eclipse plugin interacts with the Aspectj plugin to configure Eclipse to use AJDT."  I had assumed, looking at their sample, it was present to configure specific things in AspectJ, and omitted the entire definition since I had no use for those configurations.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&amp;lt;plugin&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;groupId&gt;org.codehaus.mojo&amp;lt;/groupId&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;artifactId&gt;aspectj-maven-plugin&amp;lt;/artifactId&gt;&lt;br /&gt;&amp;lt;/plugin&gt;&lt;/blockquote&gt;&lt;br /&gt;Adding this simple plugin to my &lt;build/&gt; made my classpath worries disappear.  Woot, back to Spring 3 REST!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-230718835635510870?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/230718835635510870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/230718835635510870'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2010/01/spring-3-and-aspectj.html' title='Spring 3 and AspectJ'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-1326936648799206116</id><published>2009-08-31T18:33:00.000-07:00</published><updated>2009-08-31T18:36:11.604-07:00</updated><title type='text'>Maven EAR plugin and JBoss configuration</title><content type='html'>I had added a &amp;lt;jboss&amp;gt;&amp;lt;version&amp;gt;4.2&amp;lt;/version&amp;gt;&amp;lt;/jboss&amp;gt; configuration item to my project's EAR plugin configuration.  It started deploying the contained WAR as OnlyASurvey-1.0-SNAPSHOT.ear.  This confused JBoss quite a bit as it was expecting OnlyASurvey.war. Removing the configuration reverted the filename.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Recorded here for my future reference and for anyone else who runs across this problem.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-1326936648799206116?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/1326936648799206116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/1326936648799206116'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2009/08/maven-ear-plugin-and-jboss.html' title='Maven EAR plugin and JBoss configuration'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-7437877234270844013</id><published>2009-08-28T09:42:00.001-07:00</published><updated>2009-08-28T09:54:31.424-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cost'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><title type='text'>Optimized Workstations, Part 1</title><content type='html'>Today I optimized my local PostgreSQL install a bit.  Specifically I edited postgresql.conf and adjusted parameters that I thought were relevant.  I used the list of configuration items in the General Tuning section of &lt;a href="http://www.revsys.com/writings/postgresql-performance.html"&gt;this post&lt;/a&gt; and common sense as a guide.  I turned off the fsync option, figuring that syncs are pretty useless in a begin/rollback transactional test architecture.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My test suite went from 45-50 seconds to a reliable 37-38 seconds for each run.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For someone who runs tests so often this ~10 second gain gets me a lot - 20% faster test suite execution.  For a floor of 40 developers costing the shop ~$1/second this can add up.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let's take a smallish project with a robust 2 minute test suite run.  Shaving 20%, or 24 seconds, off the run and assuming we run the whole suite 5 times per day takes our (120*5 = $600/day) test runs to (96*5 = $480/day), a savings of $120/day or $600/week or $31,200/52-week year.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Not bad for 10 minutes of investigation and a service restart.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-7437877234270844013?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/7437877234270844013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/7437877234270844013'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2009/08/optimized-workstations-part-1.html' title='Optimized Workstations, Part 1'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-6244891478041657340</id><published>2009-08-24T18:06:00.000-07:00</published><updated>2009-08-24T18:50:17.777-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tech lead'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='BDAS'/><title type='text'>BDAS 101</title><content type='html'>One of my top priorities when taking my first technical lead position was establishing a change advisory board to help me steer a shop of 30-50 developers (dependent on load).  While other CABs existed in the organization, they were corporate and this was just for developers, so I didn't label it as a CAB.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead I put together the Build, Design and Architectural Standards group, a.k.a. BDAS, which quickly became known simply as "badass".  I would have included Process, which is also in scope, but then the acronym would have been BDAPS, and I couldn't have that.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Over time I will add more posts about my experiences in BDAS.  Today I'll offer some advice on running a core developer meeting.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;b&gt;Developers can't have a frank discussion with a cop in the room&lt;/b&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It was important for the group to be made entirely of developers.  There is nothing sadder to watch than a competent engineer attempting to discover what the boss wants to hear.  I needed a group that could call it as they saw it and felt they were in a room where it was okay to do so.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Have the right people&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Team leads, senior developers, movers and shakers.  The group is going to define standards, make recommendations and guidelines and, in the case of some, manage their own teams in implementation.  It's important to have representation from all stakeholders &lt;i&gt;who are involved in the actual building and maintaining of source code&lt;/i&gt;.  That means all development teams, pretty much any group that will be committing to your SCR.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;Process, process, process&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you're going to take ten people's time, an hour every week, to get them into a room and try to get opinions and decisions out of them, first bring timbits.  Then make sure that everyone knows what the gig is about, their role in it and how it's going to go down.  Have a standard agenda format, track items that can be closed, produce minutes in a central place and do all of it yourself.  Link all meetings, resulting tickets, wiki pages, documents, code. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Today BDAS consists of a landing page, links to meetings as wiki pages simply titled "BDAS Meeting 1..n" and a JIRA project to track items.  Each week, preferably well in advance, start a wiki page with the next sequential meeting number.  Add a link to the previous meeting, list agenda items as links to your issue tracker, and add a Minutes header.  Then as the meeting goes on update the page to fill in the minutes.  A BDAS meeting page looks roughly like the following:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;BDAS Meeting 20&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Review minutes of &lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;&lt;span class="Apple-style-span"  style="color:#3366FF;"&gt;BDAS Meeting 19&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Agenda&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="color:#3366FF;"&gt;&lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;BDAS-1&lt;/span&gt;&lt;/span&gt;: Standardizing on autowiring and component scanning&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="color:#3366FF;"&gt;&lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;BDAS-3&lt;/span&gt;&lt;/span&gt;: Establish acceptable mockup formats&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;&lt;span class="Apple-style-span"  style="color:#3366FF;"&gt;BDAS-29&lt;/span&gt;&lt;/span&gt;: Review domain model support API&lt;/li&gt;&lt;li&gt;Open discussion&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Minutes&lt;br /&gt;&lt;ul&gt;&lt;li&gt;whatever comes up, &lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;&lt;span class="Apple-style-span"  style="color:#3366FF;"&gt;link to proposal doc we created&lt;/span&gt;&lt;/span&gt;, etc&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;ol&gt;&lt;br /&gt;&lt;/ol&gt;&lt;/ol&gt;&lt;div&gt;From this page link to anything else that's relevant.  If you control that content then link back to it.  Link to issues, source code (e.g., FishEye, SVN), vendors, whatever.  A hugely important aspect of BDAS is transparency and discoverability.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;If you're done, close the meeting&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Nothing you do as a tech lead will get you more brownie points than ending a meeting early.  If you've worked the agenda, done a round table and have nothing left in the plan then you're done for the day.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;If you're not done, leave it for next meeting&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Never run over.  More specifically, don't be the cause of it.  If you just can't stop others from making the meeting go longer than scheduled then make sure you keep trying to end it, suggest offline discussion, offer to put it on the table for next week or schedule a meeting of interested parties if it's so important, but get bodies out of the room.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-6244891478041657340?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/6244891478041657340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/6244891478041657340'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2009/08/bdas-101.html' title='BDAS 101'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-1282883521968539250</id><published>2009-07-24T16:33:00.001-07:00</published><updated>2009-07-24T19:28:13.603-07:00</updated><title type='text'>Domain Model Testing</title><content type='html'>&lt;div&gt;To me, measuring coverage is not about the coverage measured (regression, certainty), it's about what's left uncovered (risk, agility) and why I should care (cost, agility).  I'd prefer to have 100% coverage and be confident that it's due to ninja coders writing ninja code.  In RL I only ask that model coverage be in line with the project's overall coverage, starting with the &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#equals(java.lang.Object)"&gt;most&lt;/a&gt; &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#hashCode()"&gt;complex&lt;/a&gt; methods. Maximize coverage and minimize cost, a.k.a. the simplest (smallest) code that could possibly work.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;There is always debate over testing getters and setters, or even &lt;gasp&gt; measuring their coverage at all or including it in reports. It's seen as unfair to measure and derogatory towards one's stats.  I think that misses the point.&lt;br /&gt;&lt;br /&gt;So here's what I do and why.&lt;br /&gt;&lt;br /&gt;There's a lot to a domain model.  Herein I'm referring to persistent models, i.e., Hibernate-mapped classes (or similar, depending on the platform).  If you've worked with Hibernate applications in the real world then you have likely seen it load the entire database, cascade deletes you didn't expect, and throw the occasional exception complaining about possibly unsafe thread access.  These have nothing to do with getters and setters, though in exercising your model you will eventually exercise nearly all such methods.&lt;br /&gt;&lt;br /&gt;So what are some common issues that persistent models face?  Some that I've seen include:&lt;div&gt;&lt;ul&gt;&lt;li&gt;Forgetting metadata (&lt;a href="http://java.sun.com/javaee/5/docs/api/javax/persistence/Enumerated.html"&gt;@Enumerated&lt;/a&gt; &lt;i&gt;!!&lt;/i&gt;)&lt;/li&gt;&lt;li&gt;Omitting constraints - either on the model or -- more importantly -- in the database&lt;/li&gt;&lt;li&gt;Inappropriate eagerness and laziness&lt;/li&gt;&lt;li&gt;Unnecessary relationships, particularly bidirectional ones&lt;/li&gt;&lt;li&gt;Missing or buggy equals and hashCode methods&lt;/li&gt;&lt;li&gt;Impact of broken transactional schemes, particularly as you make changes later&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Some of the waste that I see when reviewing code involves creating test data specific to a test.  Not only is this redundant RY-ism, it increases the cost of changing a model markedly, and obfuscates the wider impact of those changes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead, over time I have come to use a pattern that I find very useful and inexpensive.&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Create a test scenario data service&lt;/li&gt;&lt;li&gt;Maintain a domain model test&lt;/li&gt;&lt;li&gt;Pervasively use scenarios in tests&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Test Scenario Data Service&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Except for very simple instances I always create data using a test data service.  Something like the following:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;@Service&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;@Transactional&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;public class TestDataServiceImpl implements TestDataService&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span"  style=" white-space: normal; font-family:Georgia;"&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;/** {@inheritDoc} Thorough, accurate. */&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;public Survey createScenario1(Account owner) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;return createScenario1(owner, true);&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;/** {@inheritDoc} Thorough, accurate. */&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;public Survey createScenario1(Account owner, boolean persist) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Assert.notNull(owner);&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Survey survey = new Survey();&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;survey.setOwner(owner);&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;survey.setName("My Test Survey #" + getMBUN());&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span"  style=" white-space: normal; font-family:Georgia;"&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;if(persist) {&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;persist(retval);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;return survey;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;/** {@inheritDoc} Thorough, accurate. */&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;public Survey createScenario2(Account owner, boolean persist) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space: pre; "&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Assert.notNull(owner);&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Survey retval = createScenario1(owner, &lt;/span&gt;&lt;i&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;false&lt;/span&gt;&lt;/i&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;);&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;retval.addQuestion(new TextQuestion(retval, "Please enter your email address:"));&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;if(persist) {&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;persist(retval);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;}&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;return survey;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This class is then wired into pretty close to 100% of my test classes.  As you can see, the createScenarioX methods often have a variant with a "boolean persist" flag, true by default, so that scenarios can build on each other without prematurely persisting information and for those times when a unit test is in order -- consistency in the scenario model maintains velocity in testing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Maintain a Domain Model Test&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In each of my projects can typically be found a class called DomainModelTest.  It contains at least one test for each scenario in my TestDataService.  These tests will invoke the createScenarioX() method, flush and clear the Hibernate session, then reload the root object and walk it, comparing values and references along the way.  While hard coded, not only do these tests exercise accessor methods (metrics for management) they also:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;document the domain model&lt;/li&gt;&lt;li&gt;document variants, use cases and negative tests&lt;/li&gt;&lt;li&gt;alert you to damage done to the test database e.g., by corporate DBAs or by inappropriate or incompetent resources, typically after you're off the project&lt;/li&gt;&lt;li&gt;fail (often fantastically) due to performance issues when the suite runs against a copy of production - therefore, run it against a copy of production from time to time.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Pervasively Use Scenarios in Tests&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Any test that I write that involves data being loaded, changed, deleted, queried or otherwise interacting with a model will always use the test service and usually in a persistent way.  No mocks here, thanks.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The trick (and goal) is to use the scenarios you created earlier pervasively throughout your entire test suite.  Not only does this reduce the cost of scenario creation, it reduces the cognitive load of inspecting, changing and maintaining your test suite because all references to createScenario3() do the same thing, cost only 1 line of code, and set the expectation for the context of the rest of the test in question.  This in turn enables estimating the impact of changes to your model in a more deterministic manner.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Lessons Learned&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The big lesson that I've learned applying this pattern is that at first you want to create the most complete scenario possible -- resist and heed my warning because each time you change or extend the model, Scenario #1 gets updated and so does the corresponding domain model test and most other tests in the suite.  All green, all good, at first.  What happens is that after a while (say, 500 tests later) you have a test suite that takes on the order of 45 seconds to run.  Come on, I don't have all minute!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here are some recommendations:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Don't be afraid of multiple scenarios - basicScenario1, complexScenario2, basicScenario3WithBranchingLogic, etc.  Start small and expand, and consider having a "maximum scenario" which implements every possible model component.  Use this latter scenario in the domain model test often, and when wanting to generate a largish dataset for performance testing&lt;/li&gt;&lt;li&gt;Start small - Scenario #1 should be the minimal dataset required to be persisted without errors&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;boolean persist&lt;/span&gt; - being able to construct a scenario without persisting it means you can customize a particular scenario or write a unit test.  This is not always possible, particularly if you have any logic in the backend - document that clearly in the javadoc&lt;/li&gt;&lt;li&gt;Have a &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;@Test createReallyBigDataSetSanityTest()&lt;/span&gt; and make sure you &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;flush()&lt;/span&gt;.  It should create &lt;i&gt;n&lt;/i&gt; scenarios, of differing types, where &lt;i&gt;n&lt;/i&gt; is something that fits in a reasonable timeout.  You will catch missing indexes, Hibernate loading too much data and various other sundry issues&lt;/li&gt;&lt;li&gt;Iterating a test implementation against multiple scenarios is a powerful technique; consider having a &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;List&amp;lt;Type&amp;gt; getAllScenarios()&lt;/span&gt; method or multiple ones if your various scenarios create trees with different roots.  It really sucks to have test failures dependent on backend data because users will inevitably create all possible scenarios in production&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/gasp&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-1282883521968539250?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/1282883521968539250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/1282883521968539250'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2009/07/domain-model-testing.html' title='Domain Model Testing'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-7529506856577985850</id><published>2009-06-30T17:59:00.001-07:00</published><updated>2009-06-30T18:12:38.414-07:00</updated><title type='text'>Bash shell configuration</title><content type='html'>Further to my last post about bash, it really bugs me having to &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;`exec bash`&lt;/span&gt; because my .&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;bashrc&lt;/span&gt; isn't being executed when I login.  After changing my shell to &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;/bin/bash&lt;/span&gt; I was able to get a useful command prompt thanks to the various defaults in place and moved on.  But when I run &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;dir&lt;/span&gt; (no, I can't &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-family: Georgia; "&gt;just  &lt;/span&gt;ls&amp;nbsp;-l&lt;/span&gt; nor &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;ll&lt;/span&gt; like normal people) I get the same horizontal &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;ls &lt;/span&gt;lameness, not the &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;dir='ls&amp;nbsp;-l&amp;nbsp;--color'&lt;/span&gt; goodness that I expect.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;I found &lt;a href="http://my.brandeis.edu/bboard/q-and-a-fetch-msg?msg_id=0001Y5"&gt;this thread&lt;/a&gt; which showed the solution for my Ubuntu box.  So I put &lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;   source &lt;span class="Apple-style-span"  style=" ;font-family:Georgia;"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;~/.bashrc&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;into my &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;.bash_profile &lt;/span&gt;and now when I su to the account I get my environment exactly as I configured it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Factoid: It turns out (man pages ftw) that bash's default is to ignore any&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;.bashrc&lt;/span&gt; file if it is being executed as "&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;sh&lt;/span&gt;", so even if &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;/bin/sh&lt;/span&gt; is symlinked to &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;/bin/bash &lt;/span&gt;the script won't be run as I assumed it would.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-7529506856577985850?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/7529506856577985850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/7529506856577985850'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2009/06/bash-shell-configuration.html' title='Bash shell configuration'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-4621291799238404896</id><published>2009-06-29T19:00:00.001-07:00</published><updated>2009-06-29T19:04:29.702-07:00</updated><title type='text'>Missing Bash Prompt</title><content type='html'>When working on new machines it always frustrates me that I end up working at a simple "$" prompt, nothing else, because .bashrc is not being used. If this happens to you, you are likely using &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;/bin/sh&lt;/span&gt; and not &lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;/bin/bash&lt;/span&gt; on a machine where the former is not symlinked to the latter (which you may be expecting). &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;$ chsh&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span"  style=" ;font-family:Georgia;"&gt;Will allow you to change your shell.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-4621291799238404896?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/4621291799238404896'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/4621291799238404896'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2009/06/missing-bash-prompt.html' title='Missing Bash Prompt'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-2091477088771879991.post-7507982457799163054</id><published>2009-06-12T15:24:00.000-07:00</published><updated>2009-06-12T15:28:29.531-07:00</updated><title type='text'>AntiSamy and Maven</title><content type='html'>I've been waiting for an easy way to get AntiSamy from a Maven repo to try it out for cleaning user-supplied markup.  While it's not in the central repo yet I did find that you can add their own repo, details of which I haven't seen published anywhere else yet:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://code.google.com/p/owaspantisamy/issues/detail?id=45"&gt;http://code.google.com/p/owaspantisamy/issues/detail?id=45&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2091477088771879991-7507982457799163054?l=www.xhalliday.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/7507982457799163054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2091477088771879991/posts/default/7507982457799163054'/><link rel='alternate' type='text/html' href='http://www.xhalliday.com/2009/06/antisamy-and-maven.html' title='AntiSamy and Maven'/><author><name>xhalliday</name><uri>http://www.blogger.com/profile/13422641290538881364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>
