<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Software is too expensive to build cheaply...</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/" />
    <link rel="self" type="application/atom+xml" href="http://twasink.net/blog/atom.xml" />
   <id>tag:twasink.net,2008:/blog//3</id>
    <link rel="service.post" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3" title="Software is too expensive to build cheaply..." />
    <updated>2008-02-13T04:41:14Z</updated>
    
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 3.35</generator>
 
<entry>
    <title>We apologised for the wrong thing</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2008/02/we_apologised_f.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=216" title="We apologised for the wrong thing" />
    <id>tag:twasink.net,2008:/blog//3.216</id>
    
    <published>2008-02-13T03:24:36Z</published>
    <updated>2008-02-13T04:41:14Z</updated>
    
    <summary>Today our Prime Minister made an important step in Australian history, by apologising to indigenous Australians for past injustices. However, I think the focus on the so-called Stolen Generation was wrong....[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Politics" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Today our Prime Minister made an important step in Australian history, by <a href="http://www.theaustralian.news.com.au/story/0,25197,23202612-5013172,00.html">apologising to indigenous Australians</a> for past injustices. However, I think the focus on the so-called Stolen Generation was wrong.</p>]]>
        <![CDATA[<p>It is true that successive Australian governments did take indigenous children from their parents, that they did so in large numbers, and that this did cause harm. And we should be sorry for it.</p>

<p>However, it is important, I feel, that we acknowledge the intent of this was never to harm indigenous people. It was to enforce child-protection laws, which mandated that no child, regardless of racial background, should be forced to live in substandard conditions. These conditions were rife in indigenous communities at the time, and they still are - witness the <a href="http://news.smh.com.au/prosecutor-in-gang-rape-case-stood-aside/20071211-1g93.html">case of the teenage girl gang-raped at the age of 7</a>; the only reason this even made the news is because the prosecutor decided to let the perpetrators go.</p>

<p>If non-indigenous children were treated in such fashion, the parents would certainly lose their children. It happens all the time - that's why we have organisations such as <a href="http://www.childsafety.qld.gov.au/">the Department of Child Safety</a>. For us to tolerate in indigenous communities what we would not tolerate in mainstream society is not "respect for their culture" - it's a revolting acknowledgement that indigenous Australians are not ready to partake in mainstream society.</p>

<p>It is wrong that our governments had to take the children away. It is vastly more wrong that we permitted, and continue to permit, these conditions. Rampant abuse of children has created and perpetuates a cycle of dependence in indigenous communities, and this cycle must be broken somehow. It is our failure to do this, or even to accept a need to do this, that we should apologise for. It is the failure of indigenous Australians to break out of this cycle by themselves that makes the apology somewhat pointless.</p>

<p>(All that said, I'm not in favour of the so-called intervention policies of the last days of the Howard government. We do not need to create special laws to deal with the situation; we need to enforce the ones we have, along with creative programs to achieve results by building communities up instead of tearing them apart)</p>]]>
    </content>
</entry>
<entry>
    <title>RSpec, JRuby and Story Testing Java Code</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2008/02/rspec_jruby_and.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=215" title="RSpec, JRuby and Story Testing Java Code" />
    <id>tag:twasink.net,2008:/blog//3.215</id>
    
    <published>2008-02-12T14:37:05Z</published>
    <updated>2008-02-12T15:55:06Z</updated>
    
    <summary>I&apos;ve long been interested in decent ways of expressing tests in a human-readable format. Not just any humans, but BAs and business reps in particular - the kind of people who will not be interested in slugging through piles of...[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
            <category term="Ruby" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>I've long been interested in decent ways of expressing tests in a human-readable format. Not just any humans, but BAs and business reps in particular - the kind of people who will not be interested in slugging through piles of language syntax. I tried <a href="http://fit.c2.com">Fit</a> sometime ago, and was impressed, but when I came back and revisited it recently, it looked a lot like the community had kind of faded away. Accordingly, I looked around at what else was available, and stumbled across <a href="http://rspec.info">RSpec</a>. Now, I want to test Java code, and RSpec is for Ruby (as the R kind of hints), but I was able to get this going under <a href="http://jruby.org">JRuby</a> fairly easily. I couldn't find any examples of other people doing that, so I thought I'd write it up.</p>]]>
        <![CDATA[<p>First, why look at RSpec? The big reason is that you write tests in a human-readable fashion, following a <a href="http://dannorth.net/whats-in-a-story">format put forward by Dan North</a>. (Dan North is one of the major contributors to RSpec, as well as <a href="http://jbehave.org">JBehave</a>). The tests look kind of like this:</p>

<pre><code>
Story: transfer from savings to checking account
  As a savings account holder
  I want to transfer money from my savings account to my checking account
  So that I can get cash easily from an ATM

  Scenario: savings account has sufficient funds
    Given my savings account balance is $100
    And my checking account balance is $10
    When I transfer $20 from savings to checking
    Then my savings account balance should be $80
    And my checking account balance should be $30

  Scenario: savings account has insufficient funds
    Given my savings account balance is $50
    And my checking account balance is $10
    When I transfer $60 from savings to checking
    Then my savings account balance should be $50
    And my checking account balance should be $10</code></pre>

<p>Yes - that's the <em>test</em>, not the <em>story</em>. You can take that and run it through the RSpec tool, and it will tell you if it passes or fails. And I think even a BA can understand the test.</p>

<p>Okay, there's a lot of magic going on here. I'll do my best to walk you through it, but please remember that I'm not a Ruby person - my total experience in Ruby before starting this was about 12 hours (it's now about 20); don't pick on my Ruby skills (but do feel free to offer suggestions for improvements).</p>

<h2>Get JRuby</h2>

<p>The end goal of this is to test Java code. That means my RSpec tests, which are backed by Ruby, need to be able to call my Java code. <a href="http://jruby.org">JRuby</a> to the rescue! JRuby, in case you didn't know, is a Ruby interpreter for the <span class="caps">JVM, </span>which allows for pretty decent integration between Java code and Ruby. Here's a <em>very</em> simple Ruby script that invokes Java code:</p>

<pre><code>require 'java'
include_class 'java.lang.System'

System.out.println &quot;Hello World&quot;</code></pre>

<p>You can save that as a script and invoke it with jruby - <code>jruby hello.rb</code> - or enter it interactively via jirb. Note that if you do this, the <code>require 'java'</code> step returns false now - there's a lot of slightly-dated examples on the net which says it should return true, but that apparently changed a little while back.</p>

<h2>Get RSpec</h2>

<p>JRuby seems to come with RSpec, but it's a slightly old version - 1.1.0, while the current release is 1.1.3. So the first order of business is to update. Ruby uses a packaging tool called 'gem' - you can use gem to download new packages, or update packages, over the web.</p>

<pre><code>$JRUBY_HOME/bin/gem install rspec</code></pre>

<p>Make sure you're running the 'gem' command inside JRuby's bin - if you've got ruby on your system already, you don't want to get mixed up. (Alternatively, you can make Ruby and JRuby share their gems, if you want).</p>

<p>This will put the RSpec library at <code>$JRUBY_HOME/lib/ruby/gems/1.8/gems/rspec-1.1.3</code>; pay attention to that path, because amongst other things, there are examples there!</p>

<h2>Create the test</h2>

<p>Okay, I told a slight lie - if we put the test above in a file called <code>'account'</code>, we're going to want to create a file called <code>'account.rb'</code>. The example above isn't valid Ruby, and we need something that is. However, it's not very big (and it's going to get smaller):</p>

<pre><code>require 'rubygems'

gem 'rspec'
load 'spec/story.rb'

  # don't worry - we'll define accounts later
with_steps_for :accounts do
  # Ruby code for &quot;run the file named like this one, but without the .rb extension&quot;
  run File.expand_path(__FILE__).gsub(&quot;.rb&quot;,&quot;&quot;)
end</code></pre>

<p>(RSpec people out there - I just want to point out that it took me about 3 hours to figure out the first 3 lines - couldn't find any examples of using RSpec as a gem that <em>wasn't</em> build up with a Rails app. Okay, someone who knew Ruby would figure it out, but it took me a while to work out why I couldn't run my examples except from within the <span class="caps">RSP</span>ec examples dir).</p>

<p>You then run the test like so - <code>jruby account.rb</code> - and you should get output like this one (and this is the only example I'm showing in full!):</p>

<pre><code>Arcadia:jrspec robertdw$ ruby account.rb 
Running 2 scenarios

Story: transfer from savings to checking account

  As a savings account holder
  I want to transfer money from my savings account to my checking account
  So that I can get cash easily from an ATM

  Scenario: savings account has sufficient funds

    Given my savings account balance is $100 (PENDING)
    And my checking account balance is $10 (PENDING)

    When I transfer $20 from savings to checking (PENDING)

    Then my savings account balance should be $80 (PENDING)
    And my checking account balance should be $30 (PENDING)

  Scenario: savings account has insufficient funds

    Given my savings account balance is $50 (PENDING)
    And my checking account balance is $10 (PENDING)

    When I transfer $60 from savings to checking (PENDING)

    Then my savings account balance should be $50 (PENDING)
    And my checking account balance should be $10 (PENDING)

2 scenarios: 0 succeeded, 0 failed, 2 pending

Pending Steps:
1) transfer from savings to checking account (savings account has sufficient funds): my savings account balance is $100
2) transfer from savings to checking account (savings account has sufficient funds): my checking account balance is $10
3) transfer from savings to checking account (savings account has sufficient funds): I transfer $20 from savings to checking
4) transfer from savings to checking account (savings account has sufficient funds): my savings account balance should be $80
5) transfer from savings to checking account (savings account has sufficient funds): my checking account balance should be $30
6) transfer from savings to checking account (savings account has insufficient funds): my savings account balance is $100
7) transfer from savings to checking account (savings account has insufficient funds): my checking account balance is $10
8) transfer from savings to checking account (savings account has insufficient funds): I transfer $20 from savings to checking
9) transfer from savings to checking account (savings account has insufficient funds): my savings account balance should be $80
10) transfer from savings to checking account (savings account has insufficient funds): my checking account balance should be $30</code></pre>

