<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Gojko Adzic &#187; spring</title>
	<atom:link href="http://gojko.net/tag/spring/feed/" rel="self" type="application/rss+xml" />
	<link>http://gojko.net</link>
	<description>Building software that matters</description>
	<lastBuildDate>Tue, 31 Jan 2012 09:07:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>QCon London 2009: Spring 3.0 upcoming features</title>
		<link>http://gojko.net/2009/03/12/qcon-london-2009-spring-30-upcoming-features/</link>
		<comments>http://gojko.net/2009/03/12/qcon-london-2009-spring-30-upcoming-features/#comments</comments>
		<pubDate>Thu, 12 Mar 2009 16:45:25 +0000</pubDate>
		<dc:creator>gojko</dc:creator>
				<category><![CDATA[news]]></category>
		<category><![CDATA[qcon]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[springsource]]></category>

		<guid isPermaLink="false">http://gojko.net/?p=795</guid>
		<description><![CDATA[Rod Johnson, CEO of SpringSource, presented the new features in the upcoming Spring 3.0 release today at the QCon London 2009 conference. Probably the most important infrastructural change is that from version 3.0, Spring will work only on Java 5+ platforms. The project layout will change slightly, moving to Maven...]]></description>
			<content:encoded><![CDATA[<p>Rod Johnson, CEO of SpringSource, presented the <a href="http://qconlondon.com/london-2009/presentation/Spring+Today+and+Tomorrow">new features in the upcoming Spring 3.0</a> release today at the <a href="/tag/qcon">QCon London 2009</a> conference. <span id="more-795"></span></p>
<p>Probably the most important infrastructural change is that from version 3.0, Spring will work only on Java 5+ platforms. The project layout will change slightly, moving to Maven style finer grained system of projects, which will be built using the the new Spring build system (&#8220;as known from Spring web flow 2.0&#8243;) which is OSGi based.</p>
<p>In terms of new features, Johnson pointed out the following innovations:</p>
<ul>
<li>Expression language for configuration</li>
<li>Comprefensive REsT support</li>
<li>support for Portlet 2.0</li>
<li>Declarative model validation</li>
<li>Early support for Java EE 6</li>
</ul>
<h2>Spring expression language</h2>
<p>Spring expression parser is the most important new addition in version 3.0, and will be shipped as part of the org.springframrwork.expression package. The Spring expression language is compatible with Unified EL, but according to Johnson does a lot more. You can use it anywhere where you can write a bean definition, for example in the configuration files:</p>
<pre style="margin-bottom:10px">
&lt;property name="prop1" value="#{systemProperties.databaseName}" /&gt;
&lt;property name="prop2" value="#{strategyBean.databaseName}" /&gt;
</pre>
<p>Expressions can reference global system properties or properties of other beans in the same context. It can also be used in annotations, for example:</p>
<pre style="margin-bottom:10px">
class Bean {
@Value ("#systemProperties.favouriteColor}")
private String favouriteColor;
}
</pre>
<p>This also applies to method parameter annotations. In addition to beans and system properties, expressions can access various web context properties such as contextProperties, contextAttributes, request, session.</p>
<h2>Rest support and MVC changes</h2>
<p>Building on annotations in Spring MVC introduced in version 2.5, Spring MVC will get strong REST support. An example of exposing controller methods with REST is:</p>
<pre style="margin-bottom:10px">
@RequestMapping(value=/show/{id}", method=GET)
public Reward show(@PathVariable("id") long id){
//...
}
</pre>
<p>This will work across different protocols (JSON, ATOM, XML). There will also be new options for handler method parameters, including @ReqestHeader and @CookieValue. People will also be able to use their own annotations and include that into the @RequestMapping support.</p>
<h2>Release schedule</h2>
<p>Johnson announced that milestone 3 will be released till the end of March, with a release candidate coming out in early May and the general availability release in June. </p>
<p><i>I&#8217;m covering the Qcon London 2009 conference in detail on this blog. Click <a href="/tag/qcon">here</a> for other news and reviews from the conference</i></p>
]]></content:encoded>
			<wfw:commentRss>http://gojko.net/2009/03/12/qcon-london-2009-spring-30-upcoming-features/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Last call for Thursday: opensource .net mini conference in London</title>
		<link>http://gojko.net/2009/01/20/last-call-for-thursday-opensource-net-mini-conference-in-london/</link>
		<comments>http://gojko.net/2009/01/20/last-call-for-thursday-opensource-net-mini-conference-in-london/#comments</comments>
		<pubDate>Tue, 20 Jan 2009 13:24:45 +0000</pubDate>
		<dc:creator>gojko</dc:creator>
				<category><![CDATA[news]]></category>
		<category><![CDATA[presentations]]></category>
		<category><![CDATA[activemq]]></category>
		<category><![CDATA[alt.net]]></category>
		<category><![CDATA[altdotnet]]></category>
		<category><![CDATA[altdotnetuk]]></category>
		<category><![CDATA[altnetuk]]></category>
		<category><![CDATA[AOP]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[nhibernate]]></category>
		<category><![CDATA[nms]]></category>
		<category><![CDATA[postsharp]]></category>
		<category><![CDATA[repository]]></category>
		<category><![CDATA[skills matter]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[spring.net]]></category>

		<guid isPermaLink="false">http://gojko.net/?p=639</guid>
		<description><![CDATA[If you are in London on Thursday and are not yet registered for the opensource .net exchange, here&#8217;s what you&#8217;ll miss: Dylan Beattie: JQuery and ASP.NET MVC David Ross: Aspect oriented programming with PostSharp Sebastien Lambla: Fluent NHibernate David de Florinier: ActiveMQ and NMS Mike Hadlow: Implementing the repository pattern...]]></description>
			<content:encoded><![CDATA[<p>If you are in London on Thursday and are not yet registered for the opensource .net exchange, here&#8217;s what you&#8217;ll miss:</p>
<ul>
<li>Dylan Beattie: JQuery and ASP.NET MVC</li>
<li>David Ross: Aspect oriented programming with PostSharp</li>
<li>Sebastien Lambla: Fluent NHibernate</li>
<li>David de Florinier: ActiveMQ and NMS</li>
<li>Mike Hadlow: Implementing the repository pattern</li>
<li>Russ Miles: Spring .NET best practices</li>
</ul>
<p>We start at 6:30 PM in the Crypt on Clarkenwell (near the Farringdon tube station). We have about 200 people registered so far, but as we have a bigger venue this time there are still open places. The mini-conference is free, but you do have to register upfront. For more details and to register, see:</p>
<p><a href="http://skillsmatter.com/event/open-source-dot-net/open-source-dot-net-exchange">http://skillsmatter.com/event/open-source-dot-net/open-source-dot-net-exchange</a></p>
]]></content:encoded>
			<wfw:commentRss>http://gojko.net/2009/01/20/last-call-for-thursday-opensource-net-mini-conference-in-london/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Transactional Spring/Slim test runner</title>
		<link>http://gojko.net/2009/01/14/transactional-springslim-test-runner/</link>
		<comments>http://gojko.net/2009/01/14/transactional-springslim-test-runner/#comments</comments>
		<pubDate>Wed, 14 Jan 2009 16:09:48 +0000</pubDate>
		<dc:creator>gojko</dc:creator>
				<category><![CDATA[articles]]></category>
		<category><![CDATA[fitnesse]]></category>
		<category><![CDATA[acceptance testing]]></category>
		<category><![CDATA[slim]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[tdd]]></category>

		<guid isPermaLink="false">http://gojko.net/?p=615</guid>
		<description><![CDATA[Here&#8217;s an implementation of the Slim test runner for Fitnesse that wraps all tests into spring transactions and rolls back on the end of each test, to make data-driven tests instantly repeatable with minimal code and no configuration changes in the fixtures or the Spring context. binaries (compiled against spring...]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s an implementation of the Slim test runner for Fitnesse that wraps all tests into spring transactions and rolls back on the end of each test, to make data-driven tests instantly repeatable with minimal code and no configuration changes in the fixtures or the Spring context.<span id="more-615"></span></p>
<ul>
<li><a href='/resources/transactionalrunner-1.0.2.jar'>binaries</a>  (compiled against spring 2.5.5 and fitnesse 20090112)</li>
<li><a href='/resources/transactionalrunner-1.0.2-src.zip'>source code</a></li>
</ul>
<h2>Usage</h2>
<ol>
<li>Include the transactionalrunner-1.0.2.jar in your classpath</li>
<li>Include Spring, all other dependencies etc in your classpath</li>
<li>Have your spring context file accessible (ideally in the classpath or on the file system). Make sure to have a transaction manager defined there and the <b>tx:annotation-driven</b> tag set up correctly to link to the transaction manager</li>
<li>Set the test runner configuration in your test suite (it&#8217;s not going to work if it is in SetUp):
<pre style="overflow: auto; font-size:12px">
!define TEST_SYSTEM {slim}
!define TEST_RUNNER {info.fitnesse.TransactionalSlimService}
!define COMMAND_PATTERN {java  -Dspring.context=classpath:spring.xml -cp %p %m -v}
</pre>
<p>the <b>-v</b> on the end is optional and causes lots of debug messages to be printed out. You should see &#8220;rolling back now&#8221; after every test in the suite. You can set the location of your basic spring context file by defining the <b>spring.context</b> parameter as in the example above. Use standard spring locator prefixes for that.</li>
<li>Change your fixtures to use <b>info.fitnesse.FitnesseSpringContext.getInstance()</b> as the spring context rather than loading it yourself. You can use autowirable bean factories from there on.</li>
<li>Run your tests as nornal. The spring transaction context will roll back after each test</li>
</ol>
<h2>How it works</h2>
<p>The test runner loads your spring context file (from the <b>spring.context</b> system variable) and then injects an additional test runner bean into the context, making it available through the static <b>info.fitnesse.FitNesseSpringContext</b> instance.</p>
<p>Instead of directly executing instructions in <b>processTheInstructions(String instructions)</b>, this test runner delegates the call to a Spring bean which is transactional, so the call passes the declarative transactional boundary for each test. The Spring bean throws an exception that causes the transaction to roll back on the end of each test and the test runner catches and ignores that exception. This uses the same idea as in <a href="http://gojko.net/2008/01/22/spring-rollback/">The magic ingredient for the FitNesse, Spring and Hibernate TDD soup</a>, but applied to Slim. (hint: you can use the same jar to run normal fitnesse tests, just change <b>TEST_SYSTEM</b> to <b>FIT</b> and <b>TEST_RUNNER</b> to <b>info.fitnesse.TransactionalFitServer</b>).</p>
<p>The rollback bean is loaded from an spring context file embedded in transactionalrunner.jar so you are not required to change anything within your spring context. The only possible problem is if there is already a bean called &#8220;rollbackBean&#8221; defined there. The embedded context doesn&#8217;t have any transaction managers or anything else defined, so it expects all that to be defined outside.</p>
]]></content:encoded>
			<wfw:commentRss>http://gojko.net/2009/01/14/transactional-springslim-test-runner/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>OpenSource .NET Exchange final programme</title>
		<link>http://gojko.net/2008/12/15/opensource-net-exchange-final-programme/</link>
		<comments>http://gojko.net/2008/12/15/opensource-net-exchange-final-programme/#comments</comments>
		<pubDate>Mon, 15 Dec 2008 03:47:30 +0000</pubDate>
		<dc:creator>gojko</dc:creator>
				<category><![CDATA[articles]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[activemq]]></category>
		<category><![CDATA[alt.net]]></category>
		<category><![CDATA[altdotnet]]></category>
		<category><![CDATA[altdotnetuk]]></category>
		<category><![CDATA[altnetuk]]></category>
		<category><![CDATA[castle]]></category>
		<category><![CDATA[castle project]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[nhibernate]]></category>
		<category><![CDATA[nms]]></category>
		<category><![CDATA[postsharp]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[spring.net]]></category>

		<guid isPermaLink="false">http://gojko.net/2008/12/15/opensource-net-exchange-final-programme/</guid>
		<description><![CDATA[Here&#8217;s the final programme for the OpenSource .NET Exchange on 22/Jan in London: Dylan Beattie : JQuery David Ross: PostSharp Sebastien Lambla: Fluent NHibernate David de Florinier: ActiveMQ and NMS Mike Hadlow: Implementing the Repository pattern Russ Miles: Spring .NET best practices After the talks, we are planning to have...]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s the final programme for the OpenSource .NET Exchange on 22/Jan in London:</p>
<ul>
<li>Dylan Beattie : JQuery</li>
<li>David Ross: PostSharp</li>
<li>Sebastien Lambla: Fluent NHibernate</li>
<li>David de Florinier: ActiveMQ and NMS</li>
<li>Mike Hadlow: Implementing the Repository pattern</li>
<li>Russ Miles: Spring .NET best practices</li>
</ul>
<p>After the talks, we are planning to have a discussion panel to compare Castle and Spring.NET and work out when to use what. </p>
<p>We have a larger venue this time, and free beer and pizza is the perfect excuse to come.</p>
<p>For full details and to sign up, see <a href="http://skillsmatter.com/event/open-source-dot-net/open-source-dot-net-exchange">the event page on skillsmatter.com</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://gojko.net/2008/12/15/opensource-net-exchange-final-programme/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The magic ingredient for the FitNesse, Spring and Hibernate TDD soup</title>
		<link>http://gojko.net/2008/01/22/spring-rollback/</link>
		<comments>http://gojko.net/2008/01/22/spring-rollback/#comments</comments>
		<pubDate>Tue, 22 Jan 2008 20:34:18 +0000</pubDate>
		<dc:creator>gojko</dc:creator>
				<category><![CDATA[articles]]></category>
		<category><![CDATA[fitnesse]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[tdd]]></category>

		<guid isPermaLink="false">http://gojko.net/2008/01/22/spring-rollback/</guid>
		<description><![CDATA[Declarative transactions in Spring and Hibernate make programming enterprise applications much easier, but they also make integrated database tests a bit tricky. I&#8217;ve finally found good solution for this problem &#8212; it is a bit dirty, but very effective. The problem The issue with declarative transactions is that integration tests...]]></description>
			<content:encoded><![CDATA[<p><img src="/images/808224_tomato_soup_2.jpg" style="border:1px solid black; margin:5px 5px 5px 5px; float:left" />Declarative transactions in Spring and Hibernate make programming enterprise applications much easier, but they also make integrated database tests a bit tricky. I&#8217;ve finally found good solution for this problem &mdash; it is a bit dirty, but very effective.<span id="more-94"></span></p>
<h2>The problem</h2>
<p>The issue with declarative transactions is that integration tests need to make calls into the service layer, which is below the transaction boundary, so when the execution goes back above the transaction boundary data changes become persistent. Then unique constraints kick in, making tests non-repeatable. If several developers run tests at the same time, they might obstruct each other,  so a test failure might not really mean that there is a bug in the code. All that is just slowing down testing and making test maintenance harder.</p>
<p>Ideally, the database transaction should isolate us from other tests running at the same time, and just rolling back that transaction should make tests automatically repeatable. However, due to way that FIT loads and executes fixtures, they will by default be outside the transaction boundary. Pushing them below that boundary turned out to be quite a challenge.</p>
<p>I&#8217;ve seen a few teams that tried to attack this problem by doing some sort of database reset or clean-up to bring the database into a known state. That slows tests down unnecessarily and still does not solve the problem of concurrent test runs. In my opinion, it makes it even worse &mdash; since your data might disappear without any explanation. </p>
<p>Individual fixtures or their methods can be made transactional with some additional code, but this becomes painful to write and maintain, and removes some flexibility from FIT and FitNesse.  That approach requires specific fixture types to be written just for transactional management and directly cancels all the benefits of FitLibrary SUT domain object wrapping. Extra code has to be added to fixtures, and they have to be injected into the session context, which makes the effort error prone and terribly ugly. And it also does not work if the page is not in flow mode, since individual fixtures will be instantiated by FIT in separate method calls from the static context, crossing the transaction boundary. Even in flow mode, you still have to take care not to go out of the transaction boundary &mdash; the fixture constructor will be, for example, outside the transaction scope. I had a gut-feel that there was a much better solution, but I simply could not see it. </p>
<h2>A better way</h2>
<p>Yesterday, it finally hit me &mdash; a totally non-intrusive solution that automatically works for all fixtures. There is no need to replicate transaction management code all over the test suites. No special fixture code is required in fixtures or services, and it does not matter whether the page is in flow mode or works with standalone fixtures. No special SetUp or TearDown pages. </p>
<p>The trick is based on the fact that FitNesse does not execute FIT in-process, but as an external program in order to allow test runners for languages like C# and Python to be plugged in. Test runner is defined by a simple parameter, which can be overriden for a single test page, individual test suite or for the whole server globally. We can use this feature to supply a different Java test runner, which will be injected into the Spring context straight from the start. We can mark the entry point as <b>@Transactional</b>, so no matter how many fixtures are instantiated and how ever they use the service layer, the calls will never cross the transaction boundary and require the transaction to commit. On the end, the test runner just has to roll back the active transaction and everything that the test changed will simply disappear.</p>
<p>You can download the code from <a href="http://fitnesse.info/spring_rollback" target="_blank">here</a> &mdash; but you will need to tweak it just a bit depending on your configuration (will be explained below). Now, for the interesting part &mdash; how the thing actually works:</p>
<h2>Rolling back transactions</h2>
<p>The first part of the puzzle is to create a Spring bean that will run under the control of the transaction manager, execute the appropriate command, and then roll back the transaction. Java SDK has a standard interface for the command pattern, called Runnable, so we will use  it:</p>
<blockquote><pre>
public class RollbackNow extends RuntimeException
{ }
public class RollbackBean{
  @Transactional
  public void process(Runnable r){
    r.run();
    throw new RollbackNow();
  }
}
</pre>
</blockquote>
<p>The command processor, that will use this to inject Fixtures under the transaction boundary, will just have to catch the <b>RollbackNow</b> exception and ignore it. Because a runtime exception crosses the transaction boundary, Spring will automatically roll back the transaction. This is a bit ugly, because exceptions are used for flow control, but it is the cleanest way I found to do it &mdash; if you know a better one, please let me know. I have initially tried to fetch the current transaction from the transaction manager and roll it back in the code, but then I got &#8220;Unexpected Rollback&#8221; exceptions after the processing. Marking the transaction as <b>rollbackOnly</b> also did not help. In any case, this approach works ok. This ugliness is encapsulated and hidden from the users completely.  </p>
<p>So this new Rollback bean needs to be registered in the spring testing context, for example under the name &#8220;rollback&#8221;.</p>
<h2>Injecting the test runner</h2>
<p>The second part of the puzzle is to change the test runner to inject tests into the rollback bean. <b>FitServer</b>, the class that runs the tests by default, is not designed to allow such injection easily. Putting the whole test runner in a Spring context does not do the trick because a single <b>FitServer</b> execution might process several tests (eg test suite), and each test should be executed in an isolated environment.  When you look at the <a href="http://fitnesse.svn.sourceforge.net/viewvc/fitnesse/trunk/src/fit/FitServer.java?view=markup" target="_blank">FitServer</a> source code, the lines between 70 and 83 where the transaction boundary should be.</p>
<p><img src='/images/fitserver.gif' style='border:1px solid black; margin:5px 5px 5px 5px'/></p>
<p>So the idea is to modify the <b>FitServer</b> code a bit, cut the part that does individual tests into a separate procedure, and inject that procedure into a transactional context. For the injection, we can use the standard <b>Runnable</b> interface and pass that on to a a simple Spring bean that runs under the control of Spring transaction management. Ideally, I&#8217;d do this by extending <b>FitServer</b> and reusing most of the functionality, but because of private variables used in the inner part, that is not possible. That class is open-source, so I just copied the code, renamed it and modified it a bit.</p>
<p>A new class implements the <b>Runnable</b> interface and takes over processing of a single document:</p>
<blockquote><pre>
 public class DocumentRunner implements Runnable {
    private int size;
    public DocumentRunner(int size) {
      this.size = size;
    }
    public void run() {
      try {
        print("processing document of size: " + size + "\n");
        String document = FitProtocol.readDocument(socketReader, size);
        Parse tables = new Parse(document);
        newFixture().doTables(tables);
        print("\tresults: " + fixture.counts() + "\n");
        counts.tally(fixture.counts);
      } catch (Exception e) {
        exception(e);
      }
    }
  }
</pre>
</blockquote>
<p>The only thing left to do is to change the <b>process</b> method in the FIT Server, so that it uses the <b>RollbackBean</b> to run individual documents under a transaction, and ignores the <b>RollbackNow</b> exception. Change the start of the method to load your context file and bean.</p>
<blockquote><pre>
 public void process() {
    ApplicationContext ctx = new FileSystemXmlApplicationContext(
        "lib/test.xml");
    RollbackBean rollbackProcessingBean = (RollbackBean) ctx
        .getBean("rollback");
    fixture.listener = fixtureListener;
    try {
      int size = 1;
      while ((size = FitProtocol.readSize(socketReader)) != 0) {
        try {
          rollbackProcessingBean.process(new DocumentRunner(size));
        } catch (RollbackNow rn) {
          print("rolling back now" + "\n");
        }
      }
      print("completion signal recieved" + "\n");
    } catch (Exception e) {
      exception(e);
    }
  }
</pre>
</blockquote>
<p>With this, everything works almost out-of-the-box. No changes are required in fixture code, nor in test pages, except the runner definition. In my example, the new runner class is <b>test.RollbackServer</b>, so my definition looks like this:</p>
<blockquote><p>
!define TEST_RUNNER {test.RollbackServer}
</p></blockquote>
<p>In fact, since the default FIT test runner is being used as a base for the new runner, there is no reason not to override the runner on the global level (setting the <b>TEST_RUNNER</b> environment property in run.sh/bat or in /root page) and have all tests on the FitNesse server automatically roll back.</p>
<h2>Conclusions</h2>
<p>I still have mixed feelings about this solution &mdash; I love it because it is very effective and does not require any changes in the fixture code or services, and it does not take any flexibility out of FIT and FitNesse. On the other hand, I&#8217;m really not happy about using exceptions to fool the transaction manager or the fact that I had to copy and modify the test runner instead of extending it. According to SVN logs, FitServer does get changed every couple of months, so theoretically the way this is currently implemented may require merging the changes with new versions in the future.</p>
<p>The fact that it magically pushes all tests under the transaction boundary and instantly makes data-driven tests repeatable should save us enough time to justify that merging every once in a while. </p>
<p>Just in case that you missed the note about code download &mdash; get the code from <a href="http://fitnesse.info/spring_rollback" target="_blank">here</a>.</p>
<p>Image credits: <a href="http://www.sxc.hu/profile/woodsy" target="_blank">Steve Woods</a></p>
<table style="border:1px solid black; background:#eeeeee; padding:5px 5px 5px 5px; margin:5px 5px 5px 5px;">
<tr>
<td>
<a href="/fitnesse/book" ><img src="/FitNesse/tddfitn-sm.jpg" style="border:1px solid black; margin:10px 10px 10px 10px;" /></a></td>
<td valign="top">This book takes you on a journey through the wonderful world of FitNesse, a great web-based collaboration tool for software acceptance testing. FitNesse enables software developers and business people to build a shared understanding of the domain and helps produce software that is genuinely fit for purpose.  </p>
<p><i> &#8220;This book fills a big gap that&#8217;s kept a lot of people from using Fit successfully.&#8221;<br /> &#8211; Mike Stockdale, author of FitNesse.NET </i><br />
<hr />
<a href="/fitnesse/book">More information</a> | <a href="http://www.lulu.com/browse/preview.php?fCID=1653280" onclick="javascript:urchinTracker('/FitNesse/book-samplechapter.pdf');">Sample chapter</a> | <a href="http://www.lulu.com/content/1653280" onclick="javascript:urchinTracker('lulu-book');">Buy the book</a> | <a href="http://www.lulu.com/content/1653280" onclick="javascript:urchinTracker('lulu-book');">Buy the PDF</a> | <a href="/FitNesse/book-source.zip" onclick="javascript:urchinTracker('/FitNesse/book-source.zip');">Source code</a> | <a href="/fitnesse/book#register" onclick="javascript:urchinTracker('/FitNesse/book-register');">Register your book</a>
</td>
</tr>
</table>
]]></content:encoded>
			<wfw:commentRss>http://gojko.net/2008/01/22/spring-rollback/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

