<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>BarneyBlog</title>
	
	<link>http://www.barneyb.com/barneyblog</link>
	<description>Thoughts, rants, and even some code from the mind of Barney Boisvert.</description>
	<lastBuildDate>Wed, 10 Mar 2010 20:27:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/barneyblog" /><feedburner:info uri="barneyblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>REReplaceCallback UDF</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/ccKrkU50DUQ/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/03/10/rereplacecallback-udf/#comments</comments>
		<pubDate>Wed, 10 Mar 2010 08:35:35 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[cfml]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1333</guid>
		<description><![CDATA[If you've used pretty much any modern language, you know all about callback functions.  Unfortunately CFML is capable of doing it, but the language itself doesn't leverage the feature anywhere.  In particular, a callback for the replace operation is of great value.  Ben Nadel has blogged about such things a couple times, and now I'm [...]]]></description>
			<content:encoded><![CDATA[<p>If you've used pretty much any modern language, you know all about callback functions.  Unfortunately CFML is capable of doing it, but the language itself doesn't leverage the feature anywhere.  In particular, a callback for the replace operation is of great value.  Ben Nadel has blogged about such things a <a href="http://www.bennadel.com/blog/191-REReplace-Java-Function-Pointers-Freakin-Sexy-.htm">couple</a> <a href="http://www.bennadel.com/blog/1479-REReplace-ColdFusion-Custom-Tag-Another-Regular-Expression-Experiment.htm">times</a>, and now I'm doing the same.  First, here's how you use it:</p>
<pre>&lt;cfscript&gt;
string = "The catapult bifurcated the bearcat.";
fancyString = REReplaceCallback(string, "(\w*)(cat)(\w*)", doit, "all");
function doit(match) {
  if (match[2] EQ "") {
    return '#match[2]#&lt;b&gt;&lt;i&gt;#match[3]#&lt;/i&gt;&lt;/b&gt;#match[4]#';
  } else {
    return '&lt;u&gt;#match[2]#&lt;b&gt;&lt;i&gt;#match[3]#&lt;/i&gt;&lt;/b&gt;#match[4]#&lt;/u&gt;';
  }
}
&lt;/cfscript&gt;</pre>
<p>As you'd imagine, the 'doit' function is invoked for each match of the regular expression (in this case looking for a literal "cat" surrounded by any number of word characters).  It then does a check on match[2] (the leading word characters) to see if it's empty and then forks based on that result (either underlining or not).  The 'match' array, as you might surmise, contains the matched expressions.  The first index is the entire expression, and an additional index is added for each subexpression in the regular expression.  In this case, there are three subexpressions, so the 'match' array will have length 3 + 1 = 4.</p>
<p>This particular conditional can be performed without a callback.  Here are a pair of REReplace calls that do it:</p>
<pre>&lt;cfscript&gt;
string = "The catapult bifurcated the bearcat.";
fancyString = REReplace(string, "(\W|^)(cat)", "\1&lt;b&gt;&lt;i&gt;\2&lt;/i&gt;&lt;/b&gt;", "all");
fancyString = REReplace(fancyString, "(\w+)(cat)\w*", "&lt;u&gt;\1&lt;b&gt;&lt;i&gt;\2&lt;/i&gt;&lt;/b&gt;\3&lt;/u&gt;", "all");
&lt;/cfscript&gt;
</pre>
<p>The first one takes care of words starting with 'cat', the second words with 'cat' inside or at the end.  Note that this only works because the result of the first replace does NOT put word characters next to 'cat' in the replacement string.  If it did that, we'd be screwed, because the two replaces happen sequentially, not in parallel.</p>
<p>In this particular case, neither one of them is very readable.  :)  With a little cleanup and a well-named temp variable, I'd say the callback version has the potential to be more readable, but the pair of REReplaces is pretty much stuck as-is.  As things get more complicated, however, the callback approach becomes dramatically clearer.</p>
<p>The big win, of course, has nothing to do with conditional replaces.  Rather, it's the ability to execute arbitrary CFML code to generate the replace string <em>based on the matched string</em>.  Your callback can do anything you want: go hit the database, shell out to a web service, go grab a dynamically selected bean from ColdSpring and get a value from it, etc.  The sky's the limit.</p>
<p>Here's the REReplaceCallback UDF itself:</p>
<pre>&lt;cffunction name="REReplaceCallback" access="private" output="false" returntype="string"&gt;
  &lt;cfargument name="string" type="string" required="true" /&gt;
  &lt;cfargument name="pattern" type="string" required="true" /&gt;
  &lt;cfargument name="callback" type="any" required="true" /&gt;
  &lt;cfargument name="scope" type="string" default="one" /&gt;
  &lt;cfscript&gt;
  var start = 0;
  var match = "";
  var parts = "";
  var replace = "";
  var i = "";
  var l = "";
  while (true) {
    match = REFind(pattern, string, start, true);
    if (match.pos[1] EQ 0) {
      break;
    }
    parts = [];
    l = arrayLen(match.pos);
    for (i = 1; i LTE l; i++) {
      if (match.pos[i] EQ 0) {
        arrayAppend(parts, "");
      } else {
        arrayAppend(parts, mid(string, match.pos[i], match.len[i]));
      }
    }
    replace = callback(parts);
    start = start + len(replace);
    string = mid(string, 1, match.pos[1] - 1) &amp; replace &amp; removeChars(string, 1, match.pos[1] + match.len[1] - 1);
    if (scope EQ "one") {
      break;
    }
  }
  return string;
  &lt;/cfscript&gt;
&lt;/cffunction&gt;
</pre>
<p>Lots of stuff going on in there, but it's basically just doing a REFind with returnsubexpressions=true, ripping apart the string to pass the pieces to the callback function, and then reassembling the string afterwards.  It'd be trivially easy to make a REReplaceNoCaseCallback function, but I haven't done.  I've implemented the function with CFFUNCTION/CFARGUMENT tags so that I can have an optional fourth parameter on CF8, but the body as CFSCRIPT so that if you want to use the UDF in pure CFSCRIPT on CF9, you only have to rewrap the body (not reimplement).</p>
<p>This particular implementation differs from what you might expect in that the callback gets substrings instead of position/length tuples (i.e., the way REFind works).  I opted for this approach for two reasons: first it removes the need for the callback to have access to the raw string, and secondly all you do with the len/pos is rip the string apart to get the characters so why make every callback do it.</p>
<p>Why did I write this?  Just for fun?  No, not at all.  I needed a way of doing rich inline markup with tags that could be implemented via plugging for a project (you get one guess), and after playing with a couple formats I concluded that porting WordPress's shortcodes was as close to an optimal solution as I was going to get.  The shortcode implementation requires this sort of conditional replace operations, so I built this UDF.  If you do PHP, it's basically equivalent to preg_replace_callback but with CFML argument ordering.</p>
<p>Yes, I'll be sharing the CFC that implements shortcodes (complete with a port of the WordPress unit tests from PHPUnit to MXUnit), but not right this second.</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 261px; width: 1px; height: 1px;">&lt;cffunction name="REReplaceCallback" output="false" returntype="string"&gt;<br />
&lt;cfargument name="string" type="string" required="true" /&gt;<br />
&lt;cfargument name="pattern" type="string" required="true" /&gt;<br />
&lt;cfargument name="callback" type="any" required="true" /&gt;<br />
&lt;cfargument name="scope" type="string" default="one" /&gt;<br />
&lt;cfset var start = 0 /&gt;<br />
&lt;cfset var match = "" /&gt;<br />
&lt;cfset var parts = "" /&gt;<br />
&lt;cfset var replace = "" /&gt;<br />
&lt;cfset var i = "" /&gt;<br />
&lt;cfloop condition="true"&gt;<br />
&lt;cfset match = REFind(pattern, string, start, true) /&gt;<br />
&lt;cfif match.pos[1] EQ 0&gt;<br />
&lt;cfbreak /&gt;<br />
&lt;/cfif&gt;<br />
&lt;cfset parts = [] /&gt;<br />
&lt;cfloop from="1&#8243; to="#arrayLen(match.pos)#" index="i"&gt;<br />
&lt;cfif match.pos[i] EQ 0&gt;<br />
&lt;cfset arrayAppend(parts, "") /&gt;<br />
&lt;cfelse&gt;<br />
&lt;cfset arrayAppend(parts, mid(string, match.pos[i], match.len[i])) /&gt;<br />
&lt;/cfif&gt;<br />
&lt;/cfloop&gt;<br />
&lt;cfset replace = callback(parts) /&gt;<br />
&lt;cfset start = start + len(replace) /&gt;<br />
&lt;cfset string = mid(string, 1, match.pos[1] &#8211; 1) &amp; replace &amp; removeChars(string, 1, match.pos[1] + match.len[1] &#8211; 1) /&gt;<br />
&lt;/cfloop&gt;<br />
&lt;cfreturn string /&gt;<br />
&lt;/cffunction&gt;</div>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/ccKrkU50DUQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/03/10/rereplacecallback-udf/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/03/10/rereplacecallback-udf/</feedburner:origLink></item>
		<item>
		<title>Sudoku PointingPairStrategy</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/7YIcl9QJOBc/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/03/05/sudoku-pointing-pair-strategy/#comments</comments>
		<pubDate>Sat, 06 Mar 2010 06:51:46 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[groovy]]></category>
		<category><![CDATA[sudoku]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1330</guid>
		<description><![CDATA[The next sudoku strategy is called a "pointing pair" which I'm going to start by generalizing into "pointing triple".  The strategy is pretty straightforward: if, for a given number in a given block, all the potential cells are in the same row or column, then that number cannot exist in any other block's cells of [...]]]></description>
			<content:encoded><![CDATA[<p>The next sudoku strategy is called a "pointing pair" which I'm going to start by generalizing into "pointing triple".  The strategy is pretty straightforward: if, for a given number in a given block, all the potential cells are in the same row or column, then that number cannot exist in any other block's cells of the same row or column.</p>
<p>A pointing pair is easier to see than a pointing triple, but necessitates making the definition slightly tighter: if a block contains only two potential cells for a given number and they're in the same row or column, then that number cannot exist in any other block's cells of the same row or column.</p>
<p>Of course, if you crank it down one more step (to a "pointing single"), you have the definition of a known cell (either a given or one already solved for).  But enough prose, on to the code:</p>
<pre>boolean play(Board board) {
  def madePlay = false
  board.blocks.each { b -&gt;
    (1..9).each { n -&gt;
      def cells = b.findAll {
        it.hasMark(n)
      }
      if (cells.size() &gt;= 2) {
        if (cells*.col.unique().size() == 1) {
          // all in one col
          cells[0].col.each {
            if (it.block != b) { // different block
              madePlay = it.removeMark(n, this) || madePlay
            }
          }
        }
        if (cells*.row.unique().size() == 1) {
          // all in one row
          cells[0].row.each {
            if (it.block != b) { // different block
              madePlay = it.removeMark(n, this) || madePlay
            }
          }
        }
      }
    }
  }
  madePlay
}
</pre>
<p>This is the longest strategy so far, but it's pretty straightforward.  For each block, consider each number.  Find all the candidate cells, and if there are two or more, see if they're all in a single column.  If so, loop over the column and remove the number from each cell not in the current block.  Then do the same check for rows.</p>
<p>Using Groovy's getAt (bracket) notation, I could have wrapped the col and row checks into a single loop to reduce some duplication, but I haven't here.  You'll see that technique in some of the later strategies, however.</p>
<p>Finally, you'll notice that the whole board is iterated over and potentially many plays are made before the method returns.  As such, using each iterators wasn't a big deal.  This is probably somewhat wasteful, because a pointing pair can potentially do a lot of elimination (and therefore falling back to the <a href="http://www.barneyb.com/barneyblog/2010/01/27/sudoku-gamerulesstrategy/">GameRulesStrategy</a> would be useful), but I haven't done it here.</p>
<p>The raw source (including the test cases) is available at <a href="https://ssl.barneyb.com/svn/barneyb/sudoku/groovy/trunk/PointingPairStrategy.groovy">PointingPairStrategy.groovy</a>, should you be interested.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/7YIcl9QJOBc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/03/05/sudoku-pointing-pair-strategy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/03/05/sudoku-pointing-pair-strategy/</feedburner:origLink></item>
		<item>
		<title>Scaling Averages By Count</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/UWI8QO1FhbM/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/03/02/scaling-averages-by-count/#comments</comments>
		<pubDate>Wed, 03 Mar 2010 03:49:30 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[potd]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1290</guid>
		<description><![CDATA[One of the problems with statistics is that they work really well when you have perfect data (and therefore don't really need to do statistics), but start falling apart when the real world rears it's ugly head and gives you data that isn't all smooth.  Consider a very specific case: you have items that people [...]]]></description>
			<content:encoded><![CDATA[<p>One of the problems with statistics is that they work really well when you have perfect data (and therefore don't really need to do statistics), but start falling apart when the real world rears it's ugly head and gives you data that isn't all smooth.  Consider a very specific case: you have items that people can rate and then you want to pull out the "favorite" items based on those ratings.  As a more concrete example, say you're Netflix and based on a person's movie ratings (from 1-5 stars), you want to identify their favorite actors (piggybacking the assumption that movies they like probably have actors they like).</p>
<p>This is a simple answer to derive: just average the ratings of every movie the actor was in, and whichever actors have the highest average are the favorites.  Here it is expressed here in SQL:</p>
<pre>select actor.name, avg(rating.stars) as avgRating
from actor
  inner join movie_actor on movie_actor.actorId = actor.id
  inner join movie on movie_actor.movieId = movie.id
  inner join rating on movie.id = rating.movieId
where rating.subscriberId = ? -- the ID of the subscriber whose favorite actors you want
group by actor.name
order by avgRating desc
</pre>
<p>The problem is that &#8211; as an example &#8211; Tom Hanks was in both Sleepless in Seattle and Saving Private Ryan.  Clearly those two movies appeal to different audiences, and it seems very reasonable that someone who saw both would like one far more than the other, regardless of whether or not they like Tom Hanks.  The next problem is if they've only seen one of those movies, the ratings are going to paint an unfair picture of Tom Hanks' appeal.  So how can we solve this?</p>
<p>The short answer is that we can't.  In order to solve it, we'd have to synthesize the missing data points, which isn't possible for obvious reasons.  However, we can make a guess based on other datapoints that we do have.  In particular, we know the average rating for all movies for a user, so we can bias "small" actor samples towards that overall average.  This will help mitigate the dramatic effect of outliers in small sample sizes when there aren't enough other datapoints to mitigate them.</p>
<p>In other words, instead of this: <img src='http://s.wordpress.com/latex.php?latex=%5Coverline%7Br%7D_%7Bactor%7D%5C%20%3D%5C%20avg%28rating_%7Bmovie_%7Bactor%7D%7D%29&#038;bg=T&#038;fg=000000&#038;s=1' alt='\overline{r}_{actor}\ =\ avg(rating_{movie_{actor}})' title='\overline{r}_{actor}\ =\ avg(rating_{movie_{actor}})' class='latex block' /></p>
<p>we can do something like this: <img src='http://s.wordpress.com/latex.php?latex=n%20%3D%20count%28rating_%7Bmovie_%7Bactor%7D%7D%29&#038;bg=T&#038;fg=000000&#038;s=1' alt='n = count(rating_{movie_{actor}})' title='n = count(rating_{movie_{actor}})' class='latex block' /> <img src='http://s.wordpress.com/latex.php?latex=%5Coverline%7Br%5E%5Cprime%7D_%7Bactor%7D%5C%20%3D%5C%20%5Cbar%7Br%7D_%7Bactor%7D%5C%20-%5C%20%5Cfrac%7B%28%5Cbar%7Br%7D_%7Bactor%7D%5C%20-%5C%20%5Cbar%7Br%7D_%7Boverall%7D%29%7D%7B1.15%5En%7D&#038;bg=T&#038;fg=000000&#038;s=2' alt='\overline{r^\prime}_{actor}\ =\ \bar{r}_{actor}\ -\ \frac{(\bar{r}_{actor}\ -\ \bar{r}_{overall})}{1.15^n}' title='\overline{r^\prime}_{actor}\ =\ \bar{r}_{actor}\ -\ \frac{(\bar{r}_{actor}\ -\ \bar{r}_{overall})}{1.15^n}' class='latex block' /></p>
<p>This simply takes the normal average from above, and "scoots" it towards the overall average based.  The denominator is a constant picked by me (more later) raised to the power equal to the number of samples we have.  This way as the number of samples goes up, the magnitude of the correction falls rapidly.  Here's a chart illustrating this (the x axis is a log scale):</p>
<p style="text-align: center;"><img class="aligncenter" title="Correction By Sample Count" src="http://chart.apis.google.com/chart?cht=lc&amp;chs=500x275&amp;chd=t:.8696,.7561,.5718,.3269,.1069,.0114,.000013&amp;chds=0,1&amp;chxt=x,y,x&amp;chxr=1,0,1,0.1&amp;chxl=0:|1|2|4|8|16|32|64|2:|samples&amp;chxp=2,50&amp;chtt=Correction+By+Sample+Count+(1.15+factor)&amp;chg=16.66,20,1,4" alt="" width="500" height="275" /></p>
<p>With only one sample, the per-actor average will be scooted 87% of the way towards the overall average.  With four samples the correction will be only 57%, and by the time you get 32 samples there will be only a 1% shift.  Note that those percentages are of the distance to the overall average, not any absolute value change.  So if a one-sample actor happens to be only 0.5 stars away from the overall average, the net correction will be 0.465.  However, if a different one-sample actor is 1.5 stars away from the overall average, the net correction will be 1.305.</p>
<p>Of course, I'm not Netflix, so my data was from PotD, but the concept is the identical.  The "1.15&#8243; factor was derived based on testing on the PotD dataset, and demonstrated an appropriate falloff as the sample size increased.  Here's a sample of the data, showing both uncorrected and corrected averages ratings, along with pre- and post-correction rankings:</p>
<table>
<thead>
<tr>
<th>Model</th>
<th>Samples</th>
<th>Average</th>
<th>Corr. Average</th>
<th>Rank</th>
<th>Corr. Rank</th>
</tr>
</thead>
<tbody>
<tr>
<td>#566</td>
<td>22</td>
<td>4.1818</td>
<td>4.1310</td>
<td>46</td>
<td>1</td>
</tr>
<tr>
<td>#375</td>
<td>12</td>
<td>4.1667</td>
<td>3.9640</td>
<td>47</td>
<td>2</td>
</tr>
<tr>
<td>#404</td>
<td>13</td>
<td>4.0000</td>
<td>3.8509</td>
<td>81</td>
<td>3</td>
</tr>
<tr>
<td>#1044</td>
<td>7</td>
<td>4.2857</td>
<td>3.8334</td>
<td>44</td>
<td>4</td>
</tr>
<tr>
<td>#564</td>
<td>5</td>
<td>4.4000</td>
<td>3.7450</td>
<td>42</td>
<td>5</td>
</tr>
<tr>
<td>#33</td>
<td>32</td>
<td>3.7500</td>
<td>3.7424</td>
<td>176</td>
<td>6</td>
</tr>
<tr>
<td>#954</td>
<td>4</td>
<td>4.5000</td>
<td>3.6895</td>
<td>40</td>
<td>7</td>
</tr>
<tr>
<td>#733</td>
<td>4</td>
<td>4.5000</td>
<td>3.6895</td>
<td>39</td>
<td>8</td>
</tr>
<tr>
<td>#330</td>
<td>7</td>
<td>4.0000</td>
<td>3.6551</td>
<td>74</td>
<td>9</td>
</tr>
<tr>
<td>#293</td>
<td>5</td>
<td>4.2000</td>
<td>3.6444</td>
<td>45</td>
<td>10</td>
</tr>
</tbody>
</table>
<p>In particular, model #33 sees a huge jump upward because of the number of samples.  You can't see it here, but the top 37 models using the simple average are all models with a single sample (a 5-star rating), which is obviously not a real indicator.  Their corrected average is 3.3391, so not far off the leaderboard, but appreciably lower than those who have consistently received high ratings.</p>
<p>For different size sets (both overall, and expected number of ratings per actor/model) the factor will need to be adjusted.  It must remain strictly greater than one, and is theoretically unbounded on the other end but there is obviously a practical/reasonable limit.</p>
<p>Is this a good correction?  Hard to say.  It seems to work reasonably well with my PotD dataset (both as a whole, and segmented various ways), and it makes reasonable logical sense too.  The point really is that if you don't care about correctness, you can do some interesting fudging of your data to help it be useful in ways that it couldn't otherwise be.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/UWI8QO1FhbM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/03/02/scaling-averages-by-count/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/03/02/scaling-averages-by-count/</feedburner:origLink></item>
		<item>
		<title>A Drastic Change</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/avkaNenfPFg/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/03/01/a-drastic-change/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 03:45:31 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[meta]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1286</guid>
		<description><![CDATA[So today I decided to upgrade to WordPress 2.9.2 (I'd been running 2.7 since forever), and unfortunately it broke K2, which is the theme I've been using since I switched to WordPress years ago.  K2 was a solid theme, but it started getting rather unstable I thought, so it was hard to get a good [...]]]></description>
			<content:encoded><![CDATA[<p>So today I decided to upgrade to <a href="http://wordpress.org/">WordPress</a> 2.9.2 (I'd been running 2.7 since forever), and unfortunately it broke <a href="http://getk2.com/">K2</a>, which is the theme I've been using since I switched to WordPress years ago.  K2 was a solid theme, but it started getting rather unstable I thought, so it was hard to get a good release.  And it's really heavy, bloated with JavaScript, and just wasn't what I wanted anymore.  However, with a busted site, it wasn't the time to go theme shopping.  A little digging around the WP core showed that they'd deprecated (and then changed the API of) the attribute_escape function, and K2 depended on the old behaviour (automatic spreading across an array).  A couple quick patches and things were back on their feet.</p>
<p>Being the idiot I am, I figured I may as well upgrade K2 (since it would have those same fixes I made, along with other related ones I didn't catch), and that went horribly.  Their 1.0.3 release pretty much failed, and is even more bloated than before.  So now, with an again-broken site, I went theme shopping.</p>
<p>Fortunately, I'd just received a couple recommendations on a K2 replacement, and <a href="http://themehybrid.com/">Theme Hybrid</a> with the <a href="http://themehybrid.com/blog/wp-content/themes/fusion/images/life-collage-preview.jpg">Life Collage</a> child theme appeared to be relatively close to what I wanted.  So after tossing that in, I again had a non-broken site.  After another hour or so of <a href="http://www.barneyb.com/barneyblog/wp-content/themes/lighthouse/style.css">CSS hackery</a> atop the Life Collage CSS (but just the Hybrid HTML), I again have a site I'm relatively happy with.  Unfortunately WordPress doesn't let child themes extend other child themes (only "root" themes), so I couldn't get all the Life Collage HTML goodies (at least without copying), but no great loss there, I don't think.</p>
<p>As an added bonus, the theme supports dropdown menus for the top nav (based on subpages), which is something I've wanted for my project list for a while, but never got around to building with K2.  So one less project I have to do, and that's always a good thing.</p>
<p>The only remaining major issues are that there doesn't seem to be a no-comments template for pages (which is unfortunate, I think), and <span style="text-decoration: line-through;">WP apparently did away with the recent posts sidebar widget (no idea why)</span>. <em>[ed. They didn't get rid of it, they just changed it to be a 'postbypost' archive widget.]</em> I'm not sure I'm going to stick with Life Collage's base styling, but I'll probably stick with Hybrid as a root theme.  Both are a little id-heavier than I could wish for, but they've got pretty solid markup which makes styling pretty straightforward.</p>
<p>Just for the sake of completeness, the other couple suggested theme replacements were <a href="http://wpframework.com/">WP Framework</a> by Ptah Dunbar and <a href="http://themeshaper.com/thematic/">Thematic</a> by Ian Stewart.  Of the three, Hybrid looked the best out of the box, I thought, though they were all very similar.  It also happened to have prepackaged child themes, which was a win for me with the state of my site at the time.  But even without that, I think I'd still have picked Hybrid.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/avkaNenfPFg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/03/01/a-drastic-change/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/03/01/a-drastic-change/</feedburner:origLink></item>
		<item>
		<title>Wow Am I UGLY!!</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/eR8TRobyq2o/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/03/01/wow-am-i-ugly/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 01:08:30 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[aside]]></category>
		<category><![CDATA[meta]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1271</guid>
		<description><![CDATA[Yeah, so apparently WordPress 2.9 totally broke K2.  My apologies for the horrific appearance of the site, though I'm delighted to say the admin area still looks awesome!  Or something.  I'll get it fixed here shortly, I promise&#8230;.
UPDATE: apparently WordPress not only deprecated `attribute_escape`, they also changed it's functionality (despite it's widespread use in the [...]]]></description>
			<content:encoded><![CDATA[<p>Yeah, so apparently WordPress 2.9 totally broke K2.  My apologies for the horrific appearance of the site, though I'm delighted to say the admin area still looks awesome!  Or something.  I'll get it fixed here shortly, I promise&#8230;.</p>
<p><strong>UPDATE:</strong> apparently WordPress not only deprecated `attribute_escape`, they also changed it's functionality (despite it's widespread use in the app) to no longer correctly process arrays.  I don't know.  In any case, replacing a couple instances of it within K2 with an `array_map` application of `esc_attr` fixed it.  The actual problem was that the BODY classes weren't being correctly generated, thereby throwing off all the CSS selectors.</p>
<p><strong>UPDATE 2:</strong> I've been wanting to get away from K2 for a while.  It was awesome, but now it's kind of broken.  And upgrading to 1.0.3 left me with all kinds of weirdness.  So I kicked it to the curb, and am going to be starting afresh.  Should be good.</p>
<p><strong>UPDATE 3:</strong> So I've obviously reskinned.  For an hour of hakcing a previously-unknown theme's CSS I'm pretty happy, but definitely still pretty rough around the edges.  But it's good enough, so now it's time for dinner.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/eR8TRobyq2o" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/03/01/wow-am-i-ugly/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/03/01/wow-am-i-ugly/</feedburner:origLink></item>
		<item>
		<title>Public URLs via Amazon S3 CFC</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/iiBlp93Ierg/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/02/26/public-urls-via-amazon-s3-cfc/#comments</comments>
		<pubDate>Sat, 27 Feb 2010 05:00:40 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[cfml]]></category>
		<category><![CDATA[tools]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1263</guid>
		<description><![CDATA[I made another minor update to my Amazon S3 CFC this evening, this time to support PUTting public objects.  To this point, the CFC left the default ACL on new objects PUT on S3, meaning that you needed an authorized URL (via a query string signature) to retrieve them.  That was the use case I [...]]]></description>
			<content:encoded><![CDATA[<p>I made another minor update to my <a href="http://www.barneyb.com/barneyblog/projects/amazon-s3-cfc/">Amazon S3 CFC</a> this evening, this time to support PUTting public objects.  To this point, the CFC left the default ACL on new objects PUT on S3, meaning that you needed an authorized URL (via a query string signature) to retrieve them.  That was the use case I had when I built it, and again every time I've used it on other projects.  However, that's not every use case.</p>
<p>With this latest update, there is now an optional fifth parameter to 'putFileOnS3' named 'isPublic'.  It defaults to false (to retain backwards compatibility).  If you set it to true, the 'public-read' ACL will be placed on the object you're PUTting, making it immediately publicly available for retrieval.  There is a corresponding change to the private 'getRequestSignature' method for accepting headers on requests to sign per Amazon's CanonicalizedAmzHeaders format.</p>
<p>As always, the CFC itself is available (<a href="http://www.barneyb.com/barneyblog/wp-content/uploads/2010/02/amazons3cfc.txt">amazon.cfc.txt</a>) and the <a href="http://www.barneyb.com/barneyblog/projects/amazon-s3-cfc/">project page</a> is the place to get latest info/releases.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/iiBlp93Ierg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/02/26/public-urls-via-amazon-s3-cfc/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/02/26/public-urls-via-amazon-s3-cfc/</feedburner:origLink></item>
		<item>
		<title>Sudoku HiddenSingleStrategy</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/54Gsjcgjr0Y/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/02/18/sudoku-hiddensinglestrategy/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 02:48:17 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[groovy]]></category>
		<category><![CDATA[sudoku]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1258</guid>
		<description><![CDATA[The next Sudoku solving strategy I want to dig into is called "hidden single".  Here's the implementation to start with:
class HiddenSingleStrategy implements Strategy {

  static getTests() {
    [
      new Test(
        '0' * 9 + '000200000' + '0' * 9 [...]]]></description>
			<content:encoded><![CDATA[<p>The next Sudoku solving strategy I want to dig into is called "hidden single".  Here's the implementation to start with:</p>
<pre>class HiddenSingleStrategy implements Strategy {

  static getTests() {
    [
      new Test(
        '0' * 9 + '000200000' + '0' * 9 + '000060000' + '0' * 9 + '000080000' + '0' * 9 * 2 + '000002000',
        {
          def cell = it.getCell(5, 5)
          cell.isKnown() &amp;&amp; cell.value == 2
        }
      )
    ]
  }

  boolean play(Board board) {
    for (h in board.houses) {
      for (n in 1..9) {
        def cells = h.findAll {
          ! it.isKnown() &amp;&amp; it.hasMark(n)
        }
        if (cells.size() == 1) {
          // we found a hidden single
          cells[0].setValue(n, this)
          return true
        }
      }
    }
    false
  }

}</pre>
<p>As you can see, the test is a little different than for <a href="http://www.barneyb.com/barneyblog/2010/01/27/sudoku-gamerulesstrategy/">GameRulesStrategy</a>.  Before I just supplied a board and a successful test was when the board is solved.  In this case I'm just checking for if cell (5, 5) is known and it's value is 2 (no need to solve the whole board).</p>
<p>The strategy itself is pretty simple: for every house, for every number, see if any cell is the ONLY cell in the house that can contain the number, and if so, set that cell to that number.  Once we find a cell to set, the method exits (returning true) and does not continue the search.  This is the reason for the 'for' loops instead of the Groovy 'each' iterator.  This lets GameRulesStrategy "clean up" the board before continuing the search.  In this case it's probably not a big deal, but with the more complex strategies this is an essential optimization.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/54Gsjcgjr0Y" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/02/18/sudoku-hiddensinglestrategy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/02/18/sudoku-hiddensinglestrategy/</feedburner:origLink></item>
		<item>
		<title>Testing LaTeX Support</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/iLWaIK4rFLA/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/02/18/testing-latex-support/#comments</comments>
		<pubDate>Fri, 19 Feb 2010 00:47:10 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[meta]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1227</guid>
		<description><![CDATA[I just set up the WordPress LaTeX plugin so I can do some formula stuff here.  This is just a little test post with some example formulas taken from my sun/earth collision model.
Euler's Identity (at different sizes): 
Gravitational Constant: 
Force due to gravity: 
Inline also works (e.g., ).
If you use shortcodes within a paragraph (e.g., [...]]]></description>
			<content:encoded><![CDATA[<p>I just set up the <a href="http://wordpress.org/extend/plugins/wp-latex/">WordPress LaTeX plugin</a> so I can do some formula stuff here.  This is just a little test post with some example formulas taken from my <a href="http://www.barneyb.com/r/sun_earth.html">sun/earth collision model</a>.</p>
<p>Euler's Identity (at different sizes): <img src='http://s.wordpress.com/latex.php?latex=e%5E%7Bi%20%5Cpi%7D%20%2B%201%20%3D%200&#038;bg=T&#038;fg=000000&#038;s=-1' alt='e^{i \pi} + 1 = 0' title='e^{i \pi} + 1 = 0' class='latex block' /></p>
<img src='http://s.wordpress.com/latex.php?latex=e%5E%7Bi%20%5Cpi%7D%20%2B%201%20%3D%200&#038;bg=T&#038;fg=000000&#038;s=2' alt='e^{i \pi} + 1 = 0' title='e^{i \pi} + 1 = 0' class='latex block' />
<p>Gravitational Constant: <img src='http://s.wordpress.com/latex.php?latex=G%3D%206.673%20%5Ctimes%2010%5E%7B-11%7D%20m%5E3%5C%3A%20kg%5E%7B-1%7D%5C%3A%20s%5E%7B-2%7D%20%3D%206.673%20%5Ctimes%2010%5E%7B-11%7D%20%5Cfrac%7Bm%5E3%7D%7Bkg%5C%3As%5E2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='G= 6.673 \times 10^{-11} m^3\: kg^{-1}\: s^{-2} = 6.673 \times 10^{-11} \frac{m^3}{kg\:s^2}' title='G= 6.673 \times 10^{-11} m^3\: kg^{-1}\: s^{-2} = 6.673 \times 10^{-11} \frac{m^3}{kg\:s^2}' class='latex block' /></p>
<p>Force due to gravity: <img src='http://s.wordpress.com/latex.php?latex=F%20%3D%20G%20%5Cfrac%7BM_1%20M_2%7D%7Br%5E2%7D&#038;bg=T&#038;fg=000000&#038;s=0' alt='F = G \frac{M_1 M_2}{r^2}' title='F = G \frac{M_1 M_2}{r^2}' class='latex block' /></p>
<p>Inline also works (e.g., <img src='http://s.wordpress.com/latex.php?latex=2%20%2B%202%20%3D%204&#038;bg=T&#038;fg=000000&#038;s=0' alt='2 + 2 = 4' title='2 + 2 = 4' class='latex' />).</p>
<p>If you use shortcodes within a paragraph (e.g., <img src='http://s.wordpress.com/latex.php?latex=3%20%2B%201%20%3D%204&#038;bg=T&#038;fg=000000&#038;s=0' alt='3 + 1 = 4' title='3 + 1 = 4' class='latex block' />) instead of the inline notation, you'll get a block. (This is a custom mod.)</p>
<p>Finally, if you put an inline in its own paragraph, it'll do this:</p>
<p><img src='http://s.wordpress.com/latex.php?latex=8%20%5Cdiv%202%20%3D%204&#038;bg=T&#038;fg=000000&#038;s=0' alt='8 \div 2 = 4' title='8 \div 2 = 4' class='latex' /></p>
<p>Unless you explicitly state that it is a block:</p>
<p><img src='http://s.wordpress.com/latex.php?latex=8%20%5Cdiv%202%20%3D%204&#038;bg=T&#038;fg=000000&#038;s=0' alt='8 \div 2 = 4' title='8 \div 2 = 4' class='latex block' /></p>
<p>The reverse of that, as you'd expect, lets in-paragraph shortcodes work too (e.g., <img src='http://s.wordpress.com/latex.php?latex=3%20%2B%201%20%3D%204&#038;bg=T&#038;fg=000000&#038;s=0' alt='3 + 1 = 4' title='3 + 1 = 4' class='latex' />).</p>
<p>If you want to see the raw LaTeX, it's in the tooltip/title of the image.  WordPress.com provides the encoding service, though the plugin supports a local/third-party image service as well.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/iLWaIK4rFLA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/02/18/testing-latex-support/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/02/18/testing-latex-support/</feedburner:origLink></item>
		<item>
		<title>Front Controllers Should NOT Extend Application.cfc</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/ZY7fKEmKH4E/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/02/12/applicationcfc-extends-front-controller-is-evil/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 22:59:25 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[cfml]]></category>
		<category><![CDATA[development]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1221</guid>
		<description><![CDATA[I've been playing with FW/1 a bit on a personal app, and it has proven incredibly frustrating due to multiple manifestations of a single problem: your Application.cfc HAS to extend the framework in order to use the framework.  My complaint really has nothing to do with FW/1 in particular, the exact same argument could be [...]]]></description>
			<content:encoded><![CDATA[<p>I've been playing with <a href="http://fw1.riaforge.org/">FW/1</a> a bit on a personal app, and it has proven incredibly frustrating due to multiple manifestations of a single problem: your Application.cfc HAS to extend the framework in order to use the framework.  My complaint really has nothing to do with FW/1 in particular, the exact same argument could be made against <a href="http://fusebox.org/">Fusebox</a>'s Application.cfc integration (but FB at least provides a "normal" way to use it).  And just to be clear, I'm also not railing against <a href="http://www.corfield.org/">Sean Corfield</a>, even though he happens to be the author of both FW/1 and Fusebox's Application.cfc integration.</p>
<p>The first problem is due to Adobe's seemingly mindless choice to require the use of Application.cfc for per-request settings (datasource, mappings, ORM config, etc.), rather than doing it the right way with tags (in the style of the CFAPPLICATION tag).  With Application.cfc being the only place you can define any of this, you cannot use Application.cfc for an individual frontend, since it has to be shared across ALL frontends.</p>
<p>Consider a prototypical blog.  It has a public side (where the public reads), and an admin side (where authors write).  Two separate front ends; one single application.  If your front controller is bound to Application.cfc, you're forced to either run two separate applications or a single dual-purpose frontend.  Either one is a mess, either reducing encapsulation or increasing duplication.  At the very least.</p>
<p>Now consider a different example: an app with a single frontend that also needs one, single, solitary, standalone page for something.  Maybe even just for a quick one-off test script.  You create 'test.cfm' in your directory (so it gets the proper Application.cfc context so you can do your ORM magic), and hit it in the browser.  Oops, your framework decided with it's onRequest handling that it's going to just do it's thing, completely ignoring your template.  Different manifestation, same problem, though this one can be mostly addressed by overriding onRequest with custom behaviour that conditionally invokes super.onRequest.</p>
<p>Like the majority of places where inheritance is used, the proper solution is composition.  Rather than having your Application.cfc extend the framework, let your application compose the framework into itself.  That way it happens on the application's terms, rather than the framework's terms.  I understand that just extending the framework is desirable for ease of initial setup, so I'm not saying you can't do that, just you (as a framework author) should provide an alternative (like Fusebox's fusebox5.cfm).  Then I (as the application developer) can decide how the framework should be used.</p>
<p>Just to be clear, the problem with using inheritance where composition is the correct choice rests on both the shoulders of Adobe (for making request settings part of Application.cfc, rather than composed in with tags), and framework authors (for requiring Application.cfc to extend the framework).  Addressing either of these problems would handle the first manifestation (paragraph 3), but only the framework authors can deal with the second manifestation (paragraph 4).</p>
<p>Bottom line, don't wire yourself in so you can self-invoke, let me invoke you when and where I want you.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/ZY7fKEmKH4E" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/02/12/applicationcfc-extends-front-controller-is-evil/feed/</wfw:commentRss>
		<slash:comments>34</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/02/12/applicationcfc-extends-front-controller-is-evil/</feedburner:origLink></item>
		<item>
		<title>Goodbye JRun, Hello Tomcat</title>
		<link>http://feedproxy.google.com/~r/barneyblog/~3/Vo5jG-T72kE/</link>
		<comments>http://www.barneyb.com/barneyblog/2010/02/12/goodbye-jrun-hello-tomcat/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 18:37:49 +0000</pubDate>
		<dc:creator>barneyb</dc:creator>
				<category><![CDATA[coldfusion]]></category>
		<category><![CDATA[meta]]></category>

		<guid isPermaLink="false">http://www.barneyb.com/barneyblog/?p=1211</guid>
		<description><![CDATA[Last night I finally kicked JRun to the curb and replaced it with Tomcat on my personal server.  I knew JRun was a pig and that I'd get some improvements by switching, I'd just never gotten around to doing it.  I don't know why I waited.  Here are two graphs from my server, see if [...]]]></description>
			<content:encoded><![CDATA[<p>Last night I finally kicked JRun to the curb and replaced it with Tomcat on my personal server.  I knew JRun was a pig and that I'd get some improvements by switching, I'd just never gotten around to doing it.  I don't know why I waited.  Here are two graphs from my server, see if you can figure out when the switch happened:</p>
<p><img class="aligncenter" title="Resident Memory" src="http://chart.apis.google.com/chart?cht=lc&amp;chs=640x300&amp;chxt=x,r&amp;chxl=0:|22:00|2:30|7:00|11:40|16:10|20:30|1:00|5:40|9:50|1:|0|256|512|768|1,024&amp;chg=12.5,12.5&amp;chdlp=t&amp;chf=c,ls,90,eeeeee,0.97142791748,ffffff,1&amp;chtt=Resident+Memory&amp;chm=b,ffffdd,0,1,0&amp;chdl=Caches|Total|Tomcat|Tomcat|JRun|MySQL|LCYSA|Apache&amp;chco=ffffdd,ff0000,009900,009900,66cc66,006699,cccc00,cc00cc&amp;chd=s:6626566566646666564666666456566666426z6645666666666656656xy666665666665666665465666566666666656666665666665666624665u6z6666554666656456656466666466666466666566566466zy54625664665z636666626566555665646663y15664656yz1z4656,wxy0wxwxyrtuvz0nqvt3ywwwwwuwwwwwwwvwvxzvtuuvvuvvvvvvvvvuvuuzwwxwwvvwxwwwwwwwwuvvwsussvuvvvvvvvvvwvwxvww3ywwwxyxyryuyoognooononooopnonnppnpppppppppppppppppppppppoqopqppoprnmmmmnnlnnmnnppomonnomnmnnmnnnnnmnlmnnmnnnmmolmmnn,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACPNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOAA,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPP,WWWXTUWRSUWWWWWWXXVUVVWWWWWWWTWWWWXXXXVWRTUVVVWWWWWZXXYUVWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,KKKKKKKGKKKKKKKKKKKKKKKKKKKKKJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLKKK,JJJJGJJFJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIJIJJJJJJJJJJJJJJJJJJKKKKKKKLLLLLLLLLLLLLLLLKKIIIIIIIIIIIIIIIIIIIIIGGG,EEEDECEEEOEEEEEEEEEEDEDEEEECCEEEDEEEEECDEDEEEEEEEFEFEEEHBDFFFEEFFEFFFFFFFFFFFFFFFFFIFGFEFFFFFFDDFFFFFEFFFFEE" alt="" width="640" height="300" /></p>
<p><img class="aligncenter" title="Page Faults" src="http://chart.apis.google.com/chart?cht=lc&amp;chs=640x250&amp;chxt=x,r&amp;chxl=0:|22:00|2:30|7:00|11:40|16:10|20:30|1:00|5:40|9:50|1:|0|3,036|6,072|9,108|12,144&amp;chg=12.5,12.5&amp;chdlp=t&amp;chtt=Page+Faults&amp;chdl=Tomcat|Tomcat|JRun|MySQL|LCYSA|Apache&amp;chco=009900,009900,66cc66,006699,cccc00,cc00cc&amp;chd=s:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,ANALRQmsQazADAAABAHzQANAAAAFCMjJAADAABGKWZXSAACABIAa2BTk9PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,AAAAAACFlDAAAAAAAAABAAAAAAAAAADAAAAAAAAAEAAAAAAAAAAACABDBETFCCAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAABAABAAAAAA,AuAAAiAArABAAAAAAABAAAAAAAAAA6AAAAAAAACHBPAAAAAAAAAA1AAAyApAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAA,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" alt="" width="640" height="250" /></p>
<p>Migration was mind-numbingly simple:</p>
<ol>
<li>download and unzip Tomcat 6.0.24</li>
<li>change server.xml so the HTTP connector listens on 8082</li>
<li>copy everything from the JRun webroot to the Tomcat webroot</li>
<li>update Apache's config to proxy to Tomcat's port number (8082) instead of JRun's (8300)</li>
<li>start Tomcat, stop JRun, restart Apache</li>
</ol>
<p>Since I'm running CF8, nothing else is required.  If I were running CF9, I'd need JTA (from <a href="http://java.sun.com/javaee/technologies/jta/index.jsp">http://java.sun.com/javaee/technologies/jta/index.jsp</a>) so Hibernate would work, since Tomcat doesn't bundle it (it's a web container, not an app server).</p>
<p>If you looked carefully at the graphs you'll have noticed <em>two</em> "Tomcat" series, rather than one.  I neglected to increase the MaxPermSize when I first set it up, so the "swap" at about 8:45am was making that change.  The "LCYSA" process is another Tomcat running a pair of Magnolia instances (in different context), "Apache" is Apache HTTPD, and "MySQL" is the MySQL 5.0 server I store everything in for all apps.</p>
<p>The charts are generated by a simple CF app that parses `ps v -A O-v` output into a database, and then builds Google Charts from the data.  You can <a href="http://www.barneyb.com/mem_state/">view</a> the app, or check out the <a href="https://ssl.barneyb.com/svn/barneyb/mem_state/trunk/">source</a>.  It's not elegant, refined, maintainable, or really even very functional, and I had no intention of sharing until magnus asked in the comments.  But I also prefer transparency, so there it is.  A crontab runs `capture_mem_state.sh` every 10 minutes, and then the CFML reads in the files it generates.</p>
<img src="http://feeds.feedburner.com/~r/barneyblog/~4/Vo5jG-T72kE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.barneyb.com/barneyblog/2010/02/12/goodbye-jrun-hello-tomcat/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://www.barneyb.com/barneyblog/2010/02/12/goodbye-jrun-hello-tomcat/</feedburner:origLink></item>
	</channel>
</rss>