<p><strong>phew</strong> Okay, we can see that it's run the test, but hasn't exactly done very much. Mostly, it's said that the test steps were "PENDING". Pending is RSpec's way of saying "I don't know what you mean". So now we have to tell it.</p>

<p>You tell RSpec what the test means by providing "steps". These steps are usually provided in separate files, but I'm going to put them all in one step file. First, though, I have to update my test script. It now looks like this:</p>

<pre><code language="ruby">
require 'rubygems'

gem 'rspec'
load 'spec/story.rb'

  # This ruby code loads all the scripts inside of the 'steps' directory.
Dir[File.join(File.dirname(__FILE__), &quot;steps/*.rb&quot;)].each do |file|
  require file
end

with_steps_for :accounts do
  run File.expand_path(__FILE__).gsub(&quot;.rb&quot;,&quot;&quot;)
end</code></pre>

<p>The bit in the middle, of course, is the important bit. So we now create an <code>'accounts_steps.rb'</code> file, inside a <code>steps</code> directory. And it's going to look like this:</p>

<pre><code language="ruby">require 'rubygems'

gem 'rspec'
load 'spec/story.rb'

  # This creates steps for :accounts
steps_for(:accounts) do
  Given(&quot;my $account_type account balance is $amount&quot;) do |account_type, amount|
  end
  When(&quot;I transfer $amount from $source_account to $target_account&quot;) do |amount, source_account, target_account|
  end
  Then(&quot;my $account_type account balance should be $amount&quot;) do |account_type, amount|
  end
end</code></pre>

<p>How do I know what to fill in? The RSpec output from earlier was kind of telling me; essentially, I just create a template. If I run the test again, I get a very simple output back indicating no errors (at least, I do if I've done it right...).</p>

<p>This test, however, doesn't test squat. There's no actual work done in any of the tests! Just as a place holder, I went back and put the word <code>pending</code> inside each block, like so:</p>

<pre><code language="ruby">Given(&quot;my $account_type account balance is $amount&quot;) do |account_type, amount|
  pending
end</code></pre>

<p>When I run the test again, I get the big <code>(PENDING)</code> warnings again - but at least now I know that RSpec is understanding my test.</p>

<p>At this point, it's time to start putting some code to test in. Here's the Java class that I'm going to test:</p>

<pre><code language="java">package net.twasink.jrspec;

public class Account {
	private final String name;
	private double balance = 0.0; // NEVER use double for monetary values in the real world.
	
	public Account(String name) {
		this(name, 0.0);
	}
	
	public Account(String name, double openingBalance) {
		this.name = name;
		this.balance = openingBalance;
	}
	
	public String getName() { return name; }
	public double getBalance() { return balance; }
	
	public void transfer(Account destination, double amount) {
		if (amount &lt;= 0.0) { throw new IllegalArgumentException(&quot;amount must be positive&quot;); }
		if (balance &lt; amount) { throw new IllegalStateException(&quot;Insufficent balance&quot;); }
		
		this.balance -= amount;
		destination.balance += amount;
 	}
}</code></pre>

<p>I compile that, and I put the results on my classpath. It's now available for JRuby to use. (Actually, I've got a shell script to do this for me; it's all in the archive at the end). So now I can start making Java calls. And what I end up with is this:</p>

<pre><code language="java">require 'rubygems'

gem 'rspec'
load 'spec/story.rb'

require 'java'

include_class 'net.twasink.jrspec.Account'

  # This creates steps for :accounts
steps_for(:accounts) do
  Given(&quot;my $account_type account balance is $amount&quot;) do |account_type, amount|
    create_account(account_type, amount[1..-1].to_f)
  end
  When(&quot;I transfer $amount from $source_account to $target_account&quot;) do |amount, source_account, target_account|
    begin
      get_account(source_account).transfer(get_account(target_account), amount[1..-1].to_f)
    rescue
    end
  end
  Then(&quot;my $account_type account balance should be $amount&quot;) do |account_type, amount|
     get_account(account_type).balance.should == (amount[1..-1].to_f)   
  end
end

def create_account(account_type, amount)
  account = Account.new(account_type, amount)
  @accounts_hash ||= {}
  @accounts_hash[account_type] = account  
end

def get_account(account_type)
  return @accounts_hash[account_type]
end</code></pre>

<p>Run the test again, and everything passes. Just to confirm, I edited some of the values to give me test failures, and sure enough, they were reported as failures.</p>

<p>So there you have it! Real live running RSpec code, testing Java classes.</p>

<p>There's a little more house-keeping to do, which you'll see in the archive. First, I created a "helper" file, that would provide support for several stories together. Then, I moved the accounts stories, its steps folder, and the helper, into a sub-folder, to create a "story group", and added an "all.rb" script that would run all the story groups (as well as a stories.rb script to run all the stories in a group). Finally, I bundled this all up into two Java projects (built via Maven), along with a shell script to run RSpec/JRuby with the right classpath. All in all, not bad for a day and half's effort. And you can <a href="/files/jrspec.tgz">grab a copy of it all</a> if you so desire.</p>

<p>This isn't perfect by any means. I've got a few itches to scratch with this yet:</p>


<ul>
<li>I'd like to find a better way to pass a classpath into JRuby. (Using the JRuby plugin for Maven is out because it wouldn't see the RSpec gem - I think)</li>
<li>I'm very sure my Ruby code could be better.</li>
<li>I still need to try this out on a bigger system; my initial target is to fire up a Spring-based application and test it in-situ.</li>
<li>I'm focussing on testing backend Java code. Could I take the same test, with a different set of "steps", and get it to test a webapp (maybe using <a href="http://wtr.rubyforge.org">Watir</a>)</li>
<li>I'm not fond of the <span class="caps">HTML </span>output for running all tests; I'd rather get something like the JUnit reports from Ant (and Maven) - a summary page, with links to the details. This would involve building a smarter formatter, which would mean learning a bunch more Ruby (attractive in its own right)</li>
<li>I'll probably need to learn how to do set-up and tear-down (setup's kind of in the "Given" section?)</li>
<li>I still need to play with the <em>other</em> type of RSpec test - the <a href="http://rspec.info/examples.html">model spec</a>. This one isn't quite as powerful a driver for me; there's no plain-text runner (or is there?), and it's more in the traditional unit-test scope for Java. Still, it would be useful in some circumstances, so I'd like to know how it works.</li>
</ul>



<p>Lots of fun to be had. Hope you've enjoyed reading this, and can take something away. Please bear in mind that I'm not a Ruby expert, so if you need help troubleshooting this stuff, I suggest you go to the appropriate user communities. <span class="caps">OTH, </span>if you've got suggestions or improvements on my technique, or comments about the approach, I'd love to hear from you; the comments box is just below.</p>]]>
    </content>
</entry>
<entry>
    <title>Hibernate Query (Lack Of) Caching</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2008/02/hibernate_query.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=214" title="Hibernate Query (Lack Of) Caching" />
    <id>tag:twasink.net,2008:/blog//3.214</id>
    
    <published>2008-02-12T11:29:10Z</published>
    <updated>2008-02-12T12:33:23Z</updated>
    
    <summary>Hibernate has long had a feature known as &quot;query caches&quot; - you can run a query, cache the result, and thus avoid running it repeatedly. The only problem is that it doesn&apos;t do what you think it does....[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Hibernate" />
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Hibernate has long had a feature known as "query caches" - you can run a query, cache the result, and thus avoid running it repeatedly. The only problem is that it doesn't do what you think it does.</p>]]>
        <![CDATA[<p>Here's an example of what I mean by a query cache:</p>

<pre><code language="java">
List blogs = sess.createQuery(&quot;from Blog blog where blog.blogger = :blogger&quot;)
    .setEntity(&quot;blogger&quot;, blogger)
    .setMaxResults(15)
    .setCacheable(true)
    .setCacheRegion(&quot;frontpages&quot;)
    .list();</code></pre>

<p>(The example code has been lifted from <a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/performance.html#performance-querycache">the Hibernate on-line manual</a>).</p>

<p>On the surface, this sounds like it would cache the results of the query until the (separately configured) cache expires the results. And if you never change any data, that's what happens. The catch, however, is that any change to any of the criteria fields results in the cache being invalidated.</p>

<p>Let's go for a realistic example. Customers have Orders. You create a page where customers can browse their order history. This doesn't have to be up-to-the-minute, so you put a query cache in, with a suitable expiry policy. The query would look similar to the above: <code>from Order order where order.customer = :customer</code>. So far so good. In testing, you verify that, yes, the query cache works and the database is not repeatedly called with queries.</p>

<p>However, when this goes live, the database starts getting swamped with these queries. It's almost like the query cache isn't getting used at all. Why? Because <em>every time</em> a customer (any customer) places an order, <em>all</em> the cached query results get removed - even if they were for a different customer.</p>

<p>This explains a little throw-away line in the Hibernate manual:</p>

<blockquote><p>"Most queries do not benefit from caching, so by default queries are not cached."</p></blockquote>

<p>Why do most queries not benefit from caching? Because, in order to get cache hits, you need to essentially be querying for static or nearly-static data.</p>

<p><span class="caps">BTW, </span>the same goes for collections. If your Customer class has a collection of Orders, you can define that collection to be cacheable; it's a separate cache from the Customer object, but it's cacheable. However, as soon as any order is made - or even merely modified - the cache will be invalidated.</p>

<p>This can make one-to-many relationships insanely expensive, particularly non-lazy ones; every time the entity goes into a session, the collection will be populated - even if the entity was already in the second level cache. If that collection cache is invalidated, as it probably is, then a database query will be run to populate the collection. If these objects <em>also</em> have a collection cache (think Customer -&gt; Order -&gt; Order LineItem), you will get <em>N</em> queries run. Go 4 levels (SalesPerson.getRecentCustomers()?), and you get <em>n*m</em> queries. Ouch.</p>

<p>Hibernate has very flexible caching policies for entities, but no flexibility around the relationships between the entities. This is a shame, particularly for the query caches - it means that for a number of types of applications, you need to build a separate caching layer <em>above</em> Hibernate, simply because the query caches get wiped far too often. At the very least, the Hibernate documentation could explain this better, elaborating on exactly why "most queries do not benefit from caching".</p>]]>
    </content>
</entry>
<entry>
    <title>... and sometimes they don&apos;t.</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2008/01/_and_sometimes.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=213" title="... and sometimes they don't." />
    <id>tag:twasink.net,2008:/blog//3.213</id>
    
    <published>2008-01-21T13:16:27Z</published>
    <updated>2008-01-22T14:45:02Z</updated>
    
    <summary>I&apos;ve spent most of the last year involved in an intensive project that really drained me - hence the lack of blogging. I want to blog in a positive fashion this year, so I&apos;ll start by getting a lot of...[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>I've spent most of the last year involved in an intensive project that really drained me - hence the lack of blogging. I want to blog in a positive fashion this year, so I'll start by getting a lot of gripes off my chest. :) Call it things that suck.</p>

<p>Here's a short list, before I define what I mean by "suck":</p>


<ul>
<li>Maven2</li>
<li><span class="caps">EJB3</span> Persistence</li>
<li>Hibernate, caches, and the way they can kill your database.</li>
<li>Maven2 (it just sucks a lot!)</li>
<li>No Java 6 on the Mac</li>
<li>No "next/previous word" keyboard navigation in the Mac terminal</li>
<li>Mac Firefox, drop-down boxes, and tabbing</li>
</ul>



<p>and I'm sure that there's more that will come to me. Today, I'm doing Maven - the rest will come later.</p>]]>
        <![CDATA[<h2>What do I mean by "suck"?</h2>

<p>Mostly, I mean "it doesn't do what I want it to do". Most of these things actually have a lot of positive aspects to them, but there are features about them that just piss me off.</p>

<h2>Maven2</h2>

<p>We shifted to using Maven2 at work about this time last year. What a can of worms that was. I'm going to stress here that it was certainly a net-positive move, but it was a very bumpy ride. (I'm just going to call it Maven from here on)</p>

<p>The basic idea of Maven is great, and there are two sweet spots that Maven hits very well - building <span class="caps">JAR</span>s, and building single webapps. If you're doing one of these, particularly if you're doing an open-source one, then Maven will have a lot of positive things for you. But if you're building more complex things, then Maven will almost certainly throw a few roadblocks in the way.</p>

<p>First, it's buggy, and the bugs don't get fixed fast. Great example - the Surefire plugin (the JUnit testrunner) 2.2 was released in April 2006. It was the version used by default in Maven 2.0.4 (also released in April 2006), and in Maven 2.0.5 (released January 2007). It had <a href="http://jira.codehaus.org/browse/SUREFIRE-122">a bug</a> - a pretty nasty one. When you ran several tests at once, the <span class="caps">XML </span>log files for a given test contained all of the tests run previously as well. Our Cruise builds were reporting our builds had nearly half a million tests, instead of the couple of thousand they should have. You can imagine the size of the log files, too. And, of course, if one of the early tests failed, then you had a couple of thousand test failures reported.</p>

<p>The bug was reported 22 May 2006. It was reported again on <a href="http://jira.codehaus.org/browse/SUREFIRE-226">17 July</a>, <a href="http://jira.codehaus.org/browse/SUREFIRE-52">05 August</a>, <a href="http://jira.codehaus.org/browse/SUREFIRE-256">10 August</a>, <a href="http://jira.codehaus.org/browse/SUREFIRE-160">11 October</a> and <a href="http://jira.codehaus.org/browse/SUREFIRE-312">24 March 2007</a>. 3 of those were marked as Major, while 2 were marked as "Cannot Reproduce". A precise fix for the problem was mentioned on 9 August, with a 6-line patch provided (by the same person) on 28 August. This fix - for a Major bug identified 5 times that essentially broke the plugin for use in a CI environment - wasn't applied to the codebase until 27 November 2006. They didn't get a 2.3 release out until 23 February - too late for the Maven 2.0.5 release in January. (You could select it explicitly if you wanted to, but you had to know to do so) So it didn't get into the mainstream until 1 April 2007 (and who releases software on April Fool's Day?) - over 10 months after the original buggy release, on a project that brags about how it can be used to quickly push bug fixes out.</p>

<p>On average, our builds have just stopped dead due to a bug from a Maven plugin every 2 months or so. The fun ones are for the bugs in plugins that "auto-update" (i.e. don't have pinned versions). Case in point - over Christmas, the Maven Invoker plugin 1.1 was released. Somehow, this broke the Antrunner plugin so that if it was invoked in a multi-module reactor build, the working directory was changed. This broke one of our modules - but only if you invoked the module as part of a multi-module build. If you built the module by itself, it worked fine. Makes it easy to debug when the problem goes away when you try to reproduce it!</p>

<p>Combining multi-module builds causes fun interactions. The best one I've got is that the classpath for a plugin can only be set once. So, as a <em>purely hypothetical</em> example, if you had, oh, one module that wanted to run <span class="caps">SQL </span>against, say, an Oracle database, using the <span class="caps">SQL </span>plugin, and another module that wanted to run <span class="caps">SQL </span>against, say, a MySQL database, you have to give <em>both</em> modules <em>both</em> sets of <span class="caps">JDBC </span>drivers. (Why do it for both? Because Maven isn't deterministic between versions/JDKs for modules that don't have any dependencies - i.e., you can build them in any order).</p>

<p>Then there's the quality of the <span class="caps">POM</span>s. Commons Logging is a <a href="http://repo1.maven.org/maven2/commons-logging/commons-logging/1.1/commons-logging-1.1.pom">great example</a> - here's a tool designed to isolate you from the exact logging library you use, and it brings in <em>3</em> logging libraries into your "compile" scope, one of which is Log4J which commons-logging "greedily" looks for. It also brings in the servlet <span class="caps">API </span>for some reason - again, at "compile" scope. Now, sure, you <em>could</em> use the commons-logging-api module instead. Except <em>nobody</em> else does. So if you're not using commons-logging explicitly (and unless you're building an open-source <span class="caps">JAR </span>to be used by someone else, why would you?) you have to deal with these stupid dependencies <strong>that should never have been forced on you.</strong></p>

<p>Now, it's not hard to fix this. Create a <code>&lt;dependencyManagement&gt;</code> section (preferably in the parent <span class="caps">POM </span>for your multi-module project) and define the exclusions for commons-logging. Except that, sometime between Maven 2.0.4 and Maven 2.0.6, this stopped working for transitive dependencies (esp. if the dependency was third-or-more hand). So you use, say, commons-beanutils, and it brings in Log4j. <span class="caps">WTF</span>??? Furthermore, the exact place that this occurs in the transitive hierarchy seems to shift between Maven versions. The short version of this is that I have had to alter <span class="caps">POM</span>s with 3 of the 4 Maven releases since I started using Maven just to cope with build breakages <em>caused by upgrading Maven</em>.<br />
  <br />
I mentioned earlier that Maven has a sweet spot with building webapps. The sour spot is building <span class="caps">EAR </span>files - especially if you've got multiple webapps. Why? Because Maven will try to shove all the libraries into the <span class="caps">WEB</span>-INF of the webapps - great for building standalone webapps, bad for <span class="caps">EAR</span>s where you want to put the libraries in the <span class="caps">EAR </span>instead (to share with any <span class="caps">EJB</span>s, or just to reduce duplication between webapps). Now, the <span class="caps">WAR </span>plugin for maven does realised this, so they give a simple switch to turn off the "bundle in the <span class="caps">WEB</span>-INF" behaviour. So far so good.</p>

<p>Except that many web-frameworks (notably Struts and WebWork (aka Struts2)) use static variables for configuration (the "static-as-singleton" antipattern). So if you put two Webwork-based webapps in the same <span class="caps">EAR, </span>you need to bundle the Webwork libraries in the <span class="caps">WEB</span>-INF/lib dir of each webapp. Except the <em>only</em> way to do this with Maven is to go through and explicitly make all the other dependencies "provided" scope and <span class="caps">THEN </span>go into the <span class="caps">EAR'</span>s pom and make them all "compile" scope so that they get bundled in the <span class="caps">EAR.</span> Aaarggh!</p>

<p>You also absolutely have to set up your own internal Maven repository, and pin down all the versions of both dependencies and the plugins. Without this, you don't get repeatable builds - ie, you can't go back, say, six months in your source control and do a build (even if you use the same version of Maven you did then). The plugins would have changed, and they change in incompatible ways. Some of those ways are just designed to make your life a pain - case in point with the Surefire plugin is that they changed what the default values of some options were. However, there are no good and easy ways to do an internal Maven repository (though there are some getting better).</p>

<p>Multi-module builds and versioning are a pain. You can define a parent <span class="caps">POM </span>for your <span class="caps">POM </span>- you can even specify a relative path to the parent <span class="caps">POM.</span> However, you have to specify the version number of the parent <span class="caps">POM </span>- it can't just look it up from the relative path. So if you have, say, a dozen modules in your project, you've repeated the version of the parent <span class="caps">POM</span> 12 times. <span class="caps">WTF</span>??</p>

<p>Some petty gripes to wind up on:</p>


<ul>
<li>the Eclipse plugin (for Maven, not the Maven plugin for Eclipse) doesn't remember that it couldn't fetch source. So when you generate the Eclipse projects for your dozen-module projects, it goes out to the web a dozen times for each dependency it can't find the source for (i.e. most of them)</li>
<li><em>What</em> is the problem with attributes on <span class="caps">XML </span>elements? Ivy specifies dependencies in one line. Maven needs 5. When you're reading through a <span class="caps">POM, </span>the excessive repetition makes it harder to find problems. Seriously, why can't we just say <code>&lt;dependency groupId=&quot;foo&quot; artifactId=&quot;bar&quot; version=&quot;1.2.3&quot; scope=&quot;compile&quot;&gt;</code>? <span class="caps">BTW, </span>this repetition tosses away any size benefits of Maven vs Ant - when I migrated from Ant to Maven, the <em>total byte size</em> of the build files went up by about 10% (the line count went up by about a third). Today, I'm averaging about 25 lines of Java code for every line in our <span class="caps">POM</span>s.</li>
<li>Not so petty - why can't we have extra scopes? The hard-coded scopes aren't flexible enough (see the webapp problem)</li>
<li>I've almost certainly spent more time and effort in the last year wrestling with <span class="caps">POM </span>files than I spent in the previous 7 years wrestling with Ant.</li>
<li>It's not like using Maven gets you away from using Ant anyway. I mean, why can't they at least provide an "execute" plugin?</li>
<li>The documentation is patchy - some parts are great, most are crappy with holes in it. The "Better Builds With Maven" book , for example, has a section on defining your own plugins using a version of the plugin tools that was not released when the book was published (and I think it's still not released). You also can't see documentation for older versions of plugins, and it can be hard to work out which version you're using (great when, for example, the behaviour of default values for options change!)</li>
</ul>



<p>To finish, I'll just quickly pre-empt most of the comments I'm likely to get:</p>


<ul>
<li>"Maven is free - stop bitching". Yes, Maven is free, and yes you get what you pay for. But it's only free if your time is worth nothing. Most of these problems take serious time to get around.</li>
<li>"Why not contribute back to the Maven community?" Well, for starters, I'm personally very busy - "intensive project that really drained me", remember? My family then takes up most of my time. Of course, work could contribute back - but it's hard to make the argument to say "we should spend time improving this buggy software we've chosen to use" (and Wotif is friendly with regards to contributing to open source). Furthermore, the Maven community has a real chip on its collective shoulder - I lurk on the Maven user and developer mailing lists, and there's a <em>lot</em> of attitude floating around on both. In order to make an improvement, you first have to identify a problem, and identifying problems is almost tantamount to attacking Maven on those lists - and many people take that personally. Even if you do come up with an improvement, and do the work to put in a patch, it's likely to get either rejected because the plugin owner disagrees with the purpose of the patch, rejected because "it will be fixed in Maven 2.1", or just really slow going in (see the Surefire bug above). In other words, the Maven community is just hard to join.</li>
<li>"If you don't like Maven, go back to Ant". Like I said, Maven is a net positive for us as a development team (even if it's probably a net negative for me personally). I just prefer my experiences to be smoother.</li>
<li>"Why haven't you reported the bugs?" We did in some cases. There are plenty of bugs we didn't report, though - mostly because they tend to be hard to get into, and we end up scratching our heads about how to come up with a simple example for a bug report (we can't exactly attach our entire code base to a bug report). Also, it can be really hard to follow the build process in detail (i.e. in the code) to work what's going wrong so you can report a bug more detailed than "Our build doesn't work"</li>
<li>"Don't like it? Come up with something better" Oh, please don't tempt me. :)</li>
</ul>



<p><span class="caps">FWIW,</span> I don't think the current state of Maven, and the Maven community in particular, is going to last. Either the tool and the community will mature (and get less defensive), or it's going to fall over in a flaming heap and be replaced by something better. If I had to bet, my money would be on the latter - probably around the end of the year, or early-to-mid next year. The Java community wants a tool like Maven, needs a tool like Maven - that's the only reason why a tool as shitty as Maven has developed such a large following. What it needs is a better Maven.</p>]]>
    </content>
</entry>
<entry>
    <title>Sometimes, things just work...</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2008/01/sometimes_thing.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=212" title="Sometimes, things just work..." />
    <id>tag:twasink.net,2008:/blog//3.212</id>
    
    <published>2008-01-21T05:23:42Z</published>
    <updated>2008-01-21T06:26:24Z</updated>
    
    <summary>9 months of intense development later, our first big integration project, with Tourico Holidays, went live today, with an additional 1000-odd hotels appearing on our site (with more to come). A good example is Texas, where we had only a...[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>9 months of intense development later, our first big <a href="http://images.wotif.com/data/asx_announcement/asx_announcement_1206.pdf">integration project</a>, with Tourico Holidays, went live today, with an additional 1000-odd hotels appearing on our site (with more to come). A good example is <a href="http://www.wotif.com/search/Advanced?country=US&amp;region=11666">Texas</a>, where we had only a few hotels outside of the major cities and we now have about 90 (plus more inside the major cities, of course).</p>]]>
        <![CDATA[<p>We had the usual problems you get with integration projects - mostly around communication problems. I can't get into specifics about the project, but here's some good rules of thumb if you're doing integration projects:</p>


<ul>
<li>Communicate. Communicate often - the more the better. Whenever you have problems, ask!</li>
<li>Agree on the inter-application communication protocols <em>early!</em></li>
<li>Write spikes to test the inter-application communication protocols. Evolve the spikes into test harnesses. Share the test harnesses!</li>
<li>Run the test harnesses regularly to make sure the protocols haven't been changed by accident.</li>
<li>Provide test sandpits for your partner's client software to come and play. Ask for the favour to be returned.</li>
<li>Agree on an expected load profile, as well as Service Level Agreements. Work out how you will monitor it - remember that your partner may not be set up to deal with a sudden load spike, even if you are.</li>
</ul>



<p>At the end of all this, turning the integration with Tourico on was somewhat of an anti-climax - we pushed the button marked "On", and it just worked.</p>

<p>Now maybe I can get my life back for a bit. :)</p>]]>
    </content>
</entry>
<entry>
    <title>The rumours of Ruby&apos;s death are greatly exaggerated...</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/05/the_rumours_of.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=211" title="The rumours of Ruby's death are greatly exaggerated..." />
    <id>tag:twasink.net,2007:/blog//3.211</id>
    
    <published>2007-05-22T06:43:05Z</published>
    <updated>2007-07-10T01:24:42Z</updated>
    
    <summary>Sheesh... it seems like every man and his dog is jumping on the latest TIOBE index figures showing a very small dip in the popularity of Ruby. Talk about lies, damned lies, and statistics......[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Sheesh... it seems like <a href="http://blueboard.com/smalljava/archives/70">every man</a> and <a href="http://gregluck.com/blog/archives/2007/05/_ruby_in_declin.html">his dog</a> is jumping on the latest <a href="http://www.tiobe.com/tpci.htm"><span class="caps">TIOBE </span>index</a> figures showing a <em>very</em> small dip in the popularity of Ruby. Talk about lies, damned lies, and statistics...</p>]]>
        <![CDATA[<p>Firstly, let me point out the obvious - it's a bloody small dip. Yes, Ruby has plateaued over the last few months. But so did every language - well, except for the ones that dropped. It's got comparable mindshare to Python, JavaScript and C#, and nobody is saying that they're vanishing.</p>

<p>Secondly, let's talk about what the <a href="http://www.tiobe.com/index.htm?tiobe_index"><span class="caps">TIOBE </span>index actually is</a>. It's a measure of how many hits you get back from a search engine when you enter in "&lt;language&gt; programming". That's it. In my highly unscientific search on google just then, I found that <a href="http://www.google.com/search?q=java+programming">java programming</a> got back about 176,000,000 hits, while <a href="http://www.google.com/search?q=ruby+programming">ruby programming</a> got 55,000,000 hits.  Yahoo got <a href="http://search.yahoo.com/search?p=java+programming">48,500,000</a> and <a href="http://search.yahoo.com/search?p=ruby+programming">16,800,000</a>, while <span class="caps">MSN </span>got <a href="http://search.msn.com/results.aspx?q=java+programming">18,507,589</a> and <a href="http://search.msn.com/results.aspx?q=ruby+programming">1,634,857</a>. So Google and Yahoo think that Java's about 3 times more popular than Ruby, while <span class="caps">TIOBE </span>and <span class="caps">MSN </span>think it's about 8ish.</p>

<p>(Curiously, <em>all</em> of the search engines said Ruby was more popular than C# - 35,000,000, 15,200,000 and a mere 354,986 hits on the respective search engines. But C# has a higer <span class="caps">TIOBE </span>rating than Ruby - 3.656% vs 2.632% for Ruby)</p>

<p>Furthermore, this doesn't even look at the popularity of certain related keywords. For example, on Google, <a href="http://www.google.com/search?q=j2ee+programming">j2ee programming</a> gets 17,300,000 hits while <a href="http://www.google.com/search?q=rails+programming">rails programming</a> gets 24,200,00 hits. So Rails programming is almost 50% more popular than <span class="caps">J2EE </span>programming!</p>

<p>Internet search hits aren't the only measure, anyway. What about <a href="http://radar.oreilly.com/archives/2007/05/state_of_the_co_10.html">book sales</a>, for example, where Ruby enjoyed a 68% growth in sales in Q1 '07 (compared with Q1 '06) in a shrinking market? Personally, I'd pay more attention to interest expressed in dollars spent than I would to internet hits.</p>

<p>For the record, I'm <em>not</em> a Ruby developer - I'm a <span class="caps">J2EE </span>specialist with about 8 years of solid Java experience (plus a few years of other stuff) behind me. Ruby looks nice, but I've never taken enough time to play with it, let alone seriously work with it. But people who push news like this out of context and celebrate the "death" of a programming language (especially prematurely!) disgust me. Get a life, guys, and learn how to read the stats, not read your own opinion into them.</p>]]>
    </content>
</entry>
<entry>
    <title>If it quacks like a startup, it is a startup...</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/05/if_it_quacks_li.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=210" title="If it quacks like a startup, it is a startup..." />
    <id>tag:twasink.net,2007:/blog//3.210</id>
    
    <published>2007-05-22T06:33:22Z</published>
    <updated>2007-05-22T06:33:38Z</updated>
    
    <summary>Mike Cannon-Brookes asks Is Atlassian still a startup? And when is a startup not a startup anymore?. Well, Mike, the answer&apos;s simple. If it still feels like a startup, it&apos;s a startup....[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Agile Development" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Mike Cannon-Brookes asks <a href="http://blogs.atlassian.com/rebelutionary/archives/2007/05/when_is_a_startup_not_a_startup_anymore.html">Is Atlassian still a startup? And when is a startup not a startup anymore?</a>. Well, Mike, the answer's simple. If it still feels like a startup, it's a startup.</p>]]>
        <![CDATA[<p>Being part of a startup is about the attitude, not the company size. Successful startups - the ones that take the world by storm, the ones that everyone wishes they could work at - are those where the employees/partners/associates are energised and passionate about their work. The focus is delivering a <em>great</em> product or service, not merely a good one. You can't do everything, but what you can do, you do with 100% dedication. It's not a certain recipe for success, but anything else is a near certain recipe for failure.</p>

<p>I've worked at three startups now (though never at the really early stages). When they lose this feel, the change was always dramatic and noticeable - it's not a question you ask, but a realisation that hits you like a punch to the stomach. If you have to ask the question, you're probably still a startup. :)</p>

<p>The most noticeable sign you're no longer a startup - when you get concerned about defending marketshare rather than how to grow it. The change to a defensive, risk-adverse mindset kills the startup mentality (risk-adverse startups don't even get off the ground). I'd also say that becoming a publicly listed company kills the startup mindset as well.</p>

<p><a href="http://www.wotif.com">Wotif</a> lost the startup feel about 18 months ago, I think. Don't get me wrong - it's still a great place to work, with great people - but it's not a startup anymore, and to be honest the change wasn't positive. It's a comparable size to Atlassain in most ways - similar employee &amp; revenue size (126 employees, AU$45.5million in revenue for <span class="caps">FY2006</span>), but it's not a startup anymore. Congratulations on keeping the startup feel, Mike, and keep those world-class products coming.</p>]]>
    </content>
</entry>
<entry>
    <title>Why external Maven repositories are a bad idea</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/04/why_external_ma.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=209" title="Why external Maven repositories are a bad idea" />
    <id>tag:twasink.net,2007:/blog//3.209</id>
    
    <published>2007-04-04T22:09:50Z</published>
    <updated>2007-04-04T22:17:17Z</updated>
    
    <summary>Bandwidth costs to mirror Maven repositories: $50 File server to store it on: $1500 Developer effort to access the internal repository: $200 Having 20 developers sit around idle because you didn&apos;t mirror the java.net repository: priceless With apologies to Mastercard....
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Bandwidth costs to mirror <a href="http://maven.apache.org">Maven</a> repositories: $50<br />
File server to store it on: $1500<br />
Developer effort to access the internal repository: $200<br />
Having 20 developers sit around idle because you didn't mirror the <a href="http://repository:https://java-net.dev.java.net/Upgrade_Page_2007-04-04.html">java.net</a> repository: priceless</p>

<p>With apologies to Mastercard.</p>]]>
        
    </content>
</entry>
<entry>
    <title>Using EJB3 with Spring</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/02/using_ejb3_with.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=208" title="Using EJB3 with Spring" />
    <id>tag:twasink.net,2007:/blog//3.208</id>
    
    <published>2007-02-12T12:43:07Z</published>
    <updated>2007-02-13T00:00:43Z</updated>
    
    <summary>In my previous entry, I talked about how you could easily use Spring from within EJB 3 beans, thanks to the magic of EJB interceptors. But what about the other way? How do you use EJB 3 from Spring? Update:...[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>In <a href="/blog/archives/2007/01/using_spring_wi.html">my previous entry</a>, I talked about how you could easily use Spring from within <span class="caps">EJB</span> 3 beans, thanks to the magic of <span class="caps">EJB </span>interceptors. But what about the other way? How do you use <span class="caps">EJB</span> 3 from Spring?</p>

<p><strong>Update:</strong> It looks like the Interface21 guys have had similar ideas - check out <a href="http://interface21.com/pitchfork/">Project Pitchfork</a></p>]]>
        <![CDATA[<p><hr /></p>

<p>Well, back in the days of <span class="caps">EJB</span> 2, you would make a reference to an <span class="caps">EJB </span>using the following incantation:</p>

<pre><code>&lt;bean id=&quot;myEjb&quot; class=&quot;org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean&quot;&gt;
  &lt;property name=&quot;jndiName&quot; value=&quot;java:comp/env/ejb/MyEjb&quot; /&gt;
  &lt;property name=&quot;businessInterface&quot; value=&quot;net.twasink.ejb.MyEjb&quot; /&gt;
&lt;/bean&gt;</code></pre>

<p>Simple enough; the <code>LocalStatelessSessionProxyFactoryBean</code> knows how to create a Stateless Session Bean, via the Home interface, which is what the <span class="caps">JNDI </span>entry points out. There's a couple of different proxy factories, for the various types of <span class="caps">EJB</span>s.</p>

<p>In <span class="caps">EJB3, </span>you don't need <code>Home</code> interfaces any more. As a result, you don't need to use a <code>LocalStatelessSessionProxyFactoryBean</code> either. Instead, you use the standard <code>JndiObjectFactoryBean</code>:</p>

<pre><code>&lt;bean id=&quot;myEjb&quot; class=&quot;org.springframework.jndi.JndiObjectFactoryBean&quot;&gt;
  &lt;property name=&quot;jndiName&quot; value=&quot;java:comp/env/ejb/MyEjb&quot; /&gt;
  &lt;property name=&quot;expectedType&quot; value=&quot;net.twasink.ejb.MyEjb&quot; /&gt;
&lt;/bean&gt;</code></pre>

<p>So this is pretty simple: look the <span class="caps">EJB </span>up in the <span class="caps">JNDI, </span>register it as a Spring Bean, and then wire it into other Spring Beans as normal. So far so good.</p>

<p>Or is it? Well, this has got a major problem: you need to specify the <span class="caps">EJB</span>s. One of the big selling points of <span class="caps">EJB</span> 3 is that you don't <em>have</em> to manage any <span class="caps">XML </span>files, and here comes Spring making you do it all over again! How annoying!</p>

<p>Fortunately, there's a solution: <a href="http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-factory-extension-bpp">BeanPostProcessors</a>. BeanPostProcessors lets you do things to a bean after its created. Things like looking for setter methods with an <code>@EJB</code> annotation on them, for example.</p>

<p>Here's the code:</p>

<pre><code language="java">/** Look for EJB3-style annotations, and provide the corresponding values from the container. */
public class EjbBeanPostProcessor implements BeanPostProcessor {

  public Object postProcessAfterInitialization(Object bean, String beanName)
  throws BeansException {
    // do nothing; the EJBs are configured before initialization.
    return bean;
  }

  public Object postProcessBeforeInitialization(Object bean, String beanName)
  throws BeansException {
    Class beanClazz = bean.getClass();
    for (Method method : beanClazz.getMethods()) {
      if (method.isAnnotationPresent(EJB.class)) {
        setEjbRef(bean, method);
      }
    }
    return bean;
  }
  
  private &lt;T&gt; void setEjbRef(T bean, Method setter) {
    Class ejbClass = determineBeanClass(setter);
    Object ejbBean = lookupBean(ejbClass, determineJndiName(ejbClass, setter), bean);
    invoke(bean, setter, ejbBean);
  }
  
  /**
   * Determine the JNDI name for the EJB. By default, we assume that it is the simple class
   * name, but we allow the setter method to override this, using the 'mappedName' attribute.
   */
  private String determineJndiName(Class ejbClass, Method setter) {   
    EJB ejbAnnotation = setter.getAnnotation(EJB.class);
    if (ejbAnnotation.mappedName().equals(&quot;&quot;)) {
      return ejbClass.getSimpleName();
    }
    return ejbAnnotation.mappedName();
  }

  private Object lookupBean(Class ejbClass, String beanName, Object bean) throws BeansException {
    try {
      return new InitialContext().lookup(&quot;java:comp/env/ejb/&quot; + beanName);
    } catch (NamingException e) {
      throw new EjbBeansException(bean, &quot;Could not resolve EJB &quot; + beanName, e);
    }
  }

  /**
   * Determine the type of the EJB. This is used to determine the JNDI name to look up. This
   * would normally be the type of the argument to the setter, but the EJB spec allows this
   * to be overriden (presumably to a subclass)
   */
  private Class determineBeanClass(Method setter) {
    EJB ejbRefAnnotation = setter.getAnnotation(EJB.class);
    if (ejbRefAnnotation.beanInterface() != null &amp;&amp; 
        Object.class.equals(ejbRefAnnotation.beanInterface()) == false) {
      return ejbRefAnnotation.beanInterface();
    }
    Class otherBeanClass = setter.getParameterTypes()[0];
    return otherBeanClass;
  }

  private &lt;T&gt; void invoke(T bean, Method method, Object ... args) {
    try {
      method.invoke(bean, args);
    } catch (IllegalArgumentException e) {
      throw new EjbBeansException(bean, &quot;Could not set EJB&quot;, e);
    } catch (IllegalAccessException e) {
      throw new EjbBeansException(bean, &quot;Could not set EJB&quot;, e);
    } catch (InvocationTargetException e) {
      throw new EjbBeansException(bean, &quot;error setting EJB reference&quot;, e.getCause());
    }
  }

  /** Simple exception detailing the type of problem that occured. */
  private static class EjbBeansException extends BeansException {
    public EjbBeansException(Object bean, String message, Throwable cause) {
      super(message + &quot; [bean class: &quot; + bean.getClass().getName() + &quot;]&quot;, cause);
    }
  }
}</code></pre>

<p>Having created this lovely BeanPostProcessor, you enable it in your Spring context file with a simple one-line statement, defining the post-processor:</p>

<pre><code>&lt;bean class=&quot;net.twasink.EjbBeanPostProcessor&quot; /&gt;</code></pre>

<p>Now, you simply need to add <code>@EJB</code> references to your Spring beans, and they will magically be set. No need to list the <span class="caps">EJB</span>s in your Spring context files or anything - it just works. It also works with anything else using Spring; we have all our <a href="http://www.opensymphony.com/webwork/">WebWork</a> actions configured this way now.</p>

<p>The only caveat to this is that you need to have the <span class="caps">JNDI </span>context set up. In a web application, for example, this means you need to have the <code>ejb-ref</code> entries in your <code>web.xml</code> file.</p>

<p>I hope you find this useful; I know I have.</p>]]>
    </content>
