<?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"
	>

<channel>
	<title>Technical Notes</title>
	<atom:link href="http://blog.anthonychaves.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.anthonychaves.net</link>
	<description>My experience with Java, C, Ruby and more.</description>
	<pubDate>Mon, 06 Oct 2008 17:45:34 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.3</generator>
	<language>en</language>
			<item>
		<title>GridGain grid computing meeting on October 22</title>
		<link>http://blog.anthonychaves.net/java/2008/10/06/gridgain-grid-computing-meeting-on-october-22</link>
		<comments>http://blog.anthonychaves.net/java/2008/10/06/gridgain-grid-computing-meeting-on-october-22#comments</comments>
		<pubDate>Mon, 06 Oct 2008 17:45:34 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[scalability]]></category>

		<category><![CDATA[bostonsug]]></category>

		<category><![CDATA[grid computing]]></category>

		<category><![CDATA[gridgain]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/?p=55</guid>
		<description><![CDATA[If anyone is in the Boston/Waltham, MA area on October 22nd the Boston Scalability User Group is having a meeting with Nikita Ivanov from GridGain.  Free pizza and a free talk about Java-centric grid computing sounds like a good deal to me (disclaimer: I&#8217;m the meeting organizer).  Check out the meeting details at the BostonSUG [...]]]></description>
			<content:encoded><![CDATA[<p>If anyone is in the Boston/Waltham, MA area on October 22nd the Boston Scalability User Group is having a meeting with Nikita Ivanov from GridGain.  Free pizza and a free talk about Java-centric grid computing sounds like a good deal to me (disclaimer: I&#8217;m the meeting organizer).  Check out the meeting details at the <a href="http://www.bostonsug.org/2008/10/02/october-22-meeting-announcement/">BostonSUG web site</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/java/2008/10/06/gridgain-grid-computing-meeting-on-october-22/feed</wfw:commentRss>
		</item>
		<item>
		<title>Rails and Restful Authentication</title>
		<link>http://blog.anthonychaves.net/ruby/2008/07/11/rails-and-restful-authentication</link>
		<comments>http://blog.anthonychaves.net/ruby/2008/07/11/rails-and-restful-authentication#comments</comments>
		<pubDate>Fri, 11 Jul 2008 16:28:01 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[authentication]]></category>

		<category><![CDATA[restful_authentication]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/?p=54</guid>
		<description><![CDATA[It&#8217;s been quite a while since my last Ruby on Rails post so let&#8217;s go over a little &#8220;gotcha&#8221; that you might encounter when using the restful_authentication plugin.
Getting started with restful_authentication is fairly easy.  After installing the plugin it&#8217;s just a matter of running the following generate command:
script/generate authenticated user sessions
This will create a migration [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been quite a while since my last Ruby on Rails post so let&#8217;s go over a little &#8220;gotcha&#8221; that you might encounter when using the <a href="http://agilewebdevelopment.com/plugins/restful_authentication">restful_authentication plugin</a>.</p>
<p>Getting started with restful_authentication is fairly easy.  After installing the plugin it&#8217;s just a matter of running the following generate command:</p>
<pre>script/generate authenticated user sessions</pre>
<p>This will create a migration for a users table, a model and controller for users and a sessions controller.  It will also add some named routes to config/routes.rb.</p>
<p>So far so good, right?  Well, a common mistake when generating the model and controllers is to user the singular form of both user and session.  The command will run successfully and do exactly what you told it to do.  You won&#8217;t become aware of a problem until you try to log in for the first time and see this:</p>
<h1>NameError        in SessionsController#create</h1>
<pre>uninitialized constant SessionsController</pre>
<p><code>RAILS_ROOT: C:/rails/bookmarks</code></p>
<div id="traces"><a onclick="document.getElementById('Framework-Trace').style.display='none';document.getElementById('Full-Trace').style.display='none';document.getElementById('Application-Trace').style.display='block';; return false;" href="http://localhost:3000/session#">Application Trace</a> |             <a onclick="document.getElementById('Application-Trace').style.display='none';document.getElementById('Full-Trace').style.display='none';document.getElementById('Framework-Trace').style.display='block';; return false;" href="http://localhost:3000/session#">Framework Trace</a> |             <a onclick="document.getElementById('Application-Trace').style.display='none';document.getElementById('Framework-Trace').style.display='none';document.getElementById('Full-Trace').style.display='block';; return false;" href="http://localhost:3000/session#">Full Trace</a></p>
<div id="Application-Trace" style="display: block;">
<pre></pre>
</div>
<div id="Framework-Trace" style="display: none;">
<pre><code>c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:278:in `load_missing_constant'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:467:in `const_missing'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:479:in `const_missing'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/inflector.rb:283:in `constantize'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/core_ext/string/inflections.rb:143:in `constantize'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/routing/route_set.rb:386:in `recognize'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:148:in `handle_request'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:107:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:104:in `synchronize'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:104:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:120:in `dispatch_cgi'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:35:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/webrick_server.rb:112:in `handle_dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/webrick_server.rb:78:in `service'
c:/ruby/lib/ruby/1.8/webrick/httpserver.rb:104:in `service'
c:/ruby/lib/ruby/1.8/webrick/httpserver.rb:65:in `run'
c:/ruby/lib/ruby/1.8/webrick/server.rb:173:in `start_thread'
c:/ruby/lib/ruby/1.8/webrick/server.rb:162:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:162:in `start_thread'
c:/ruby/lib/ruby/1.8/webrick/server.rb:95:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:92:in `each'
c:/ruby/lib/ruby/1.8/webrick/server.rb:92:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:23:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:82:in `start'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/webrick_server.rb:62:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/commands/servers/webrick.rb:66
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:354:in `new_constants_in'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/commands/server.rb:39
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
script/server:3</code></pre>
</div>
<div id="Full-Trace" style="display: none;">
<pre><code>c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:278:in `load_missing_constant'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:467:in `const_missing'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:479:in `const_missing'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/inflector.rb:283:in `constantize'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/core_ext/string/inflections.rb:143:in `constantize'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/routing/route_set.rb:386:in `recognize'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:148:in `handle_request'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:107:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:104:in `synchronize'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:104:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:120:in `dispatch_cgi'
c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.1.0/lib/action_controller/dispatcher.rb:35:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/webrick_server.rb:112:in `handle_dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/webrick_server.rb:78:in `service'
c:/ruby/lib/ruby/1.8/webrick/httpserver.rb:104:in `service'
c:/ruby/lib/ruby/1.8/webrick/httpserver.rb:65:in `run'
c:/ruby/lib/ruby/1.8/webrick/server.rb:173:in `start_thread'
c:/ruby/lib/ruby/1.8/webrick/server.rb:162:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:162:in `start_thread'
c:/ruby/lib/ruby/1.8/webrick/server.rb:95:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:92:in `each'
c:/ruby/lib/ruby/1.8/webrick/server.rb:92:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:23:in `start'
c:/ruby/lib/ruby/1.8/webrick/server.rb:82:in `start'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/webrick_server.rb:62:in `dispatch'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/commands/servers/webrick.rb:66
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:354:in `new_constants_in'
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require'
c:/ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/commands/server.rb:39
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
script/server:3</code></pre>
</div>
</div>
<h2 style="margin-top: 30px;">Request</h2>
<p><strong>Parameters</strong>:</p>
<pre>{"commit"=&gt;"Log in",
"authenticity_token"=&gt;"0c0f07222da8ed58d8c6ebcafb9bf32f0bc84143",
"login"=&gt;"anthony",
"password"=&gt;"mysupersecretpassword"}</pre>
<p>Uh oh! what went wrong?  It looks like Rails is looking for a controller called SessionsController.  But we generated one named SessionController.  Why is this happening?  Take a look in the views/session/new.html.erb file.</p>
<pre>&lt;% form_tag session_path do -%&gt;</pre>
<pre>&lt;p&gt;&lt;label for="login"&gt;Login&lt;/label&gt;&lt;br/&gt;</pre>
<pre>&lt;%= text_field_tag 'login' %&gt;&lt;/p&gt;</pre>
<pre>
&lt;p&gt;&lt;label for="password"&gt;Password&lt;/label&gt;&lt;br/&gt;</pre>
<pre>&lt;%= password_field_tag 'password' %&gt;&lt;/p&gt;</pre>
<pre>
&lt;!-- Uncomment this if you want this functionality</pre>
<pre>&lt;p&gt;&lt;label for="remember_me"&gt;Remember me:&lt;/label&gt;</pre>
<pre>&lt;%= check_box_tag 'remember_me' %&gt;&lt;/p&gt;</pre>
<pre>--&gt;</pre>
<pre>
&lt;p&gt;&lt;%= submit_tag 'Log in' %&gt;&lt;/p&gt;</pre>
<pre>&lt;% end -%&gt;</pre>
<p>The form_tag target is session_path.  session_path is created for us by magic because of its mapping in routes.rb.</p>
<pre>map.resource :session</pre>
<p>When mapping a resource like this Rails looks for the controller with the pluralized name of the resource name by default, which means session_path is getting Session<strong>s</strong>Controller.  We can change the behavior by explicitly setting the controller for the resource.</p>
<pre>map.resource :session, :controller =&gt; :session</pre>
<p>Try to log in again and you will be authenticated!  Of course it may just be easier to remember to use the pluralized name of whatever controller you want to use for managing sessions.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/ruby/2008/07/11/rails-and-restful-authentication/feed</wfw:commentRss>
		</item>
		<item>
		<title>Terracotta Tech Talk</title>
		<link>http://blog.anthonychaves.net/java/2008/05/20/53</link>
		<comments>http://blog.anthonychaves.net/java/2008/05/20/53#comments</comments>
		<pubDate>Tue, 20 May 2008 17:21:01 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[hibernate]]></category>

		<category><![CDATA[spring]]></category>

		<category><![CDATA[memcached]]></category>

		<category><![CDATA[terracotta]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/05/20/53</guid>
		<description><![CDATA[I&#8217;m developing a Java/Spring web application deployed on JBoss 4.2.2 that makes heavy use of memcached in order to reduce the latency experienced by the user.  The user interacts with data that is (unbeknownst to them) in the cache.  In order to make changes permanent a message is placed on a queue that [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m developing a Java/Spring web application deployed on JBoss 4.2.2 that makes heavy use of memcached in order to reduce the latency experienced by the user.  The user interacts with data that is (unbeknownst to them) in the cache.  In order to make changes permanent a message is placed on a queue that is picked up by an MDB.  The message is just the key used to access the data in the cache.  The MDB takes the data out of the cache and then saves it to the database.  This greatly improves the user experience because they do not have to wait for the next page to load while costly database writes occur.  So far, so good.</p>
<p>In this case I am unfortunately duplicating data by separating the reads from the writes.  The data lives in the Hibernate session used by the read-only app server in order to load the data in the first place.  It also has to be loaded in the write-only app server in order for that Hibernate session to work with it, which brings me to the point of this post: Is there any way that I can use <a href="http://www.terracotta.org">Terracotta</a> to pool my Hibernate-managed objects into one pool used by both the read-only and write-only app servers?  I think I would like to pool my Hibernate second-level cache.  It seems like <a href="http://terracotta.org/confluence/display/explore/Hibernate">Terracotta can get the job done</a>.</p>
<p>That said, I&#8217;m anxious to explore this topic more when Orion Letizi from Terracotta Tech comes to speak at the <a href="http://www.bostonsug.org">Boston Scalability User Group</a> next Wednesday on May 28th.  The meeting starts at 6 p.m. at the IBM Innovation Center in Waltham, MA.  Of course there will be pizza and soda (sponsored by Terracotta - thank you!) so come to the meeting to have some food and explore what Terracotta can do for your project.  All the details, including directions, are on the <a href="http://www.bostonsug.org">Boston Scalability User Group</a> web site.  Remember, there will be pizza!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/java/2008/05/20/53/feed</wfw:commentRss>
		</item>
		<item>
		<title>The Evil Query</title>
		<link>http://blog.anthonychaves.net/java/2008/03/18/the-evil-query</link>
		<comments>http://blog.anthonychaves.net/java/2008/03/18/the-evil-query#comments</comments>
		<pubDate>Tue, 18 Mar 2008 16:21:47 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[database]]></category>

		<category><![CDATA[hibernate]]></category>

		<category><![CDATA[scalability]]></category>

		<category><![CDATA[sql]]></category>

		<category><![CDATA[criteria]]></category>

		<category><![CDATA[mysql]]></category>

		<category><![CDATA[query]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/03/18/the-evil-query</guid>
		<description><![CDATA[Here is the use case: I want to search all of my bookmarks by multiple tags with an &#8220;and&#8221; operation.  That means I want to see bookmarks that are tagged with Java AND JavaEE, not just Java and not just JavaEE.  I want to see bookmarks tagged with C# AND 3.5 not just [...]]]></description>
			<content:encoded><![CDATA[<p>Here is the use case: I want to search all of my bookmarks by multiple tags with an &#8220;and&#8221; operation.  That means I want to see bookmarks that are tagged with Java AND JavaEE, not just Java and not just JavaEE.  I want to see bookmarks tagged with C# AND 3.5 not just C# and not just 3.5.  For most of the project I&#8217;m using Hibernate and the Criteria API with a lot of success.  In this case I was not able to massage Hibernate into creating the correct query and that&#8217;s fine.  Hibernate has its uses and it is acceptable to write custom SQL when the need arises.  I wrote a method to build up the query and execute it.  Here is part of the method that builds up the query:</p>
<table bgcolor="#ffffff" border="0" cellpadding="3" cellspacing="0">
<tr>
<td align="left" nowrap="nowrap" valign="top"><code><font color="#000000">StringBuilder sqlString =<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong>new </strong></font><font color="#000000">StringBuilder</font><font color="#000000">(<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff">                                &#8220;select distinct(b.id)<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff">from bookmarks b &#8220;</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">counter = </font><font color="#990000">0</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>for</strong></font><font color="#000000">(</font><font color="#000000">Tag tag : tags</font><font color="#000000">) {</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">String alias = </font><font color="#2a00ff">&#8220;j&#8221; </font><font color="#000000">+ Integer.toString</font><font color="#000000">(</font><font color="#000000">counter</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">String joinFragment = </font><font color="#2a00ff">&#8221; inner join bookmarks_to_tags {alias}<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff"> on b.id = {alias}.bookmark_id<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff"> and {alias}.tag_id = ? &#8220;</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">joinFragment = joinFragment.replace</font><font color="#000000">(</font><font color="#2a00ff">&#8220;{alias}&#8221;</font><font color="#000000">, alias</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">sqlString.append</font><font color="#000000">(</font><font color="#000000">joinFragment</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">counter++;</font><br />
<font color="#ffffff">    </font><font color="#000000">}</font></code></td>
</tr>
</table>
<p>Right away it looks like this query will get very expensive for large tag sets.  For smaller sets the query performs in a reasonable amount of time but more than 12 tags makes the query take quite a while.  This is exacerbated by the fact that this query will be executed multiple times if there is a sufficiently large result set (default is over 25) to give the user a paginated view.  This totally flies in the face of being nice to the database.  Hammering it with a complex query over and over won&#8217;t do the users any favors.</p>
<p>What are my options here?  I can work on making this query less complex (something that I will certainly do), but there is still the problem that, no matter how nice this query becomes, the query will be executed over and over as a user pages through the results.  The complexity of the query and its repeated execution makes the result set a good candidate for caching.  Caching the result set in memory with a key tied to the user or their session will allow the expensive query to be executed once with the bookmarks stored in a cache.  When the user pages through the results the cache will be consulted first where the result set will be stored.  I&#8217;m in the process of selecting a caching package for this project so I will post my selection and integration process soon.</p>
<p>Until then, I&#8217;d be happy to hear any suggestions on making this query a little nicer while preserving its &#8220;andness&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/java/2008/03/18/the-evil-query/feed</wfw:commentRss>
		</item>
		<item>
		<title>Spring Web MVC and the benefits of pagination</title>
		<link>http://blog.anthonychaves.net/java/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination</link>
		<comments>http://blog.anthonychaves.net/java/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination#comments</comments>
		<pubDate>Fri, 14 Mar 2008 08:05:06 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[architecture]]></category>

		<category><![CDATA[hibernate]]></category>

		<category><![CDATA[scalability]]></category>

		<category><![CDATA[spring]]></category>

		<category><![CDATA[criteria]]></category>

		<category><![CDATA[mvc]]></category>

		<category><![CDATA[pagination]]></category>

		<category><![CDATA[sql]]></category>

		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination</guid>
		<description><![CDATA[Lately I&#8217;ve been working on a project that keeps my web bookmarks sync&#8217;d across multiple computers and different web browsers.  The project is implemented in Java using JBoss 4.2.2, MySQL 5.0 and Spring 2.0.  It&#8217;s not a very big project right now, only about 40 class files and some small number of configuration [...]]]></description>
			<content:encoded><![CDATA[<p>Lately I&#8217;ve been working on a project that keeps my web bookmarks sync&#8217;d across multiple computers and different web browsers.  The project is implemented in Java using JBoss 4.2.2, MySQL 5.0 and Spring 2.0.  It&#8217;s not a very big project right now, only about 40 class files and some small number of configuration files, but the amount of data the application deals with can get fairly large.<br />
<span id="more-48"></span><br />
Since this app isn&#8217;t the only way to manage ones bookmarks that everyone has used since the dawn of the web, a feature to import bookmarks from browsers was very high on the to-do list.  So across the set of different browsers I&#8217;ve used on the different computers, and the backed up bookmarks that survived system formats I have around 700 bookmarks in total.  That probably isn&#8217;t a whole lot, but that many spread across thousands of users can add up quickly.</p>
<p>I used jhat to find out that each bookmark object uses 1.5 kilobytes of memory.  It doesn&#8217;t take much to see that a LOT of bookmarks will fit into 8 gigs of memory on a medium sized server.  It would take hundreds of thousands of users to generate enough bookmark data to fill that memory.  At this point it&#8217;s clear the application server box can handle the data load (that isn&#8217;t to say that it can handle the network load, however).</p>
<p>I spent some time coming up with a nice data model and ORM design so accessing the data once it&#8217;s in memory is lightning fast, maybe even faster.  But what about when it&#8217;s on a hard disk, deep in the database?  Dragging my 700 bookmarks out of the database when I only want to view the 25 most frequently visited is a bit overkill, especially when you consider dragging everyone else&#8217;s bookmarks out at the same time.  Even though this is still a small-time application I decided to be nice to the database anyway and implement some kind of paging solution.  Spring Web MVC doesn&#8217;t offer much that I&#8217;m aware of in regard to pagination help so it looks like I&#8217;m on my own.</p>
<p>Pagination is something that must take place in the model with the help of the view.  Right away we&#8217;ve got two things to deal with, though it&#8217;s best to deal with them in isolation.  The first thing I did was get the model right.  I&#8217;m using Hibernate for ORM and the Criteria API makes it easy to set offsets and limits.  That is, I can ask for the 50 next items (limit) starting at row 20 (offset).</p>
<p>The Criteria API is great at getting your data into a workable format without HQL or SQL (not that either of them is a bad thing.  I enjoy working with them).  Let&#8217;s take a look at the method that gets some new bookmarks.</p>
<p class="java" align="left">
<table bgcolor="#ffffff" border="0" cellpadding="3" cellspacing="0">
<tr>
<td align="left" nowrap="nowrap" valign="top"><code><br />
<font color="#ffffff">  </font><font color="#7f0055"><strong>public </strong></font><font color="#000000">List&lt;Bookmark&gt; getNewestBookmarks</font><font color="#000000">(</font><font color="#7f0055"><strong>int </strong></font><font color="#000000">offset, </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">numBookmarks</font><font color="#000000">) {</font><br />
<font color="#ffffff">    </font><font color="#000000">DetachedCriteria c = DetachedCriteria.forClass</font><font color="#000000">(</font><font color="#000000">Bookmark.</font><font color="#7f0055"><strong>class</strong></font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#000000">c.addOrder</font><font color="#000000">(</font><font color="#000000">Order.desc</font><font color="#000000">(</font><font color="#2a00ff">&#8220;createdDate&#8221;</font><font color="#000000">))</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>return </strong></font><font color="#000000">getHibernateTemplate</font><font color="#000000">()</font><font color="#000000">.findByCriteria</font><font color="#000000">(</font><font color="#000000">c, offset, numBookmarks</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">  </font><font color="#000000">}</font></code></td>
</tr>
</table>
<p>So this method gets the model for us, but where do the offset and limit come from?  From the controller, of course.  This method is called from the controller like so:</p>
<p class="java" align="left">
<table bgcolor="#ffffff" border="0" cellpadding="3" cellspacing="0">
<tr>
<td align="left" nowrap="nowrap" valign="top"><code><br />
<font color="#ffffff">  </font><font color="#7f0055"><strong>public </strong></font><font color="#000000">ModelAndView newest</font><font color="#000000">(</font><font color="#000000">HttpServletRequest request, HttpServletResponse response</font><font color="#000000">) {</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">numberOfBookmarks = requestUtils.getSafeValueFromRequest</font><font color="#000000">(</font><font color="#000000">request, </font><font color="#2a00ff">&#8220;numberOfBookmarks&#8221;</font><font color="#000000">, </font><font color="#990000">250</font><font color="#000000">, defaultNumberOfNewest</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">offset = requestUtils.getSafeValueFromRequest</font><font color="#000000">(</font><font color="#000000">request, </font><font color="#2a00ff">&#8220;offset&#8221;</font><font color="#000000">, </font><font color="#990000">300</font><font color="#000000">, </font><font color="#990000">0</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><br />
<font color="#ffffff">    </font><font color="#000000">List bookmarks = bookmarkService.getNewestBookmarks</font><font color="#000000">(</font><font color="#000000">offset, numberOfBookmarks</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><br />
<font color="#ffffff">    </font><font color="#000000">Map model = requestUtils.generateBookmarkModel</font><font color="#000000">(</font><font color="#000000">offset, numberOfBookmarks, </font><font color="#990000">300</font><font color="#000000">, bookmarks</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#000000">model.put</font><font color="#000000">(</font><font color="#2a00ff">&#8220;bookmarkListName&#8221;</font><font color="#000000">, </font><font color="#2a00ff">&#8220;Newest Bookmarks&#8221;</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>return new </strong></font><font color="#000000">ModelAndView</font><font color="#000000">(</font><font color="#2a00ff">&#8220;index&#8221;</font><font color="#000000">, model</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">  </font><font color="#000000">}</font></code></td>
</tr>
</table>
<p>After sanity checking the offset and limit received from the request they are passed on to the getNewestBookmarks method to create and execute a somewhat unsightly query.  At this point, rather than gathering up all 700 of my bookmarks only 25 or so are returned.  What if I one day want to view the top 50 most visited bookmarks though?  Or what if I want to scroll through them one page at a time?  Passing in 0, 25 for the offset and limit of every method call won&#8217;t cut it.  The view needs to tell us how many bookmarks to retrieve and where to start the search.</p>
<p>Let&#8217;s say I&#8217;m viewing the top 25 bookmarks in my collection and I want to view 26-50.  Somehow the view has to tell the controller to tell the model about the  desired offset and limit.   This can be done by passing them in as request parameters.  In the code sample above, the HttpServletRequest object passed in to the controller contains values for offset and limit (numberOfBookmark) variables.  The JSP code below shows how to pass these parameters when a user clicks a link. (Actually I&#8217;m having a hard time getting the JSP to show up right when the code is rendered as HTML.  Just click the link and view the entire file instead.)</p>
<p><a href="http://blog.anthonychaves.net/wp-content/uploads/2008/03/operations.jsp" title="operations">JSP Pagination Operations</a></p>
<p>This page uses the JSTL core tag library to build the links that display the &#8220;previous/next/more/less&#8221; options.  Right after the body tag there is code to create four URLs, one for each action.  Each one starts off with a name and a value.  c:url var=&#8221;viewMoreUrl&#8221; value=&#8221;" creates a page-scoped variable called viewMoreUrl.  This variable will later be used to display the link that allows me to display five more bookmarks when I view the list.  The value is left blank which tells the tag to use the current base URL in creating the new one.  If I&#8217;m on a page that is located at /bookmarks/pages/viewUserBookmarks.html the new URL will start off as /bookmarks/pages/viewUserBookmarks.html.</p>
<p>Adding parameters to a URL is easy.  Using the c:param tag we get c:param name=&#8221;numberOfBookmarks&#8221; value=&#8221;${numberOfBookmarks + 5}&#8221;.  This tag appends a variable called numberOfBookmarks to the base URL described in the previous paragraph.  The value is a JSP expression that uses the numberOfBookmarks value passed in the model and increases it by five to pass back in the request.  Because we want the link only to display five more bookmarks and not change the offset that value will remain the value passed out in the model.  That gives us a URL that looks like /bookmarks/pages/viewUserBookmarks.html?numberOfBookmarks=30&amp;offset=0.</p>
<p>When this URL is accessed the request values for offset and numberOfBookmarks are exposed in the HttpServletRequest object for use by the controller.  When the getNewestBookmarks method is called the offset and limit are 0 and 30 instead of 0 and 25.</p>
<p>Obtaining a new offset value is done like getting a new limit value.  We start off with the same base URL and leave the numberOfBookmarks value unchanged.  We need to change the offset value though. c:param name=&#8221;offset&#8221; value=&#8221;${offset + numberOfBookmarks}&#8221; creates the offset value of the URL.  If we are viewing the newest bookmarks (offset 0)  and we are viewing 30 bookmarks when we click the link we should see bookmarks 31-60.</p>
<p>Here is an interesting thing to consider: what if a user goes to the second page using the default values.  The user will be viewing bookmarks 26-50.  The user then clicks the &#8220;view more&#8221; link three times and is now viewing 40 bookmarks.  The &#8220;previous&#8221; will now request that the offset be -15.  Unfortunately we can&#8217;t have negative bookmarks.  In this case the controller has some sanity checking built in to it.  The input parameter values must always be validated before they are used.  The controller uses a utility method to get a safe value for the offset and limit and ensures that neither can be a negative value.  This safe value is then passed back to the view once the model has been built.  Even though the offset in the request says -15 the controller will only go as low as 0.  More interesting work takes place in the generateBookmarksModel method, which I will talk about more next week.</p>
<p>What did I learn from making this effort?  Well, giving 8GB of RAM to an application server is a little overkill for a domain model like this.  The pagination that I came up with is still rudimentary, but a little more work could add more features, namely displaying page number links and the ability to change the number of bookmarks added at a time.</p>
<p>What is gained from this effort?  Being nice to the database opens up a few doors.  It dramatically improves scalability because it allows these requests to be stateless.  If an app server queried the database for all of my bookmarks then my session would have to be directed to that app server every time I made a request in order to not incur the cost of hitting the database again by another app server.  If round-robin load balancing is used the Hibernate session on each app server would contain a copy of my data and that&#8217;s a waste of resources, most notably it&#8217;s a hit to the database that didn&#8217;t have to happen.</p>
<p>Reducing the load on the database by getting only what we need and stateless sessions gives me the option of scaling the database with less difficulty.  A mostly-read application can get by with fewer hits to the write master and do a majority of their work from read-only slaves.   Spreading complex queries over multiple read slaves is much preferable to every app server hitting one database for every query.  By having stateless requests I can have more app servers (using less memory) hitting more database servers without maxing out the CPU utilization due to the cost of complex queries.</p>
<p>What should I do with the unused memory in the app server boxes?  Rather than give it to one app server instance for use as a cache it should be given to the pool of application servers for use as a cache.  One way to do that is with memcached.  I&#8217;ll talk about that more in the future too.</p>
<p>As always please leave comments.  I enjoy getting feedback on what I&#8217;ve written.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/java/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination/feed</wfw:commentRss>
		</item>
		<item>
		<title>Spring Web MVC lists</title>
		<link>http://blog.anthonychaves.net/java/2008/02/13/spring-web-mvc-lists</link>
		<comments>http://blog.anthonychaves.net/java/2008/02/13/spring-web-mvc-lists#comments</comments>
		<pubDate>Wed, 13 Feb 2008 17:42:49 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[spring]]></category>

		<category><![CDATA[coupling]]></category>

		<category><![CDATA[development]]></category>

		<category><![CDATA[frameworks]]></category>

		<category><![CDATA[lists]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[webapps]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/02/13/spring-web-mvc-lists</guid>
		<description><![CDATA[I recently came across a blog post describing how *not* to use the Spring Web MVC form tag library.  Spring Web MVC is one of those things that has a high learning curve but it pays off big once you get the hang of it.  The form tag library is no exception.  [...]]]></description>
			<content:encoded><![CDATA[<p>I recently came across a blog post describing how *not* to use the Spring Web MVC form tag library.  Spring Web MVC is one of those things that has a high learning curve but it pays off big once you get the hang of it.  The form tag library is no exception.  The original blog poster seems to want to display a list of things the form user can manipulate and then send the changes back to the app server.  There are a few different ways we can do this with Spring Web MVC and some JSTL libraries so let&#8217;s take a look at the way I prefer to do it.<span id="more-47"></span></p>
<p>Spring Web MVC utilizes the ModelAndView class to encapsulate the M and V of MVC.  The model part is simply a map of objects associated with a request.  The key is the string name that you will use to access the object via the tag libraries.  Placing an object with a key of &#8220;userPrefs&#8221; into the map will make the UserPrefs object available to the HttpServletRequest and eventually part of the implicit request object that is part of JSPs.  In order to display any user data in a JSP it has to be part of the request object and it gets there by placing it in the Map used by ModelAndView.</p>
<p>In my experience with Spring Web MVC I&#8217;ve found that most form controller classes extend SimpleFormController.  SimpleFormController derives most of its lifecycle from AbstractFormController, which provides us with a few interesting methods.  There is one method in particular that is interesting.  The referenceData(HttpServletRequest request) method returns a Map and this method is called during the showForm(&#8230;) method.  showForm returns a ModelAndView object which will include whatever data is included in the Map returned by referenceData.  The default implementation of referenceData returns null, so we have to override it in our class that extends SimpleFormController.</p>
<p>Here is some example code from my controller class which extends SimpleFormController.  The point here is to provide a list of Bookmark objects to the form for a user to delete.</p>
<p><code><br />
<font color="#ffffff">  </font><font color="#646464">@Override</font><br />
<font color="#ffffff">  </font><font color="#7f0055"><strong>protected </strong></font><font color="#000000">Map referenceData</font><font color="#000000">(</font><font color="#000000">HttpServletRequest request</font><font color="#000000">) </font><font color="#7f0055"><strong>throws </strong></font><font color="#000000">Exception </font><font color="#000000">{</font><br />
<font color="#ffffff">    </font></code><code><font color="#000000"> </font></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">List userBookmarkList = </font></code><code><font color="#000000"> </font></code><code><font color="#000000"> </font></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">bookmarkService.getUserBookmarks</font><font color="#000000">(<br />
</font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">request.getUserPrincipal</font><font color="#000000">()</font><font color="#000000">.toString</font><font color="#000000">())</font><font color="#000000">;<br />
</font></code><code></code><code></code><code></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000"><br />
</font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">Map model = </font><font color="#7f0055"><strong>new </strong></font><font color="#000000">HashMap</font><font color="#000000">()</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font></code><code><font color="#000000"> </font></code><code><font color="#000000"> </font></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">model.put</font><font color="#000000">(</font><font color="#2a00ff">&#8220;userBookmarks&#8221;</font><font color="#000000">, userBookmarkList</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font></code><code><font color="#000000"> </font></code><code><font color="#000000"> </font></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#7f0055"><strong>return </strong></font><font color="#000000">model;</font><br />
<font color="#ffffff">  </font><font color="#000000">}</font></code></p>
<p>This is pretty simple example.  It&#8217;s first getting the user from the request object.  This is an authenticated session where a user has provided a login name and password.  That name is retrieved from the request object and used to get a List of all of their bookmarks.  The List is then put into a HashMap with the key &#8220;userBookmarks&#8221;.</p>
<p>After referenceData returns the Map those entries are then passed on to the view by the showForm method.  We can use a couple JSTL tag libraries to iterate over the list and display each bookmark while building the form.  The following is an excerpt from the view (NOTE – This is pre-Spring 2.5):</p>
<table border="0">
<tr>
<td nowrap="true"><code> <font color="#000000">&lt;%</font><font color="#646464">@ </font><font color="#000000">taglib prefix=</font><font color="#2a00ff">&#8220;form&#8221; </font><font color="#000000">uri=</font><font color="#2a00ff">&#8220;http://www.springframework.org/tags/form&#8221; </font><font color="#000000">%&gt;</font><br />
<font color="#000000">&lt;%</font><font color="#646464">@ </font><font color="#000000">taglib prefix=</font><font color="#2a00ff">&#8220;c&#8221; </font><font color="#000000">uri=</font><font color="#2a00ff">&#8220;http://java.sun.com/jsp/jstl/core&#8221; </font><font color="#000000">%&gt;</font><br />
<font color="#000000">&#8230;</font><br />
<font color="#000000">&lt;form:form&gt;</font><br />
<font color="#ffffff">  </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">&lt;c:forEach items=</font><font color="#2a00ff">&#8220;${userBookmarks}&#8221; </font><font color="#000000">var=</font><font color="#2a00ff">&#8220;bookmark&#8221;</font><font color="#000000">&gt;</font><br />
<font color="#ffffff">    </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">&lt;form:checkbox path=</font><font color="#2a00ff">&#8220;bookmarkIds&#8221; </font><font color="#000000">value=</font><font color="#2a00ff">&#8220;${bookmark.id}&#8221;</font><font color="#000000">/&gt;&lt;c:out value=</font><font color="#2a00ff">&#8220;${bookmark.id}&#8221;</font><font color="#000000">/&gt; &#8212; &lt;c:out value=</font><font color="#2a00ff">&#8220;${bookmark.title}&#8221; </font><font color="#000000">escapeXml=</font><font color="#2a00ff">&#8220;true&#8221;</font><font color="#000000">/&gt;&lt;br/&gt;</font><br />
<font color="#ffffff">  </font></code><code></code><code></code><code></code><code></code><code></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">&lt;/c:forEach&gt;</font><br />
<font color="#ffffff">  </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code><font color="#000000">&lt;input type=</font><font color="#2a00ff">&#8220;submit&#8221; </font><font color="#000000">value=</font><font color="#2a00ff">&#8220;Delete checked bookmarks&#8221;</font><font color="#000000">/&gt;</font><br />
<font color="#000000">&lt;/form:form&gt;</font></code></td>
</tr>
</table>
<p>This view uses two tag libraries, one from JSTL, the other being the Spring form tag library.  Within the form tags the JSTL forEach tag is used to iterate over the list of bookmarks.  The list is obtained via the ${userBookmarks} variable.  This calling convention takes advantage of the search order for finding request parameters used by JSTL.  Part of the search includes looking in the request object that is part of every JSP.  The items attribute is the object we want to iterate over, that being the object keyed by userBookmarks.</p>
<p>That object is the List of bookmarks we got from the business tier.  By iterating over the bookmarks we have access to each Bookmark object and each of it&#8217;s fields.  The checkbox tag is used to build up the HTML view of the each bookmark with a check box next to it for the user to click.  When the user is done selecting bookmarks to delete the onSubmit method is called back in the controller.</p>
<p>So how does Spring know which bookmarks have been checked in the form?  The form object bound to the form keeps track of the checked bookmarks.  This form object is very simple, it encapsulates an array of Strings and provides a getter and setter for the array.</p>
<p><code><br />
<font color="#7f0055"><strong>package </strong></font><font color="#000000">net.anthonychaves.bookmarks.dataobject;</font></code></p>
<p><font color="#7f0055"><strong>public class </strong></font><font color="#000000">DeleteBookmarksForm </font><font color="#000000">{</font><br />
<font color="#ffffff"> </font><br />
<font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#7f0055"><strong>private </strong></font><font color="#000000">String</font><font color="#000000">[] </font><font color="#000000">bookmarkIds;</font></p>
<p><font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code> <font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#7f0055"><strong>public </strong></font><font color="#7f0055"><strong>void </strong></font><font color="#000000">setBookmarkIds</font><font color="#000000">(</font><font color="#000000">String</font><font color="#000000">[] </font><font color="#000000">bookmarkIds</font><font color="#000000">) {</font><br />
<font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code> <font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code> <font color="#ffffff">    </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#7f0055"><strong>this</strong></font><font color="#000000">.bookmarkIds = bookmarkIds;</font><br />
<font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code> <font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#000000">}</font></p>
<p><font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#7f0055"><strong>public </strong></font><font color="#000000">String</font><font color="#000000">[] </font><font color="#000000">getBookmarkIds</font><font color="#000000">() {</font><br />
<font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code> <font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code> <font color="#ffffff">    </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#7f0055"><strong>return </strong></font><font color="#000000">bookmarkIds;</font><br />
<font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code> <font color="#ffffff">  </font><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><code></code><code><font color="#000000"> </font></code><font color="#000000">}</font><br />
<font color="#000000">}</font></p>
<p>The “value” part of our checkbox contains the bookmark ID.  When the checkbox is checked and the form is submitted the Spring dispatcher servlet collects all of the checked values of the same path into an array and puts them into the DeleteBookmarksForm object.  The form object is then passed to the DeleteBookmarksController via the onSubmit method.  We don&#8217;t have to implement the entire onSubmit method, though.  The default implementation calls doSubmitAction which takes only our command object, in this case DeleteBookmarksForm, as an argument.  Once we have a list of bookmark IDs to delete we can call the business tier to remove them from the database (after appropriate error checking, of course).</p>
<p>Spring 2.5 introduces the form:checkboxes tag which eliminates the need to use the JSTL forEach tag to iterate over the list of objects.  The checkboxes tag takes care of that for you.</p>
<p>There is more to working with lists in Spring Web MVC than the material presented here.  I&#8217;ve included a few references below in case you&#8217;re interested in finding out more.  Thanks for reading and as usual let me know if you have any questions or comments!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/java/2008/02/13/spring-web-mvc-lists/feed</wfw:commentRss>
		</item>
		<item>
		<title>Response to Web Application: To Couple or Not to Couple?</title>
		<link>http://blog.anthonychaves.net/java/2008/02/08/response-to-web-application-to-couple-or-not-to-couple</link>
		<comments>http://blog.anthonychaves.net/java/2008/02/08/response-to-web-application-to-couple-or-not-to-couple#comments</comments>
		<pubDate>Fri, 08 Feb 2008 20:33:24 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[coupling]]></category>

		<category><![CDATA[development]]></category>

		<category><![CDATA[frameworks]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[webapps]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/personal/2008/02/08/response-to-web-application-to-couple-or-not-to-couple</guid>
		<description><![CDATA[I just read this thread on The Server Side and it makes me nervous that people still ask this kind of question.  The thread starts off by asking &#8220;An usually annoying question when designing an action-based web application is: &#8220;Should I place everything in my action or should I separate the web logic from [...]]]></description>
			<content:encoded><![CDATA[<p>I just read <a href="http://www.theserverside.com/news/thread.tss?thread_id=48304" target="_blank">this thread on The Server Side</a> and it makes me nervous that people still ask this kind of question.  The thread starts off by asking &#8220;An usually annoying question when designing an action-based web application is: &#8220;Should I place everything in my action or should I separate the web logic from the business logic?&#8221; and the alarm bells in my head are going off already.<span id="more-45"></span></p>
<p>Business logic <strong>always</strong> belongs in its own layer.  Why?  Business logic is <strong>the thing</strong> that makes your application what it is.  It doesn&#8217;t matter how it&#8217;s dressed up - web app, desktop app, web service - the business logic accomplishes the task the user wants to perform.  Business logic <strong>is</strong> your application.  Because of its importance business logic should be placed in a sacred place where it will not be disturbed by presentation logic or infrastructure underpinnings - the business tier.  Separating the business concerns from all other concerns buys a lot of benefits for the development team.</p>
<p>What if aggregate data must be displayed in multiple locations?  For example, shopping cart data could be displayed on a checkout screen and also as a side bar while the user is still shopping.  In an action-based framework this code would be duplicated in two different actions.  It would be present in the action that generated checkout screen and again in the action that assembled the shopping view screen.  Rather than duplicate the code why not make a method call into the business layer that did the same thing?  The end result is the same except it separates the concerns of web and business logic.</p>
<p>Refactoring this code into a business tier method helps make our application more loosely coupled.  Loose coupling is important for business logic because it allows for easier reuse, testability and maintanence.  If the business logic is located in the action then creating a web service around it is going to be more difficult.  The business logic will have to be decouple from the web action and pulled into a layer where both the web action and web service can use it.  A good example of this would be a web page that displays new items available to a customer.  A web page is ok but the customer would have to keep checking back for updates.  An easier way to publish this information would be to make it available as an RSS feed the customer can subscribe to.  Duplicating the logic to get the new items isn&#8217;t particularly difficult, but why make maintenance more difficult than it has to be?  The application should be refactored to have a business layer that is responsible for getting the data which can be called by two different locations in the presentation layer (the web tier and the RSS feed creator).  Testing the business logic (you&#8217;re testing, right?) and bug fixing only have to be done in one location thanks to this refactoring too.</p>
<p>Another quote from the thread is, &#8220;The other side of the story is that to have two separate classes, one dealing with the http/web layer (ex: UserAction) and one dealing with the business logic layer (ex: UserService) is bureaucratic.&#8221;  This statement shows a lack of understanding of functional decomposition.  Each module should be responsible for one and only one thing.  If the web layer is for dealing with actions then actions should only be dealt with in the web layer.  If the web layer is for dealing with actions then business logic should <strong>not</strong> be dealt with at the web layer.  Business logic belongs where it can remain undisturbed by other concerns.  Tight coupling of these concerns makes testing more difficult because it requires testing everything it once rather than testing in isolation.  Because your application is only as good as the business logic the business layer should have a test suite that can be run without depending on action or presentation logic.</p>
<p>There are plenty of other reasons you should keep business and web logic separate.  This is in no way an exhaustive list, but it&#8217;s certainly something to think about.  If you&#8217;re building any professional application then business logic should always be in its own layer.  Not only will your boss/team/successor thank you for it, it&#8217;s good software development and you&#8217;ll thank yourself for it too.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/java/2008/02/08/response-to-web-application-to-couple-or-not-to-couple/feed</wfw:commentRss>
		</item>
		<item>
		<title>Boston Scalability User Group</title>
		<link>http://blog.anthonychaves.net/java/2008/02/04/boston-scalability-user-group</link>
		<comments>http://blog.anthonychaves.net/java/2008/02/04/boston-scalability-user-group#comments</comments>
		<pubDate>Mon, 04 Feb 2008 20:59:16 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[Personal]]></category>

		<category><![CDATA[Rails]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[architecture]]></category>

		<category><![CDATA[career]]></category>

		<category><![CDATA[scalability]]></category>

		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/02/04/boston-scalability-user-group</guid>
		<description><![CDATA[I&#8217;ve been digging around lately for a Boston area user group dedicated to architectural scalability and I haven&#8217;t been able to find one.  Other user groups that I regularly attend have meetings centered around scalability once in a while, but I&#8217;m looking for something with a schedule dedicated to the topic.  It&#8217;s a [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been digging around lately for a Boston area user group dedicated to architectural scalability and I haven&#8217;t been able to find one.  Other user groups that I regularly attend have meetings centered around scalability once in a while, but I&#8217;m looking for something with a schedule dedicated to the topic.  It&#8217;s a hot topic in the industry right now with a lot happening on different fronts and there should be some ongoing professional discussion dedicated what goes into growing an application.</p>
<p>Here are some of the topics I want to talk about:</p>
<ul>
<li>Data growth and access - What kind of DBMS topologies allow maximum scalability without breaking the budget?  What if the budget wasn&#8217;t a problem?  How do you migrate your data model from hundreds of users to millions of users?  Should you partition user data across shards or keep it in a central database?  How do you profile data access paterns?  Is your application mostly-read or mostly-write?  When should you use an LDAP directory for data storage?  When should you use MySQL and when should you use Oracle?</li>
<li>Application server scaling - app server clustering, web server integration, load balancing, application session management, data caching</li>
<li>Web Tier - load balancing reverse proxies, data caching, working with HTTP, considerations for exposing functionality via REST</li>
<li>Language conisderations and platform choices - Dynamic languages vs. Static languages, Linux vs. Windows, Solaris vs. Linux, RedHat vs. Oracle, IBM vs. Oracle - How do you make these choices?  What evaluation critera are most important?  Which ones are misleading?</li>
<li>Framework scalability - How can you scale if your framework can&#8217;t?  Does Rails really scale better than Spring Web MVC?  Where do PHP frameworks fit in?  What are the alternatives?  This is where we will investigate what you gain and lose by binding yourself to a particular framework, how to keep the coupling to a minimum and how to use your framework as a solid foundation instead viewing it as a cage.</li>
<li>Emerging technology - Should you become familiar with Map Reduce and Hadoop?  What kind of impact do object databases have on your application?  Should you buy your own Sun or Dell boxes or use Amazon&#8217;s EC2 and S3?</li>
<li>Application architecture - How do you write an application that scales?  What does your application look like as it grows from servicing hundreds to millions of users?  How does it handle session management?  How does it access datastores?  Are there any design patterns that are helpful?  What are the anti-patterns to be aware of?</li>
</ul>
<p>Like I said, I haven&#8217;t found a group dedicated to discussing these topics.  If you know of one in the Boston area please let me know.  Assuming there isn&#8217;t one <strong>I am willing to start one</strong>.  I&#8217;d like to start off small and meet at coffee shops around Burlington or Lowell.  I have no delusions that this is going to start off or even become as big as NEJUG is now.  If it starts off as a few people getting together to talk about scalability trends, cool caching solutions and specific products then I&#8217;d call it a good start.</p>
<p>The meeting location is still TBD and will be based on how many people are interested in attending.  It will probably be somewhere in Burlington, MA.  There is no planned presentation at this time.  Instead we will have a meet and greet and then discuss scalability trends and news, what approaches to scalability are commonly used now and what are the plans for the future.</p>
<p>If you are interested in coming or in finding out more information please email me at &lt;my first name&gt;@&lt;my domain name&gt;.&lt;my tld&gt; or leave a comment below.  Please make sure to include your email address when you fill out the form so that I can get in touch with you - your email address will not be displayed on my site.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/java/2008/02/04/boston-scalability-user-group/feed</wfw:commentRss>
		</item>
		<item>
		<title>Too Many Bugs - Conclusion</title>
		<link>http://blog.anthonychaves.net/work/2008/01/30/too-many-bugs-conclusion</link>
		<comments>http://blog.anthonychaves.net/work/2008/01/30/too-many-bugs-conclusion#comments</comments>
		<pubDate>Wed, 30 Jan 2008 17:20:55 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[career]]></category>

		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/work/2008/01/30/too-many-bugs-conclusion</guid>
		<description><![CDATA[This is the final part (for now) of the thoughts I&#8217;ve collected on improving the relationship between software developer and QE to find high-risk bugs sooner.  You might want to check out parts one, two, three and four before continuing here.
To some people it might seem that a lot of what I&#8217;ve written about [...]]]></description>
			<content:encoded><![CDATA[<p>This is the final part (for now) of the thoughts I&#8217;ve collected on improving the relationship between software developer and QE to find high-risk bugs sooner.  You might want to check out parts <a href="http://blog.anthonychaves.net/work/2008/01/23/too-many-bugs">one,</a> <a href="http://blog.anthonychaves.net/work/2008/01/24/too-many-bugs-part-2">two,</a> <a href="http://blog.anthonychaves.net/work/2008/01/28/too-many-bugs-part-3">three</a> and <a href="http://blog.anthonychaves.net/work/2008/01/29/too-many-bugs-part-4">four</a> before continuing here.</p>
<p>To some people it might seem that a lot of what I&#8217;ve written about here is common sense.  The condensed version of this text might be &#8220;go talk to QE&#8221;.  But surprisingly there are exceedingly few people that actually do it.  <span id="more-43"></span>QE, or any other group for that matter, is a mysterious team with bad intentions that lives on the other side of the fence we through our software over.  Working with QE over Bugzilla and email is like a 2400 bps modem.  You just don&#8217;t get what you need over it.  Software developers are typically even more prone to this reclusive behavior due to the kind of people attracted to the field.  I struggled with the same social problems for years, usually with less than satisfactory results.  Eventually I found a way to work with others that works well for everyone.  I had to struggle against my strongly introspective personality and force myself to communicate verbally, in person, with people around me and affected by my work.  Why?  Because it didn&#8217;t seem like anyone else was doing it and there had to be a better way to deliver high quality software that people wanted to use.</p>
<p>By paying attention to these things the quality of my technical work improved and my visibility to people both on and outside of my teams skyrocketed.  I&#8217;ve made an effort to improve how I work with others and in doing so I&#8217;ve made a name for myself.  Possessing superior technical skill is of little use if no one recognizes it.  Think about that for a minute.  You can be the greatest programmer of your generation and you&#8217;re not going anywhere if you can&#8217;t convince others you&#8217;re worthwhile.</p>
<p>Oh yeah, what effect does this have on the bug triage meeting?  If you&#8217;ve read this far then you know you&#8217;re showing up loaded with ammo.  Every bug you talk about is going to be important because you&#8217;ve eliminated all the small ones along the way.  Imagine being in a room with your peers and a cross functional team where every word you say is inherently more valuable because you&#8217;ve done your homework.  You&#8217;ve put in the effort to make this meeting more valuable for everyone else that attends.  You&#8217;re not wasting their time by even looking at bug 34987 that only had a minor impact on documentation.  You&#8217;re in front of an audience that wants to discuss things that matter, business or technical.</p>
<p>No one wants to get up in the morning and think, &#8220;I want to sit in a bug triage session all afternoon today and again tomorrow&#8221;.  In this meeting you&#8217;re able to talk about every bug as it comes up.  You know the issue, you know the root cause and you know what kind of scope it takes to fix the problem.  That&#8217;s the kind of information that&#8217;s needed at bug triage sessions.  That information helps to determine the severity and priority.  It let&#8217;s people decide whether or not the bug needs to be fixed right now and that is exactly the point of bug triage meetings.  People will notice that you&#8217;ve done your homework and they will appreciate it.  It will get you noticed.  People will realize you have in-depth technical skill and you can work well with others which almost guarantees a position as a successful technical lead and it will open up a lot of other doors in your career.  What, you just wanted to make the bug triage meeting shorter?</p>
<p>Well, like software this paper isn&#8217;t perfect.  I&#8217;m certain there are missing topics, incorrect statements and gaping holes in logic and arguments.  There are entire books written on this subject and I don&#8217;t have any delusion that I&#8217;ve covered even one one-hundredth of 1% of the social issues that hold our industry back from consistently releasing high quality software, on time, on budget.  But, like I do for my software, I keep a list of known risks for this paper and I&#8217;ve decided that it&#8217;s finally time to ship it, bugs and all.  Congratulations on reading the entire thing and bigger congratulations for just skipping to the end if that&#8217;s how you got here.  If you&#8217;ve found this to be worthwhile please submit it to Digg, Slashdot, StumbleUpon, del.icio.us or whatever other social network you use.  Also please leave your comments and discuss what I&#8217;ve written.  Feel free to add topics you think would be interesting.</p>
<p><script type="text/javascript"> digg_url = 'http://blog.anthonychaves.net/work/2008/01/28/too-many-bugs-conclusion'; </script><br />
<script src="http://digg.com/tools/diggthis.js" type="text/javascript"></script></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/work/2008/01/30/too-many-bugs-conclusion/feed</wfw:commentRss>
		</item>
		<item>
		<title>Too Many Bugs - Part 4</title>
		<link>http://blog.anthonychaves.net/work/2008/01/29/too-many-bugs-part-4</link>
		<comments>http://blog.anthonychaves.net/work/2008/01/29/too-many-bugs-part-4#comments</comments>
		<pubDate>Tue, 29 Jan 2008 16:54:49 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
		
		<category><![CDATA[career]]></category>

		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/work/2008/01/29/too-many-bugs-part-4</guid>
		<description><![CDATA[Today brings us to part four in the series of thoughts I&#8217;ve put together on reducing bug triage brutality by creating a more productive relationship with your QE team.  You might want to check out part one, part two and part three before reading on.
There&#8217;s the old saying &#8220;when you&#8217;re in a hole stop [...]]]></description>
			<content:encoded><![CDATA[<p>Today brings us to part four in the series of thoughts I&#8217;ve put together on reducing bug triage brutality by creating a more productive relationship with your QE team.  You might want to check out <a href="http://blog.anthonychaves.net/work/2008/01/23/too-many-bugs">part one</a>, <a href="http://blog.anthonychaves.net/work/2008/01/24/too-many-bugs-part-2">part two</a> and <a href="http://blog.anthonychaves.net/work/2008/01/28/too-many-bugs-part-3">part three</a> before reading on.</p>
<p>There&#8217;s the old saying &#8220;when you&#8217;re in a hole stop digging&#8221;.  So far we&#8217;ve talked about how to stop digging but we haven&#8217;t talked about how to get out of the hole yet.  A bug list is a bit like Pandora&#8217;s box in that once the bugs are open they&#8217;re like all the sorrows let out of the box.  <span id="more-42"></span>You just can&#8217;t put the sorrows back in the box and you just can&#8217;t get rid of all the bugs.  What happens to you when you&#8217;ve got a bug list a mile long?  You try to avoid looking at it for one.  But there are always people in management that subscribe to the mange-by-numbers theory who take bug metrics a little too seriously.  Eventually you&#8217;re going to make it to the top of the naughty list when they&#8217;re looking at who has the most open bugs.  This can be interpreted in a few ways and none of them are good.  They could think you&#8217;re a terrible developer if you write so many bugs.  They could think you&#8217;re lazy if you&#8217;ve got such a huge backlog.  They could think you can&#8217;t prioritize well if they see something related to their pet topic is on your bug list and you&#8217;re working on something different.  They could think any number of different things and whatever they come up with definitely will not benefit you one bit.  You need to massage your bug list to stay off the slacker radar.</p>
<p>Start by picking off some bugs that are related somehow.  They can be related on feature, topology specificity, platform specificity.  Pick a few that you don&#8217;t understand well.  Pick a few that you think are configuration error, user error, documentation error.  Make up a list with these bugs and a summary of each one or print out each bug report.  I hope you can guess what&#8217;s coming next.  That&#8217;s right - go get your QE person or people and ask when they will have time to sit down with you to address your concerns.  What you&#8217;re doing right here is proving to QE that they are valuable and their work is not in vain.  You&#8217;re validating their presence in the organization by asking them to talk about their work.  They need someone to listen to what they&#8217;re saying and it&#8217;s your job as a developer to do it.  You&#8217;re making them feel good by making them feel important.  This is a great way to start off a meeting or meeting request.</p>
<p>Let them know what bugs you want to discuss before the meeting starts to give them time to prepare too.  This meeting is going to go very smoothly if everyone comes prepared.  Your goal here is to find out enough information to resolve all of the bugs on your list you prepared.  Every one of them must have some resolution before you leave this meeting.  This is not a bug triage meeting because you know going into it that all the bugs must be resolved.  This is a technical meeting that results in action items from all parties.</p>
<p>Start the meeting by saying &#8220;I understand you have some concerns about&#8230; &lt;fill in the software module blank&gt;.  I was wondering if you could give me some insight into what you&#8217;re looking for from me&#8221;.  Begin the discussion on each bug by asking them how they encountered it even if it&#8217;s already logged in the bug report.  Ask questions about their process.  A lot of extra information that wasn&#8217;t captured in Bugzilla will come out of this conversation.</p>
<p>Sometimes enough information will come out that you will all see that the bug is clearly due to incorrect expectations or incomplete documentation.  These are easy bugs to resolve.  Sometimes you&#8217;ll get that a bug is clearly a bug and through your conversation you will find out exactly what you need to do to fix it.  These are also pretty easy to deal with.  Another kind of bug that can end up here is one that turns out to be an enhancement request.  I&#8217;ll talk in-depth about how to deal with enhancement requests another time, but if they come up in the meeting it&#8217;s best to just move on to the next bug.  If you don&#8217;t you&#8217;ll end up at a mini-triage meeting trying to decide the priority, whether or not it&#8217;s an enhancement request and so on.</p>
<p>At the end of the meeting there should be a list of developer action items for you to take care of and a list of QE action items for them to take care of.  Have these meetings as often as you can in order to drive down your list of bugs.  Remember you&#8217;re trying to show up at the bug triage meeting with the shortest bug list possible and this will drive down your list very quickly.</p>
<p>I can derive another relevant example from the first iteration I wrote about in <a href="http://blog.anthonychaves.net/work/2008/01/24/too-many-bugs-part-2">part two</a>.  Remember that QE had opened a huge number of bugs in a very short time period on my deliverable.  I was overwhelmed by the incoming load and I couldn&#8217;t just mark all the bugs &#8220;won&#8217;t fix&#8221;.  If QE wasn&#8217;t riled up already that would be the thing to do it.  Most of the bugs came from two people in QE so I had to get them into a room with me, if not to talk to them then just to keep them away from their keyboards so they couldn&#8217;t open any more bugs for me.</p>
<p>I went in to the meeting with a print out of every bug I wanted to discuss.  Each one was related to the feature that I had just delivered.  I started the meeting by saying, &#8220;John, I understand you have some concerns about this feature.  It seems like there are some things that are preventing you from testing further into the feature and I would like to know what I can do to help you get further along&#8221;.  Notice how I didn&#8217;t say anything about &#8220;Please stop opening bugs or I&#8217;m going to quit my job&#8221; or &#8220;I hate you QE guys so much that I&#8217;ve written a thousand more bugs that you&#8217;ll never discover&#8221; or even something nicer like &#8220;There are an awful lot of bugs against this feature and I&#8217;m not sure some of them are valid&#8221;.  Everything I said was phrased in a way that conveys my want to help QE.  And I really did want to help them.  Helping them helps me.  There is no rule that says developers can&#8217;t help QE and if there is a rule that says that it&#8217;s wrong.  Dev./QE is a two way street.</p>
<p>After that we went through each bug.  We discovered that some of them were duplicates of each other.  That gave QE the action item to close each duplicate bug.  Some of the bugs were discovered to be due documentation that wasn&#8217;t explicit or detailed enough.  Those bugs came into my action item list to fix the documentation.  I asked the QE guys what documentation details would have helped them navigate through the steps safely so that I could use it in my resolution.</p>
<p>Some of the bugs were because the user tried to set up the software on an unsupported platform.  You can&#8217;t use SuSE 7 and Perl 4 when the supported platform is Red Hat ES 4 and Perl 5.  Those bugs become action items for QE; retest those bugs on supported platforms.</p>
<p>The most interesting bugs were real bugs caused by unexpected QE needs, but needs vital to their testing.  Getting a firm grasp of what is causing these bugs is important and hopefully you can gain enough information that you can propose a fix in a relatively short period of time.  Those bugs became action items for me to resolve.</p>
<p>In creating these action items it&#8217;s also important to provide a time table as to when QE can expect to see the resolution.  Talking about the bug is one thing, but the goal is to drive down the bug list and the only way to do that is to actually fix the bugs.  For my action items I told the QE guys that they could expect to see a fix in the next patch release they get from development and I made sure to deliver.  Setting joint expectations and then driving toward them is a recurring pattern here and your consistency in doing so is important to your success.</p>
<p>Come back again tomorrow for the conclusion!</p>
<p><script type="text/javascript"> digg_url = \\\'http://blog.anthonychaves.net/work/2008/01/28/too-many-bugs-part-4\\\'; </script><br />
<script src="http://digg.com/tools/diggthis.js" type="text/javascript"></script></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/work/2008/01/29/too-many-bugs-part-4/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