</entry>
<entry>
    <title>Comments re-enabled</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/02/comments_reenab.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=207" title="Comments re-enabled" />
    <id>tag:twasink.net,2007:/blog//3.207</id>
    
    <published>2007-02-12T08:03:07Z</published>
    <updated>2007-02-12T08:05:02Z</updated>
    
    <summary>I had to disable comments a little while back, due to excessive blog spam (the alternative was to have my ISP kick me off their servers). I&apos;ve now made some changes to MT to do the CAPTCHA filtering earlier -...
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="General" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>I had to disable comments a little while back, due to excessive blog spam (the alternative was to have my <span class="caps">ISP </span>kick me off their servers). I've now made some changes to MT to do the <span class="caps">CAPTCHA </span>filtering earlier - hopefully it's a bit nicer now. We'll see.</p>]]>
        
    </content>
</entry>
<entry>
    <title>Using Spring with EJB 3</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/01/using_spring_wi.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=206" title="Using Spring with EJB 3" />
    <id>tag:twasink.net,2007:/blog//3.206</id>
    
    <published>2007-01-27T23:40:13Z</published>
    <updated>2007-01-28T00:38:59Z</updated>
    
    <summary>Back when we were planning the migration to Glassfish, I realised we would have two dependency-injection frameworks in use - EJB 3 and Spring. For obvious reasons, I wanted to know more about how these would interact. At the time...[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Back when we were planning the migration to Glassfish, I realised we would have two dependency-injection frameworks in use - <span class="caps">EJB</span> 3 and Spring. For obvious reasons, I wanted to know more about how these would interact. At the time (last July), I couldn't find anyone who had used <span class="caps">EJB</span> 3 and Spring together - even <a href="http://interface21.com/people/ben.html">Ben Alex</a> from Interface21 hadn't come across it. Six months later, and I still haven't heard of anyone using Spring <em>from</em> <span class="caps">EJB</span>s. Except for us.</p>]]>
        <![CDATA[<p>There's lots of reasons to use both <span class="caps">EJB</span> 3 and Spring. <span class="caps">EJB</span>s have benefits Spring doesn't give - integration with things like Servlets and <span class="caps">JSP </span>tags, better integration with the container for monitoring and management, tool support, and so on. Spring has benefits as well - most notably, you don't need the container, but also you get more control over the bean lifecycle. Together, they are a good complement.</p>

<p>But how do you get a Spring-managed bean from an <span class="caps">EJB</span>? The answer: dependency injection, of course. That is, with the aid of a <span class="caps">EJB</span> Interceptor:</p>

<pre><code language="java">
public class SpringBeanInterceptor {

  private BeanFactory beanFactory;

  @PostConstruct public void configureSpringBeans(InvocationContext context) throws Exception {
    for (Method method : context.getTarget().getClass().getMethods()) {
      if (method.isAnnotationPresent(SpringBean.class)) {
        Class springClass = method.getParameterTypes()[0];
        String springName = determineSpringBeanName(method, springClass);
        Object springBean = beanFactory().getBean(springName);
        try {
          method.invoke(context.getTarget(), springBean);
        } catch (IllegalArgumentException e) {
          throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
          throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
          throw new RuntimeException(e.getCause());
        }
      }
    }
    context.proceed();
  }

  private String determineSpringBeanName(Method method, Class springClass) {
    String springName = method.getAnnotation(SpringBean.class).value();
    if (springName.length() &gt; 0) {
      springName = toCamelCase(springClass.getSimpleName());
    }
    return springName;
  }

  private String toCamelCase(String string) {
    return Character.toLowerCase(string.charAt(0)) + string.substring(1, string.length());
  }
  
  private BeanFactory beanFactory() {
    if (beanFactory == null) {
      BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(&quot;classpath*:beanRefContext.xml&quot;);
      BeanFactoryReference ref = locator.useBeanFactory(&quot;applicationContext-service&quot;);
      beanFactory = ref.getFactory();
    }
    return beanFactory;
  }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringBean {
  String value() default &quot;&quot;;
}</code></pre>

<p>That's it. This Interceptor will look over an <span class="caps">EJB, </span>after it's created, and provides the Spring beans. This version looks for 'setter-methods' with an <code>@SpringBean</code> annotation (you could also change it to look for fields), and looks up a corresponding bean from the Spring context (the ContextSingletonBeanFactoryLocator makes this possible).</p>

<p>Here's how you would use it:</p>

<pre><code language="java">
@Stateless
@Interceptors(SpringBeanInterceptor.class)
public class MyServiceBean implements MyService {
  private MySpringBean bean;

  ...

  @SpringBean public void setMySpringBean(MySpringBean bean) {
    this.bean = bean;
  }
}</code></pre>

<p>With this (and one other trick I'll discuss soon), you can (nearly) seamless integrate <span class="caps">EJB3 </span>and Spring.</p>

<p>The one big limitation of this: with Stateless Session Beans, you can only inject stateless dependencies, and you can only inject them when the <span class="caps">EJB </span>is initialised.</p>]]>
    </content>
</entry>
<entry>
    <title>Testing EJBs without a container</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/01/testing_ejbs_wi.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=205" title="Testing EJBs without a container" />
    <id>tag:twasink.net,2007:/blog//3.205</id>
    
    <published>2007-01-27T21:57:49Z</published>
    <updated>2007-01-27T22:44:44Z</updated>
    
    <summary>One of the more annoying aspects of testing EJBs has always been the fact you need to bundle them up in a JAR (and often an EAR) and deploy them to a server to thoroughly test them. This process drags...[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>One of the more annoying aspects of testing <span class="caps">EJB</span>s has always been the fact you need to bundle them up in a <span class="caps">JAR </span>(and often an <span class="caps">EAR</span>) and deploy them to a server to thoroughly test them. This process drags out the development of unit tests, and makes life generally painful.</p>

<p>As of <span class="caps">EJB</span> 3, however, it's no longer necessary. instead, it is fairly trivial to mock out the container entirely (and safely!)</p>]]>
        <![CDATA[<p>I'm not talking about classic unit-tests, where <em>everything</em> is mocked out. You could already do that with <span class="caps">EJB2 </span>without too much hassle. No, I'm talking about tests that basically wire everything up the way it is when deployed, including such things as:</p>


<ul>
<li>references to other <span class="caps">EJB</span>s;</li>
<li>database connections;</li>
<li>security; and</li>
<li>transactional support</li>
</ul>



<p>How do you do it? Simple: you write a factory that creates your <span class="caps">EJB </span>instance and wires it up for you. This can be done through the power of reflection - by looking at the annotations.</p>

<p>(In theory, you could have done this with <span class="caps">EJB2, </span>as well, but the external deployment descriptor made it painful).</p>

<p>Enough talk. Here's some code that demonstrates this:</p>

<pre><code language="java">
public class EjbFactory {

  public interface EjbMocker {
    &lt;T&gt; void provideMocks(T bean);
  }
  
  private Map beans = new HashMap();
  private final EjbMocker mocker;
  
  public EjbFactory() {
    this(new NoMocks());
  }
  public EjbFactory(EjbMocker mocker) {
    this.mocker = mocker;
  }
  
  public &lt;T&gt; T createEjb(Class&lt;T&gt; service) {
    T bean = createBean(service);
    T decoratedBean = decorate(bean);
    registerEjb(service, decoratedBean);
    addEjbRefs(bean);
    runCustomInterceptors(bean);
    postConstruct(bean);
    mocker.provideMocks(bean);
    return decoratedBean;
  }

  private &lt;T&gt; void runCustomInterceptors(T bean) {
    if (bean.getClass().isAnnotationPresent(Interceptors.class)) {
      // TODO: This really should be built up as a chain, not a loop.
      Class[] interceptors = bean.getClass().getAnnotation(Interceptors.class).value();
      InvocationContext context = new MockInvocationContext(bean);
      for (Class interceptorClazz : interceptors) {
        postConstruct(newInstance(interceptorClazz), context);
      }
    }
  }
  private &lt;T&gt; T newInstance(Class&lt;T&gt; clazz) {
    try {
      return clazz.newInstance();
    } catch (InstantiationException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }
  
  private &lt;T&gt; T createBean(Class&lt;T&gt; service) {
    try {
      if (service.isAnnotationPresent(Stateless.class)) {
        return newInstance(service);
      }
      if (service.isInterface() == false) {
        throw new RuntimeException(&quot;Trying to mock a non-service: &quot; + service.getName());
      }
      Class serviceClass = Class.forName(service.getName() + &quot;Bean&quot;);
      return (T) newInstance(serviceClass);
    } catch (ClassNotFoundException e) {
      throw new RuntimeException(&quot;Could not find bean class for &quot; + service.getName());
    }
  }

  private &lt;T&gt; Object registerEjb(Class&lt;T&gt; service, T bean) {
    return beans.put(service, bean);
  }
  
  private &lt;T&gt; T decorate(T bean) {
    return bean;
  }
  
  private &lt;T&gt; void addEjbRefs(T bean) {
    Class beanClazz = bean.getClass();
    for (Method method : beanClazz.getMethods()) {
      if (method.isAnnotationPresent(EJB.class) &amp;&amp; ejbIsIgnored(method) == false) {
        setEjbRef(bean, method);
      }
    }
  }

  private boolean ejbIsIgnored(Method method) {
    List&lt;Class&gt; ignoredBeans = getIgnoredBeans();
    return ignoredBeans.contains(determineBeanClass(method));
    
  }
  private List&lt;Class&gt; getIgnoredBeans() {
    if (mocker.getClass().isAnnotationPresent(IgnoredEjbs.class)  false) {
      return Collections.emptyList();
    }
    Class[] ignoredEjbs = mocker.getClass().getAnnotation(IgnoredEjbs.class).value();
    return Arrays.asList(ignoredEjbs);
  }
  private <T> void setEjbRef(T bean, Method setter) {
    Class otherBeanClass = determineBeanClass(setter);
    Object otherBean = lookupBean(otherBeanClass);
    invoke(bean, setter, otherBean);
  }
  private Class determineBeanClass(Method setter) {
    EJB ejbRefAnnotation = setter.getAnnotation(EJB.class);
    if (ejbRefAnnotation.beanInterface() != null && 
        Object.class.equals(ejbRefAnnotation.beanInterface())  false) {
      return ejbRefAnnotation.beanInterface();
    }
    Class otherBeanClass = setter.getParameterTypes()[0];
    return otherBeanClass;
  }
  private &lt;T&gt; void invoke(T bean, Method method, Object ... args) {
    try {
      method.invoke(bean, args);
    } catch (IllegalArgumentException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
      throw new RuntimeException(e.getCause());
    }
  }
  
  private Object lookupBean(Class otherBeanClass) {
    if (beans.containsKey(otherBeanClass)) {
      return beans.get(otherBeanClass);
    }
    return createEjb(otherBeanClass);
  }

  private &lt;T&gt; void postConstruct(T bean) {
    Class beanClazz = bean.getClass();
    for (Method method : beanClazz.getMethods()) {
      if (method.isAnnotationPresent(PostConstruct.class)) {
        invoke(bean, method);
      }
    }
  }

  private void postConstruct(Object bean, InvocationContext context) {
    Class beanClazz = bean.getClass();
    for (Method method : beanClazz.getMethods()) {
      if (method.isAnnotationPresent(PostConstruct.class)) {
        invoke(bean, method, context);
      }
    }
  }

  private static class NoMocks implements EjbMocker {
    public &lt;T&gt; void provideMocks(T bean) {
      // do squat.
    }
  }
  
  private static class MockInvocationContext implements InvocationContext {
    private final Object bean;
    
    public MockInvocationContext(Object bean) { this.bean = bean; }
    
    public Object getTarget() { return bean; }
    
    public Object proceed() throws Exception {
      return null;
    }
    }
   // ... more mock methods here.
  }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoredEjbs {
  Class[] value() default { };
}</code></pre>

<p>Let me just run you through this...</p>

<p>You create a stubbed-out <span class="caps">EJB </span>like this: <code>new EjbFactory.create(MyService.class)</code>. The <code>createBean()</code> method does the following things:</p>


<ul>
<li>create an instance of your <span class="caps">EJB </span>(using a simple naming convention, in this case, to find the implementing class - MyServiceBean)</li>
<li>decorate the bean. In this early version, it does nothing. However, this would allow you to use dynamic proxies to implement security checking and transactional support.</li>
<li>add references to other <span class="caps">EJB</span>s, by looking for @EJB annotations.</li>
<li>run any interceptors declared for the <span class="caps">EJB, </span>and finally</li>
<li>invoke the post-construct methods.</li>
</ul>



<p>This example code isn't complete - it's based on an early version of what we built at Wotif. For example, there isn't any support in this for injecting resources (such as database connections - or persistence contexts). Nor is the transactional or security support demonstrated. However, it's very functional.</p>

<p>A couple of other features worth noticing:</p>


<ul>
<li>You can inject mocks or stubs into the created <span class="caps">EJB</span>s if you want (for example, we inject a security utility that's a real pain to use directly).</li>
<li>You can also ignore <span class="caps">EJB</span>s if you want, so that they don't get stubbed out. Presumably, if you need them, you would inject a mock (I've used this to fake out facades to remote servers, for example).</li>
</ul>



<p>We used this to convert a number of our <span class="caps">EJB </span>tests from calling against the deployed server to testing without deploying. As you can imagine, this makes the test-code cycle a lot faster.</p>

<p>While "strong" unit testing is still my preferred option, these more complete tests have their place - especially for testing persistence logic. This makes it a lot easier to test out of container, and reduces (not elimiate!) your need to test against a deployed app.</p>]]>
    </content>
</entry>
<entry>
    <title>Interfaces with EJB3</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/01/interfaces_with.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=204" title="Interfaces with EJB3" />
    <id>tag:twasink.net,2007:/blog//3.204</id>
    
    <published>2007-01-27T11:54:49Z</published>
    <updated>2007-01-28T00:34:05Z</updated>
    
    <summary>Starting last October, we went through a process of upgrading the main application at Wotif to be running under Java EE 5 (not just Java SE 5). The biggest part of this was upgrading from EJB 2 to EJB 3....[more..]
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Starting last October, we went through a process of upgrading the main application at Wotif to be running under Java EE 5 (not just Java SE 5). The biggest part of this was upgrading from <span class="caps">EJB</span> 2 to <span class="caps">EJB</span> 3.</p>

<p>One of the things I noticed was that <span class="caps">EJB3 </span>gives you a lot of choices for how to implement and configure <span class="caps">EJB</span>s and their associated interfaces. There's a lot of "standards" on how to do it, many of them conflicting, but very little explanation of why. This is the set of standards I came up with (and was largely successful in getting implemented), and why I chose them.</p>]]>
        <![CDATA[<h3>Don't annotate interfaces</h3>

<p>This one is simple enough: the <code>@Local</code> and <code>@Remote</code> annotations don't go on the interfaces. They will instead be placed on the actual <span class="caps">EJB.</span></p>

<p>Why do this? It's because I'm a fan of two concepts: domain-driven design, and implementation independence. I strongly believe in putting as much logic into my domain classes as possible. Things like <span class="caps">EJB</span>s provide services from the environment, not logic. This implies that domain classes can call out to services.</p>

<p>But I don't want to build a dependency on a given technology, such as <span class="caps">EJB</span>s, into my domain classes<sup class="footnote"><a href="http://twasink.net/blog/archives/2007/01/interfaces_with.html#fn1">1</a></sup>. There's no certainty that any given service <em>is</em> going to be an <span class="caps">EJB, </span>anyway. For this reason, I don't want the <span class="caps">EJB3 </span>service annotations on my service interfaces.</p>

<p>(It's probably worth pointing out here that we compile the domain classes separately from the services; these dependency rules are enforced at compile-time)</p>

<h3>Either Local or Remote, not both</h3>

<p>As a rule, any given <span class="caps">EJB </span>should be intended to be a local <span class="caps">EJB </span>or a remote <span class="caps">EJB.</span> Yes, it is <em>possible</em> for an <span class="caps">EJB </span>to implement both a local and remote interface, but there are lots of reasons that this is a bad idea.</p>

<p>Because an <span class="caps">EJB </span>should only be remote or local, the corresponding service interface should be conveniently named - e.g. MyService, not MyServiceLocal or MyServiceRemote.</p>

<p>Why do this? First, you have to think about why you'd use a remote interface in the first place. Ignoring reasons such as testing, the only real reason to go for a remote interface is that you want to put the client to the <span class="caps">EJB </span>on a different box than the service.</p>

<p>If you do this, there's a good chance that you will be upgrading the server and the client at separate times - maybe just a few minutes apart, but still separate. This means you need to think about the evolution of your classes, and how to deal with that upgrade problem. This, in turn, means that it's not just sufficient to make the types used in the remote interfaces serializable - they have to be externalizable as well.</p>

<p>At Wotif, we run every important piece of software across multiple servers for redundancy. Currently, we follow Fowler's First Law of Distributed Object Design, and run parallel instances rather than having distributed objects. This means, for example, that upgrades are done by taking one node out at a time, upgrading it, and putting it back in. If we used remote interfaces, we would either have to bind clients to their servers (creating two single-points-of-failure), or take out the entire cluster when we do an upgrade, OR spend effort designing robust interfaces that tolerate upgrades. Frankly, it's just easier to deal with local interfaces as a rule.</p>

<p>That said, most of our <span class="caps">EJB</span>s have both local and remote interfaces - the remote interface is present for the older-style tests that test the <span class="caps">EJB </span>against a deployed server. But these remote interfaces are not intended to be used by the application itself.</p>

<p>(Remote interfaces, at least under Glassfish, are also more annoying to manage - most of our remaining deployment descriptors deal with security settings for the remote interfaces)</p>

<h3>Configure the <span class="caps">EJB </span>via annotations</h3>

<p>Well, as much as possible, anyway. We still have an <code>ejb-jar.xml</code> file, as well as a <code>sun-ejb-jar.xml</code> file, but there isn't much in them. All that's in the <code>ejb-jar.xml</code> file is some environment variables that are configured at build time.</p>

<h3>Don't use Entity Beans</h3>

<p>This was more a legacy of our old design - we didn't use <span class="caps">EJB</span> 2 entity beans, relying instead on Hibernate for our persistence needs. Because we didn't use them before, we aren't using them now.</p>

<p>We will be investigating <span class="caps">EJB</span> 3 Entity Beans on future projects, but to be honest, I think that using Hibernate directly is still a better idea.</p>

<p>For a similar reason, we don't use Stateful Session Beans or Timer Beans (though Timer Beans will also be investigated as an alternative to Quartz).</p>

<h3>Use Dependency Injection</h3>

<p>The single biggest change for the better in <span class="caps">EJB</span> 3 is the introduction of dependency injection, via the <code>@EJB</code> and <code>@Resource</code> annotations. We make extensive use of them. Dependency injection rocks, and enables the next rule.</p>

<p>There's lots of good reasons to use dependency injection, and I won't bother re-iterating them here. The one thing to point out is that you can only inject threadsafe dependencies; with stateless session beans (and <span class="caps">MDB</span>s) you can only inject dependencies as part of the <span class="caps">EJB </span>initialisation steps.</p>

<h3>Unit test <span class="caps">EJB</span>s directly</h3>

<p>That is, write unit tests that instantiate the <span class="caps">EJB </span>implementation, configure it with suitable mocks, and test.</p>

<p>Solid unit testing, without deploying, is an essential part of building <span class="caps">EJB</span>s quickly and reliably. <span class="caps">EJB3 </span>finally makes this possible.</p>

<p>We also have a lot of <span class="caps">EJB </span>tests that are the primary place our database logic gets tested. We still need these tests, so to make it easier I wrote a test harness to automatically configure an <span class="caps">EJB </span>instance with their dependencies - ie, wiring up other <span class="caps">EJB </span>references and database connections. I converted a bunch of our previous "against-the-container" tests to using this new style, simply by changing how they obtained the <span class="caps">EJB </span>reference - no other change to the test required.</p>

<h3>Enough rules</h3>

<p>That's all the rules. By way of finishing this up, here's an example of what an <span class="caps">EJB </span>looks like:</p>

<pre><code language="java">
public interface HelloService {
 String sayHello();
}

@Stateless
@Local(HelloService.class)
@Remote(HelloServiceRemote.class)
public class HelloServiceBean implements HelloService {
   public String sayHello() { return &quot;Hello world&quot;; }
}

public interface HelloServiceRemote {
 String sayHello();
} </code></pre>

<p>And that's about it.</p>

<p>----</p>

<p class="footnote" id="fn1"><sup>1</sup> There are exceptions, the biggest one being persistence - I'm happy, for example, to use <span class="caps">EJB3 </span>and Hibernate-style persistence annotations on my domain classes. However, even in this case, I like to limit the dependency to the smallest amount possible. The idea is to trade the "purity" of implementation independence for some degree of convenience.</p>]]>
    </content>
</entry>
<entry>
    <title>Running on Glassfish</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2007/01/running_on_glas.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=203" title="Running on Glassfish" />
    <id>tag:twasink.net,2007:/blog//3.203</id>
    
    <published>2007-01-24T23:12:14Z</published>
    <updated>2007-01-24T23:15:14Z</updated>
    
    <summary>Well, it took long enough, but it&apos;s finally done: Response Headers - http://www.wotif.com/ X-Powered-By: Servlet/2.5, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1 Set-Cookie: PersistCountryCode=1; Domain=.wotif.com; Expires=Wed, 23-Sep-2037 22:11:45 GMT; Path=/ Content-Encoding: gzip Content-Type: text/html;charset=utf-8 Content-Length: 11520 Date: Wed,...
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="Java" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Well, it took long enough, but it's finally done:</p>

<pre><code>Response Headers - http://www.wotif.com/

X-Powered-By: Servlet/2.5, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1, JSP/2.1
Set-Cookie: PersistCountryCode=1; Domain=.wotif.com; Expires=Wed, 23-Sep-2037 22:11:45 GMT; Path=/
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Content-Length: 11520
Date: Wed, 24 Jan 2007 22:11:45 GMT
Server: Sun Java System Application Server Platform Edition 9.0_01
Connection: close

200 OK</code></pre>

<p>Phew. <em>Now</em> maybe I can get those backlogged <span class="caps">EJB3</span>-based articles out.</p>]]>
        
    </content>
</entry>
<entry>
    <title>Back again</title>
    <link rel="alternate" type="text/html" href="http://twasink.net/blog/archives/2006/11/back_again.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://twasink.net/cgi-bin/blog/mt-atom.cgi/weblog/blog_id=3/entry_id=202" title="Back again" />
    <id>tag:twasink.net,2006:/blog//3.202</id>
    
    <published>2006-11-15T01:43:46Z</published>
    <updated>2006-11-15T01:56:32Z</updated>
    
    <summary>Sorry for the long absence; I&apos;ve been inundated at work and in my personal life. To top it all off, I had to migrate my domain due to changes at my hosting provider. However, I expect to start posting again...
</summary>
    <author>
        <name>Robert</name>
        <uri>http://twasink.net</uri>
    </author>
            <category term="General" />
    
    <content type="text/html" xml:lang="en" xml:base="http://twasink.net/blog/">
        <![CDATA[<p>Sorry for the long absence; I've been inundated at work and in my personal life. To top it all off, I had to migrate my domain due to changes at my hosting provider.</p>

<p>However, I expect to start posting again soon - expect the regular stream of quality<sup class="footnote"><a href="http://twasink.net/blog/archives/2006/11/back_again.html#fn1">1</a></sup> articles that I used to produce.</p>

<p class="footnote" id="fn1"><sup>1</sup> Remember - everything has quality. Sometimes, you can even put a nice adjective in front.</p>]]>
        
    </content>
</entry>

</feed> 

