<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;D0QNSXozeyp7ImA9WxBSFUo.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555</id><updated>2009-12-23T07:36:38.483-06:00</updated><title>FoemBlog</title><subtitle type="html">Erlang, Ruby, Java, etc.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.foemmel.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.foemmel.com/" /><link rel="hub" href="http://pubsubhubbub.appspot.com/" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>21</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/foemmel" /><feedburner:info uri="foemmel" /><entry gd:etag="W/&quot;CEQHQn0yeSp7ImA9WxRUFEU.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4417766772023566586</id><published>2008-11-21T22:43:00.006-06:00</published><updated>2008-11-23T17:18:53.391-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-23T17:18:53.391-06:00</app:edited><title>New Job!</title><content type="html">I've just finished my second week here at &lt;a href="http://www.drwholdings.com"&gt;DRW Trading Group&lt;/a&gt;.  So far I've been extremely impressed by the quality of the people, the number of interesting projects going on, and the overall energy of the place. It's also been fun catching up with the various ThoughtWorkers roaming the halls here. &lt;br /&gt;&lt;br /&gt;Windows XP was installed on my machine when I first arrived, but I'm happy to say that's been remedied and I'm now up and running with Ubuntu 8.10. Combined with a dual quad-core machine and a 30" monitor (plus a 19" side monitor) it's definitely the nicest workstation I've ever had. Much appreciated.&lt;br /&gt;&lt;br /&gt;As for what I'm actually working on here at DRW, all I can say is that I'm in the "algorithmic trading group", of which the first rule is "don't talk about the algorithmic trading group" (intellectual property and all). So we'll leave it at that for now...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4417766772023566586?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4417766772023566586/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4417766772023566586" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4417766772023566586?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4417766772023566586?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/UmpDyb265rM/new-job.html" title="New Job!" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/11/new-job.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0ECRH45fCp7ImA9WxRTGU8.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-3378690342426375998</id><published>2008-09-08T19:02:00.004-05:00</published><updated>2008-09-08T21:01:05.024-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-08T21:01:05.024-05:00</app:edited><title>Erlang R12B-4 Released</title><content type="html">The &lt;a href="http://github.com/mfoemmel/erlang-otp/tree/master"&gt;erlang-otp&lt;/a&gt; repository at GitHub has been updated.&lt;br /&gt;&lt;br /&gt;Some folks were having trouble building the previous releases, due to a well-known "feature" in git that causes it to ignore empty directories. I've modified my import script to add empty&lt;code&gt;.gitignore&lt;/code&gt; files where necessary, so that those empty directories come out the other end. The build scripts should now run out of the box.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-3378690342426375998?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/3378690342426375998/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=3378690342426375998" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/3378690342426375998?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/3378690342426375998?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/uHo1xiBajAs/erlang-r12b-4-released.html" title="Erlang R12B-4 Released" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/09/erlang-r12b-4-released.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEEHRHo4fSp7ImA9WxRTFU0.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-5969671842983489627</id><published>2008-09-03T23:30:00.001-05:00</published><updated>2008-09-03T23:30:35.435-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-03T23:30:35.435-05:00</app:edited><title>LLVM Buzz</title><content type="html">I've been playing around with the &lt;a href="http://llvm.org/"&gt;Low Level Virtual Machine&lt;/a&gt; toolkit a bit recently, which is a whole suite of tools for implementing compilers, virtual machines, and other goodies only a C.S. major could love. I'd always been vaguely aware of the project, but a couple of recent developments made me think it's time to take a serious look at it:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;FlaCC&lt;/h3&gt;The first was the presentation that Scott Peterson of Adobe gave at the 2008 LLVM Developer's Meeting a few weeks ago (slides and video &lt;a href="http://llvm.org/devmtg/2008-08/"&gt;here&lt;/a&gt;). Scott's managed to get LLVM bytecode running on top the the Flash VM, which has all sorts of interesting implications (although it seems a bit backwards - shouldn't the high-level virtual machine i.e. Flash run on &lt;i&gt;top&lt;/i&gt; of the low-level virtual machine - not the other way around?). In particular, it means that you could potentially run arbitrary C programs in the browser, since a C-to-LLVM bytecode compiler already exists. &lt;br /&gt;&lt;br /&gt;Most of the major programming languages out there have interpreters written in C, so it follows that you could then run Ruby or Python or whatever in the modified Flash runtime without a whole lot of work. The browser would become even more like an OS, hosting arbitrary applications and giving them low-level APIs for accessing the DOM and other resources, instead of imposing particular languages or object models. Huzzah.&lt;br /&gt;&lt;br /&gt;Unfortunately, it doesn't look like FlaCC will be supported anytime time, which means we'll have to limp along with Javascript and Actionscript for the time being. And to be honest I can't see a reason why Adobe would want to support this - if I could run vanilla Ruby in the browser, why would I pony up the big bucks for all those ActionScript development tools?&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Snow Leopard&lt;/h3&gt;The second interesting development is Apple's use of LLVM in the next release of their operating system, code named &lt;a href="http://www.apple.com/macosx/snowleopard/"&gt;Snow Leopard&lt;/a&gt;. It appears that both Grand Central (which distributes processing across cores) and OpenCL (which distributes processing between the CPU and the GPU) will make use of LLVM.  In fact, Apple seems to be throwing so much weight behind the LLVM project, that it makes be wonder if they're building their own LLVM-based browser plugin. There does seem to be a rather gaping hole in this chart:&lt;table style="padding-top: 0; margin-top: 0"&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Company&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Preferred Language&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Virtual Machine&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Microsoft&lt;/td&gt;&lt;td&gt;C#, Visual Basic&lt;/td&gt;&lt;td&gt;.NET CLR&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Adobe&lt;/td&gt;&lt;td&gt;ActionScript&lt;/td&gt;&lt;td&gt;Flash&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Sun&lt;/td&gt;&lt;td&gt;Java&lt;/td&gt;&lt;td&gt;Java Virtual Machine&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Apple&lt;/td&gt;&lt;td&gt;Objective-C&lt;/td&gt;&lt;td&gt;???&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;Since Apple refuses to support Flash on the iPhone, and they haven't been shy about using iTunes as a way to get users to install other Apple software, my guess is that they will release an LLVM-based browser plugin at some point. Just a hunch.&lt;br /&gt;&lt;br /&gt;Anyway, to get the hang of things I'm working on a LLVM backend for a Lisp-like language. More to come...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-5969671842983489627?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/5969671842983489627/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=5969671842983489627" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/5969671842983489627?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/5969671842983489627?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/GdOAq225dBQ/llvm-buzz.html" title="LLVM Buzz" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/09/llvm-buzz.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4EQXo7eyp7ImA9WxdUFk0.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4330274247073106557</id><published>2008-08-01T11:25:00.000-05:00</published><updated>2008-08-01T11:25:00.403-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-08-01T11:25:00.403-05:00</app:edited><title>Erlang, Multiple Assignment, and Closures</title><content type="html">There's been some discussion on the tubes recently about the value of Erlang's single-assignment semantics, with people weighing in on &lt;a href="http://tonyarcieri.org/articles/2008/07/26/the-single-assignment-cargo-cult"&gt;both&lt;/a&gt; &lt;a href="http://patricklogan.blogspot.com/2008/07/calling-names.html"&gt;sides&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;However, no one seems to be asking the key question, namely what happens when you mix closures (which already exist in Erlang) with multiple-assignment variables? That's probably because the answer is a bit messy: you get an &lt;i&gt;object-oriented language&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;To see why this is the case, let's go ahead and define a &lt;code&gt;Person&lt;/code&gt; class in this (hypothetical) Erlang variant. The &lt;code&gt;Person&lt;/code&gt; class has a single &lt;code&gt;Name&lt;/code&gt; field and corresponding getter and setter methods:&lt;br /&gt;&lt;pre&gt;-record(person,{get_name,set_name}).&lt;br /&gt;&lt;br /&gt;new_person(Name) -&gt;&lt;br /&gt;    #person{&lt;br /&gt;        get_name=fun() -&gt; &lt;br /&gt;            Name &lt;br /&gt;        end,&lt;br /&gt;        set_name=fun(NewName) -&gt; &lt;br /&gt;            Name = NewName   % Multiple assignment&lt;br /&gt;        end  &lt;br /&gt;    }.&lt;/pre&gt;That's all there is to it. The arguments to the function (i.e. &lt;code&gt;Name&lt;/code&gt;) act as the fields of the object, and the anonymous functions assigned to the record (i.e. &lt;code&gt;get_name&lt;/code&gt; and &lt;code&gt;set_name&lt;/code&gt;) act as the methods. This means we can now create Person objects, pass them around, and modify them whenever we want:&lt;pre&gt;Person = new_person("Sean Combs"),&lt;br /&gt;print(Person#person.get_name()),&lt;br /&gt;Person#person.set_name("Puff Daddy"),&lt;br /&gt;print(Person#person.get_name()).&lt;br /&gt;etc...&lt;/pre&gt;&lt;br /&gt;Why does this matter? Because objects have &lt;i&gt;identity&lt;/i&gt;, and identity's a real bitch when it comes to distributed systems. In particular, a lot of questions pop up when you try to send an object over the wire to another process. Do you:&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Copy the object, in which case any changes made to it on the remote process won't be visible locally&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Send a reference to the object, such that any time the object is referred to in the remote process,  a call is made back to the original process (at great cost to performance)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Let the developer indicate on an object-by-object basis which of the previous two options they would prefer&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Punt, and simply forbid access to mutable variables from within closures.&lt;br /&gt; This is pretty limiting, to the point where it makes it impossible to even write a simple &lt;code&gt;foreach&lt;/code&gt; function. For example, the following code, which adds the numbers &lt;code&gt;[1,2,3]&lt;/code&gt; together, wouldn't be allowed (since &lt;code&gt;Sum&lt;/code&gt; is mutable and accessed from a closure):&lt;br /&gt;&lt;pre&gt;Sum = 0, &lt;br /&gt;foreach(fun(N)-&gt; Sum += N end, [1,2,3]), &lt;br /&gt;print(Sum)&lt;/pre&gt;&lt;br /&gt;Assuming we're OK with this, we still have to deal with the fact that we now have two types of variables in the language: mutable and immutable. Therefore we must either:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;Prefix immutable variables with "final" to indicate they can be used within a closure&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Prefix mutable variables with "var" to indicate they cannot be used within a closure&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;Rely on the compiler to infer which variables are mutable and which are used within closures (possibly producing some very cryptic error messages)&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;Take a "snapshot" of all variables in scope when a closure is created, and ignore any subsequent changes to the variable. For example, the following code would print "foo":&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Name = "foo",&lt;br /&gt;PrintName = fun() -&gt; print(Name) end,&lt;br /&gt;Name = "bar",&lt;br /&gt;PrintName().&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But this would print "bar":&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Name = "foo",&lt;br /&gt;Name = "bar",&lt;br /&gt;PrintName = fun() -&gt; print(Name) end,&lt;br /&gt;PrintName().&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Confusing, to say the least.&lt;br /&gt;    &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/ul&gt;Personally, I don't find any of these options very appealing. But if you really want  multiple-assignment variables in Erlang, you're going to have to pick one of them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4330274247073106557?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4330274247073106557/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4330274247073106557" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4330274247073106557?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4330274247073106557?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/geVMQ9Jqn7E/erlang-multiple-assignment-and-closures.html" title="Erlang, Multiple Assignment, and Closures" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/08/erlang-multiple-assignment-and-closures.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAGQng4eCp7ImA9WxdVFU0.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4023815477371275100</id><published>2008-07-19T16:19:00.001-05:00</published><updated>2008-07-19T16:25:23.630-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-07-19T16:25:23.630-05:00</app:edited><title>RESTful Protocol Buffers</title><content type="html">I'm considering adding support for &lt;a href="http://code.google.com/p/protobuf/"&gt;Protocol Buffers&lt;/a&gt; to the RESTful application that I'm currently working on, and was wondering what people thought. &lt;br /&gt;&lt;br /&gt;I'm aware of all the arguments against using binary formats in REST APIs - but we deal with some pretty big sets of data on our project, and the speed and compactness of Protocol Buffers compared to XML/JSON is just too tempting. So here's how I'm thinking we might implement things: &lt;ul&gt;&lt;br /&gt;&lt;li&gt;XML will remain the default representation for all resources.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If the client includes the &lt;code&gt;application/x-protobuf&lt;/code&gt; MIME type in their &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1"&gt;&lt;code&gt;Accept&lt;/code&gt;&lt;/a&gt; header, the server will return a Protocol Buffer instead (and set the &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17"&gt;&lt;code&gt;Content-Type&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;application/x-protobuf&lt;/code&gt;).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When a Protocol Buffer is returned, the HTTP response will also include an &lt;code&gt;X-Protobuf-Schema&lt;/code&gt; header containing the URI for the &lt;code&gt;.proto&lt;/code&gt; schema file.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;Is anyone else doing something similar? It would probably make sense to coordinate on MIME types, etc...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4023815477371275100?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4023815477371275100/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4023815477371275100" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4023815477371275100?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4023815477371275100?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/N960SOyXdRU/restful-protocol-buffers.html" title="RESTful Protocol Buffers" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/07/restful-protocol-buffers.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkUERX07fCp7ImA9WxdQEk8.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-8286237696878696868</id><published>2008-06-11T15:47:00.010-05:00</published><updated>2008-06-11T16:36:44.304-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-06-11T16:36:44.304-05:00</app:edited><title>Erlang R12B-3 Released</title><content type="html">There's a new release of Erlang/OTP out today, which you can download from the &lt;a href="http://www.erlang.org/download.html"&gt;usual&lt;/a&gt; location. I've also imported it into the &lt;a href="http://github.com/mfoemmel/erlang-otp/tree/master"&gt;erlang-otp&lt;/a&gt; repository at GitHub.&lt;br /&gt;&lt;br /&gt;According to the &lt;a href="http://www.erlang.org/download/otp_src_R12B-3.readme"&gt;readme&lt;/a&gt;, this release contains an "experimental" regular expression module called &lt;a href="http://www.erlang.org/doc/man/re.html"&gt;&lt;code&gt;re&lt;/code&gt;&lt;/a&gt;. The module wraps a lower-level PCRE library and is "many times faster than the pure Erlang implementation". It also looks like it works equally well with both binary- and list-based strings. So I guess the race is one to see who can get their &lt;a href="http://wikis.sun.com/display/WideFinder/Wide+Finder+Home"&gt;WideFinder&lt;/a&gt; implementation up and running first...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-8286237696878696868?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/8286237696878696868/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=8286237696878696868" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/8286237696878696868?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/8286237696878696868?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/CO1n5vq8Cg0/erlang-r12b-3-released.html" title="Erlang R12B-3 Released" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/06/erlang-r12b-3-released.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8DR3g4cCp7ImA9WxdRGUU.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-7244380476563963261</id><published>2008-06-08T19:19:00.008-05:00</published><updated>2008-06-08T22:41:16.638-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-06-08T22:41:16.638-05:00</app:edited><title>Updated Erlang/OTP Repository at GitHub</title><content type="html">I ended up deleting and recreating the &lt;a href="http://github.com/mfoemmel/erlang-otp/tree/master"&gt;erlang-otp&lt;/a&gt; repository at github today, in case anyone's having problems accessing it.  My &lt;a href="http://blog.foemmel.com/2008/04/erlangotp-source-at-github.html"&gt;original&lt;/a&gt; import script had a bug that prevented files from being deleted correctly between versions, which meant the files in the repository didn't exactly match the files in the source tarballs. It should be fixed now, but you were using the old repo you'll probably need to do a clean &lt;code&gt;git clone&lt;/code&gt; to get things working again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-7244380476563963261?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/7244380476563963261/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=7244380476563963261" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/7244380476563963261?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/7244380476563963261?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/noETn90vhbs/updated-erlangotp-reposistory-at-github.html" title="Updated Erlang/OTP Repository at GitHub" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/06/updated-erlangotp-reposistory-at-github.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EDQHwycSp7ImA9WxdRFUg.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4415496741723704540</id><published>2008-06-01T19:12:00.003-05:00</published><updated>2008-06-04T01:07:51.299-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-06-04T01:07:51.299-05:00</app:edited><title>Message Ordering in Erlang</title><content type="html">I've noticed that when people first learn Erlang (myself included), they tend to assume that messages sent between processes must be handled in the same order they're received. Turns out this isn't the case - you can pluck a message out from &lt;i&gt;anywhere&lt;/i&gt; in a process's inbox, assuming you know a pattern that uniquely matches it. This is called a "selective receive".&lt;br /&gt;&lt;br /&gt;For example, let's say we fire off a couple of messages to a process registered as &lt;code&gt;myserver&lt;/code&gt;:&lt;pre&gt;myserver ! {name, "World"},&lt;br /&gt;myserver ! {greeting, "Hello"}&lt;/pre&gt;Even though the we're sending the &lt;code&gt;greeting&lt;/code&gt; message &lt;i&gt;after&lt;/i&gt; the &lt;code&gt;name&lt;/code&gt; message, it's possible for the receiver to process the greeting first, by matching on the &lt;code&gt;greeting&lt;/code&gt; atom in the first position of the tuple:&lt;pre&gt;Greeting = receive {greeting,G} -&gt; G end,&lt;br /&gt;Name = receive {name,N} -&gt; N end&lt;/pre&gt;Here the first line removes the second message (the &lt;code&gt;greeting&lt;/code&gt;) from the inbox, and the second line removes the first message (the &lt;code&gt;name&lt;/code&gt;). &lt;br /&gt;&lt;br /&gt;This may not seem like a big deal, but there are a lot of cases where this behavior comes in extremely handy. Below are couple of examples: synchronous messaging, and parallel processing of lists:&lt;br /&gt;&lt;h3&gt;Synchronous Messaging&lt;/h3&gt;Layering synchronous messaging (i.e. your basic remote request/response) on top of the asynchronous stuff built into Erlang seems like it should be easy - you just send a message to another process, and wait until you get a message back. And a lot of the time that's all there is to it.&lt;br /&gt;&lt;br /&gt;However, it's possible that your client process has lots of messages coming in from different places, in which case there's no guarantee that the first message you receive after sending a request will be the actual &lt;i&gt;response&lt;/i&gt; to your request. You'll have to do a selective receive to filter out the irrelevant messages.&lt;br /&gt;&lt;br /&gt;An easy way to do this is to include a unique token with each request, and have the server include it in its response. That way the client can just pattern match on the token.&lt;br /&gt;&lt;br /&gt;For example, say we have a time server that sends the current time back to any process that requests it (plus the token):&lt;pre&gt;time_server() -&gt;&lt;br /&gt;    receive&lt;br /&gt;        {get_time, Pid, Token} -&gt;&lt;br /&gt;            Pid ! {time, time(), Token}&lt;br /&gt;        end,&lt;br /&gt;    time_server().&lt;br /&gt;&lt;/pre&gt;We can then get the time from the server using the following two functions. The first is responsible for sending the &lt;code&gt;get_time&lt;/code&gt; message to the time server. It uses &lt;a href="http://www.erlang.org/doc/man/erlang.html#make_ref-0"&gt;make_ref&lt;/a&gt; the generate the token:&lt;pre&gt;request_time(TimeServer) -&gt; &lt;br /&gt;    Token =  make_ref(),&lt;br /&gt;    TimeServer ! {get_time, self(), Token},&lt;br /&gt;    Token.&lt;/pre&gt;The second is responsible for taking that token and using it to selectively retrieve the response from the inbox:&lt;pre&gt;receive_time(Token) -&gt;&lt;br /&gt;    receive {time, Time, Token} -&gt; Time end.&lt;/pre&gt;That's pretty much it. We just need to put the two together:&lt;pre&gt;get_time(TimeServer) -&gt;&lt;br /&gt;    Token = request_time(TimeServer),&lt;br /&gt;    receive_time(Token).&lt;/pre&gt; We now have a function that will send a request to another process and block until it receives a response, without affecting any of the other messages in the inbox.&lt;br /&gt;&lt;h3&gt;Parallel Processing Of Lists&lt;/h3&gt;Now let's say we want to make calls to a &lt;b&gt;bunch&lt;/b&gt; of different servers. And we'd like those servers to execute the calls in parallel because, well, that's just how we do things in Erlang.  &lt;br /&gt;&lt;br /&gt;So continuing our previous example, assume we have a list of &lt;code&gt;time_server&lt;/code&gt; processes. We want to send our &lt;code&gt;get_time&lt;/code&gt; message to all of them, and we want to block until we receive all of the responses. We'd also like the responses to be returned as a list whose order corresponds with the original list, so that we know which time value goes with which &lt;code&gt;time_server&lt;/code&gt;. (We can't just use the order the responses are received, since that's basically random.)&lt;br /&gt;&lt;br /&gt;A very concise way to implement all this in Erlang is to use two &lt;a href="http://www.erlang.org/doc/reference_manual/expressions.html#6.22"&gt;list comprehensions&lt;/a&gt;: one to send the requests out and generate the list of tokens, and another to take the list of tokens and selectively receive the responses. For example:&lt;pre&gt;get_times(TimeServers) -&gt;&lt;br /&gt;    Tokens = [ request_time(TimeServer) || TimeServer &lt;- TimeServers],&lt;br /&gt;    [ receive_time(Token) || Token &lt;- Tokens ].&lt;/pre&gt;That's all it takes to execute those calls in parallel, and to put the results in the right order. Not bad for two lines of code.&lt;br /&gt;&lt;h3&gt;And More...&lt;/h3&gt;We could take this example a lot further: maybe we want to ignore time servers that don't respond within a certain amount of time; or automatically restart them when they crash; or process the responses as they come in instead of collecting them in a list; and so on. &lt;br /&gt;&lt;br /&gt;All these scenarios are easy to implement using selective receives (and other language features like linked processes). The interprocess communication built into Erlang seems simple, but it's been carefully designed to give you a lot of flexibility when designing your system.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4415496741723704540?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4415496741723704540/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4415496741723704540" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4415496741723704540?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4415496741723704540?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/I_IYVY6RfCI/message-ordering-in-erlang.html" title="Message Ordering in Erlang" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/06/message-ordering-in-erlang.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4EQngyeip7ImA9WxdTFUs.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4450854641986345127</id><published>2008-05-09T13:10:00.011-05:00</published><updated>2008-05-11T23:51:43.692-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-11T23:51:43.692-05:00</app:edited><title>"Hello, World" Revisited - Automatic Reloading</title><content type="html">My &lt;a href="http://blog.foemmel.com/2008/05/hello-world-webapp-in-erlang.html"&gt;last post&lt;/a&gt; contained some code for a minimal "Hello, World" webapp in Erlang. However, that code wasn't very Erlangy - if you wanted to make a change to the application, you had to kill the shell and restart it. That's clearly not going to impress the "nine nines uptime" crowd. Plus it's annoying to develop that way.&lt;br /&gt;&lt;br /&gt;So here's some bonus code that will cause the server to compile and reload changes on the fly, without having to restart the server. It probably won't get you to nine nines, but it's a start. Just append the following to the original "hello_world.erl" file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;reload(SessionID, Env, Input) -&gt;&lt;br /&gt;    case make:all([load]) of&lt;br /&gt;        up_to_date -&gt;&lt;br /&gt;            hello_world:service(SessionID, Env, Input);&lt;br /&gt;        _ -&gt;&lt;br /&gt;            mod_esi:deliver(SessionID, [&lt;br /&gt;                "Content-Type: text/plain\r\n\r\n", &lt;br /&gt;                "compilation error"&lt;br /&gt;            ])&lt;br /&gt;    end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;code&gt;reload&lt;/code&gt; function acts as a wrapper around our original &lt;code&gt;service&lt;/code&gt; function, calling &lt;a href="http://erlang.mirror.libertine.org/documentation/doc-5.4.13/lib/tools-2.4.7/doc/html/make.html"&gt;&lt;code&gt;make:all&lt;/code&gt;&lt;/a&gt; (which will compile and reload any out of date code) before forwarding the request.&lt;br /&gt;&lt;br /&gt;You'll also need to stick an &lt;code&gt;export&lt;/code&gt; at the top of the file, after the module declaration:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-export([reload/3]).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Last but not least, you'll need to create a file called "Emakefile", which tells Erlang what to compile during the call to &lt;code&gt;make:all&lt;/code&gt;. In our case, the file is pretty simple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;{'*', []}.&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;Now start up the &lt;code&gt;erl&lt;/code&gt; shell and run the same steps as before:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Eshell V5.6  (abort with ^G)&lt;br /&gt;1&gt; c(hello_world).&lt;br /&gt;{ok,hello_world}&lt;br /&gt;2&gt; inets:start().&lt;br /&gt;ok&lt;br /&gt;3&gt; hello_world:start().&lt;br /&gt;{ok,&lt;0.51.0&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But this time, use the following URL to test the app, which will hit our new &lt;code&gt;reload&lt;/code&gt; function:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://localhost:8081/erl/hello_world:reload"&gt;http://localhost:8081/erl/hello_world:reload &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You should now be able to make changes to &lt;code&gt;hello_world.erl&lt;/code&gt; (e.g. change the message from "Hello, world" to something else), hit refresh in your browser, and immediately see the changes.&lt;br /&gt;&lt;h4&gt;What's &lt;i&gt;really&lt;/i&gt; happening here?&lt;/h4&gt;When I first started learning Erlang, I'd assumed that whenever you reloaded modules (via &lt;code&gt;make:all&lt;/code&gt; or &lt;a href="http://www.erlang.org/doc/man/code.html#load_file-1"&gt;&lt;code&gt;code:load_file&lt;/code&gt;&lt;/a&gt; or whatever) that those changes would go into effect immediately, similar to the way the &lt;code&gt;load&lt;/code&gt; command works in Ruby. However, that's not quite how things work - just because changes to a module have been &lt;i&gt;loaded&lt;/i&gt; doesn't necessarily mean they will be &lt;i&gt;executed&lt;/i&gt;. This is because Erlang gives you very fine grained control over when code changes take effect.&lt;br /&gt;&lt;br /&gt;The key to all this is the ':' operator. On the surface, it just looks like a namespace separator, used to disambiguate between functions with the same name in different modules. For example, within our &lt;code&gt;hello_world&lt;/code&gt; module, you would think that the following two lines of code would be identical, and that the &lt;code&gt;hello_world:&lt;/code&gt; in the first line would be redundant:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;hello_world:service(SessionID,Env,Input).&lt;br /&gt;service(SessionID,Env,Input).&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;However, they're not quite the same. The call in the first line actually checks to see if a newer version of the &lt;code&gt;hello_world&lt;/code&gt; module has been loaded, and if so, dispatches to that version. The call in the second line always dispatches to the same version as is currently running, even if a newer version has been loaded. If a new version of the module has &lt;b&gt;not&lt;/b&gt; been loaded, their behavior is the same.&lt;br /&gt;&lt;br /&gt;The upshot of this is that you can have two versions of your code running at the same time (Erlang doesn't let you have more than two, however). This is similar to the way some web servers have a "graceful" restart option, that allows currently executing requests to continue using the old configuration, while new requests use a different configuration. Except in Erlang you can pick the exact function call in your application where these upgrades happen. Very cool.&lt;br /&gt;&lt;br /&gt;You can test this behavior by leaving out the &lt;code&gt;hello_world:&lt;/code&gt; prefix in the fourth line of the reload function. You'll notice that your changes are no longer picked up immediately, but are picked up on the &lt;b&gt;next&lt;/b&gt; invocation (since the initial call to &lt;code&gt;hello_world:reload&lt;/code&gt; by the HTTP server will trigger an update).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4450854641986345127?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4450854641986345127/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4450854641986345127" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4450854641986345127?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4450854641986345127?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/NzQ_d-C9mAw/hello-world-revisited-automatic.html" title="&quot;Hello, World&quot; Revisited - Automatic Reloading" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/05/hello-world-revisited-automatic.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QHR308fyp7ImA9WxZaGUk.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4317133863478067668</id><published>2008-05-04T17:33:00.005-05:00</published><updated>2008-05-04T18:55:36.377-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-04T18:55:36.377-05:00</app:edited><title>"Hello World" Webapp in Erlang</title><content type="html">When I first started using Erlang it took a fair bit of trial and error to create a server that generated dynamic HTTP content. The main problem is the lack of good tutorials out there for doing these kinds of things. The documentation that comes with Erlang is good for reference, but is not that helpful if you're just learning the language.&lt;br /&gt;&lt;br /&gt;In particular, there didn't seem to be a basic "Hello World" example for building web applications. So here's my attempt to fix that. Below is some code that starts up an HTTP server, and dynamically generates a simple "Hello, World" page: &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-module(hello_world).&lt;br /&gt;-export([start/0,service/3]).&lt;br /&gt;&lt;br /&gt;start() -&gt;&lt;br /&gt; inets:start(httpd, [&lt;br /&gt;  {modules, [&lt;br /&gt;   mod_alias, &lt;br /&gt;   mod_auth, &lt;br /&gt;   mod_esi, &lt;br /&gt;   mod_actions, &lt;br /&gt;   mod_cgi, &lt;br /&gt;   mod_dir, &lt;br /&gt;   mod_get, &lt;br /&gt;   mod_head, &lt;br /&gt;   mod_log, &lt;br /&gt;   mod_disk_log&lt;br /&gt;  ]},&lt;br /&gt;  {port,8081},&lt;br /&gt;  {server_name,"hello_world"},&lt;br /&gt;  {server_root,"log"},&lt;br /&gt;  {document_root,"www"},&lt;br /&gt;  {erl_script_alias, {"/erl", [hello_world]}},&lt;br /&gt;  {error_log, "error.log"},&lt;br /&gt;  {security_log, "security.log"},&lt;br /&gt;  {transfer_log, "transfer.log"},&lt;br /&gt;  {mime_types,[&lt;br /&gt;   {"html","text/html"},&lt;br /&gt;   {"css","text/css"},&lt;br /&gt;   {"js","application/x-javascript"}&lt;br /&gt;  ]}&lt;br /&gt; ]).&lt;br /&gt; &lt;br /&gt;service(SessionID, _Env, _Input) -&gt;&lt;br /&gt; mod_esi:deliver(SessionID, [&lt;br /&gt;  "Content-Type: text/html\r\n\r\n", &lt;br /&gt;  "&amp;lt;html&gt;&amp;lt;body&gt;Hello, World!&amp;lt;/body&gt;&amp;lt;/html&gt;"&lt;br /&gt; ]).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To run it, save the code to a file called &lt;code&gt;hello_world.erl&lt;/code&gt;, and create two subdirectories next to it called "www" and "log" (these subdirectories can be empty, but they need to be there for the server to start). Then fire up &lt;code&gt;erl&lt;/code&gt; and run the following three commands:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Eshell V5.6  (abort with ^G)&lt;br /&gt;1&gt; c(hello_world).&lt;br /&gt;{ok,hello_world}&lt;br /&gt;2&gt; inets:start().&lt;br /&gt;ok&lt;br /&gt;3&gt; hello_world:start().&lt;br /&gt;{ok,&lt;0.51.0&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You should now be able to browse to the following URL and see your message (if for some reason it doesn't work for you please let me know):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://localhost:8081/erl/hello_world:service"&gt;http://localhost:8081/erl/hello_world:service&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;For more info, here are the reference docs for the relevant Erlang modules:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://www.erlang.org/doc/man/inets.html"&gt;inets&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://www.erlang.org/doc/man/httpd.html"&gt;httpd&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://www.erlang.org/doc/man/mod_esi.html"&gt;mod_esi&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4317133863478067668?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4317133863478067668/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4317133863478067668" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4317133863478067668?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4317133863478067668?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/Pk9Dj8gcDMI/hello-world-webapp-in-erlang.html" title="&quot;Hello World&quot; Webapp in Erlang" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/05/hello-world-webapp-in-erlang.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4CSXY4eyp7ImA9WxdTE0o.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-1323997796851530354</id><published>2008-04-25T15:04:00.006-05:00</published><updated>2008-05-09T19:06:08.833-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-09T19:06:08.833-05:00</app:edited><title>Erlang/OTP Source at GitHub</title><content type="html">I wrote a little script the other day to download all of the Erlang/OTP source releases that were available at erlang.org, and stick them in a single git repository. I've uploaded it to &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt;, if anyone's interested:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://github.com/mfoemmel/erlang-otp/tree/master"&gt;http://github.com/mfoemmel/erlang-otp/tree/master&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I found out after the fact that &lt;a href="http://archaelus.livejournal.com/"&gt;archaelus&lt;/a&gt; had done something similar, and has a git repository hosted here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://git.erlang.geek.nz/?p=erlang-otp.git;a=summary"&gt;http://git.erlang.geek.nz/?p=erlang-otp.git;a=summary&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The main difference between the two is that the one at GitHub includes releases going a lot further back (R6B-0 vs R11B-5) - which is good if you're curious about how Erlang has evolved over time, but also means the repository is that much bigger when it comes time to do a clone (Erlang includes a bunch of binary files in their "source" releases, which don't seem to compress very well). Archaelus also includes a few 3rd party patchsets in his repository, which may be of interest. &lt;br /&gt;&lt;br /&gt;The nice thing about GitHub, however, is that it makes it really easy for anyone to branch a project and make changes, and then make those changes available to everyone else (who can then merge them back into their own branches, and so on). Maybe this could help open up the Erlang development process a bit?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-1323997796851530354?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/1323997796851530354/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=1323997796851530354" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/1323997796851530354?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/1323997796851530354?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/CsTqtGTCZ-4/erlangotp-source-at-github.html" title="Erlang/OTP Source at GitHub" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/04/erlangotp-source-at-github.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEUNRH84cSp7ImA9WxZbEEk.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-400647198923203439</id><published>2008-04-10T18:22:00.001-05:00</published><updated>2008-04-12T19:24:55.139-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-04-12T19:24:55.139-05:00</app:edited><title>Erlang 12B-2 Released</title><content type="html">Looks like the second service pack for Erlang/OTP 12B has been &lt;a href="http://www.erlang.org/download.html"&gt;released&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;One nice change is that the &lt;code&gt;percept&lt;/code&gt; application no longer depends on &lt;code&gt;libgd&lt;/code&gt;. This should make compiling for Mac OS X a bit easier, since Leopard doesn't ship with GD by default. &lt;br /&gt;&lt;br /&gt;They also appear to have fixed some of the other OS X related &lt;a href="http://heath-tech.blogspot.com/2008/02/problems-with-erlang-r12b-1-on-leopard.html"&gt;build issues&lt;/a&gt; that cropped up in the first service pack.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-400647198923203439?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/400647198923203439/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=400647198923203439" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/400647198923203439?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/400647198923203439?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/Si6MJPACYqc/erlang-12b-2-released.html" title="Erlang 12B-2 Released" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/04/erlang-12b-2-released.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8AR3ozeip7ImA9WxZTEko.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-6562671692578477037</id><published>2008-01-13T13:55:00.001-06:00</published><updated>2008-01-13T20:47:26.482-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-13T20:47:26.482-06:00</app:edited><title>Switching to Blogger</title><content type="html">In an attempt to reduce the total number of machines in my life that I need to administer, I'm moving my blog over to Blogger. This will probably break my RSS feeds - my apologies.  If you notice any other issues please let me know at &lt;a href="mailto:blog@foemmel.com"&gt;blog@foemmel.com&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-6562671692578477037?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/6562671692578477037/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=6562671692578477037" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/6562671692578477037?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/6562671692578477037?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/q_G5Y6ZatFY/switching-to-blogger.html" title="Switching to Blogger" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2008/01/switching-to-blogger.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8HRnc_cCp7ImA9WxZTEko.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-179249115908752306</id><published>2007-12-05T22:02:00.000-06:00</published><updated>2008-01-13T20:47:17.948-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-13T20:47:17.948-06:00</app:edited><title>Erlang R12B Released</title><content type="html">New version of Erlang out today - release notes &lt;a href="http://www.erlang.org/doc/highlights.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For newbies like me, a big reason to upgrade is the much improved error reporting in the Erlang shell. For example, here's how the previous version reports a divide-by-zero exception:&lt;br /&gt;&lt;pre style="font-weight: bold;"&gt;Eshell V5.5.5  (abort with ^G)&lt;br /&gt;1&gt; 1/0.&lt;br /&gt;** exited: {badarith,[{erlang,'/',[1,0]},&lt;br /&gt;                    {erl_eval,do_apply,5},&lt;br /&gt;                    {shell,exprs,6},&lt;br /&gt;                    {shell,eval_loop,3}]} **&lt;br /&gt;&lt;br /&gt;=ERROR REPORT==== 5-Dec-2007::23:32:41 ===&lt;br /&gt;Error in process &lt;0.31.0&gt; with exit value: {badarith,[{erlang,'/',[1,0]},{erl_eval,do_apply,5},{shell,exprs,6},{shell,eval_loop,3}]}&lt;/pre&gt;New version:&lt;br /&gt;&lt;pre style="font-weight: bold;"&gt;Eshell V5.6  (abort with ^G)&lt;br /&gt;1&gt; 1/0.&lt;br /&gt;** exception error: bad argument in an arithmetic expression&lt;br /&gt;   in operator  '/'/2&lt;br /&gt;      called as 1 / 0&lt;/pre&gt;Better!&lt;br /&gt;&lt;br /&gt;I ran into one small hitch building the new version on my Mac, namely a "libgd not found" error while running "./configure".  I installed version 2.0.36RC1 from here, which did the trick:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.libgd.org/releases/"&gt;http://www.libgd.org/releases/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-179249115908752306?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/179249115908752306/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=179249115908752306" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/179249115908752306?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/179249115908752306?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/GtdcYDf6HIA/erlang-r12b-released.html" title="Erlang R12B Released" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://blog.foemmel.com/2007/12/erlang-r12b-released.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak8HQXw9fCp7ImA9WxZTEko.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-7106366143068687758</id><published>2007-12-02T20:13:00.000-06:00</published><updated>2008-01-13T20:47:10.264-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-01-13T20:47:10.264-06:00</app:edited><title>Periscope: An Agile Dashboard</title><content type="html">My colleagues Ben Erickson and Ben Feng have put together a nice little tool for tracking progress on Agile/XP projects, called &lt;a href="http://periscope.rubyforge.org/"&gt;Periscope&lt;/a&gt;. The idea is that you can use Jira (or potentially some other tool) to track your stories, and Periscope will occasioanally read that data, combine it with your build results, and generate reports indicating how far along you are.&lt;br /&gt;&lt;br /&gt;It's still in its "alpha" stage, but I'd recommend checking it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-7106366143068687758?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/7106366143068687758/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=7106366143068687758" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/7106366143068687758?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/7106366143068687758?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/jtHeswqwmJo/periscope-agile-dashboard.html" title="Periscope: An Agile Dashboard" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2007/12/periscope-agile-dashboard.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8DSHs5eip7ImA9WxZVF04.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4752270077823643444</id><published>2007-11-18T23:07:00.004-06:00</published><updated>2008-03-28T14:51:19.522-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-28T14:51:19.522-05:00</app:edited><title>Erlang: Initial Thoughts</title><content type="html">As an experiment, I recently tried porting some code from a Java project at work to Erlang, to see if that might be a direction we'd want to head in. The code being converted is very functional (e.g. almost all data structures are immutable) and easily parallelizable, so it seemed like a good fit.&lt;br /&gt;&lt;br /&gt;I was hoping to quickly find some excuse for why Erlang wouldn't be suitable for the kind of work we're doing. The little voice in my head has been nagging me for a while that "Java is the wrong language for this project" and I was hoping to shut it up, a least for a while, so that I could get back to work.&lt;br /&gt;&lt;br /&gt;But so far it's been pretty clear sailing. Erlang is famous for its concurrency features, but the thing that surprised me was how concise a language it is. Of the handful of classes I've converted, the Erlang versions were pretty consistently about 25% the size of the Java versions (and I'm sure they'll get even smaller once I know Erlang better).&lt;br /&gt;&lt;br /&gt;The reasons for the big difference seem to be:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Higher-order Functions&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A lot of our code involves iterating through lists, performing some action on each element, and aggregating the results. For example, in Java, if you want to go through a list of items and add up all of their "quantity" fields, you might do something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  public int totalQuantity(List&amp;lt;item&amp;gt; items) {&lt;br /&gt;    int totalQuantity = 0;&lt;br /&gt;    for(Item item : items) {&lt;br /&gt;      totalQuantity += item.getQuantity();&lt;br /&gt;    }&lt;br /&gt;    return totalQuantity;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whereas in Erlang you can use higher-order functions ("map" in this case) to do this in one line:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  total_quantity(Items) -&gt; sum(map(fun(Item) -&gt; quantity(Item) end, Items)).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Erlang also supports list comprehensions, which can make things a bit more readable:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  total_quantity(Items) -&gt; sum([quantity(Item) || Item &lt;- Items]).  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is pretty standard functional programming stuff - no real surprises. (Java will hopefully improve on this front with the introduction of &lt;a href="http://www.javac.info/"&gt;closures&lt;/a&gt; in JDK7.)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Pattern Matching&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I was more surprised by how handy Erlang's pattern matching facilities were. For example, using a single "=" operator, you can do multiple things like check that a status code equals "ok", assert that a list contains at least two items, and extract the first item from that list:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;{ok, [H|_|_]} = foo().&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you've only ever used languages where "=" means assignment, this statement may look a bit strange. I won't get into how it all works here, since it's a big topic, but I will say that once you get the hang of it, it's hard to go back.&lt;br /&gt;&lt;br /&gt;From a language perspective, Erlang's pervasive use of pattern matching is probably what differentiates it most from some of the more mainstream programming languages. Patterns are used everywhere, including function parameters, case/if statements, and when receiving messages from other processes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tuples&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Being able to create new data structures on the fly, without having to define a new class, is a big time saver. For example, to create a value representing an item and its quantity, you can just do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Item = {"Carrots", 3}.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;These types of structures are a pain to deal with in Java, since you need to create an entirely new class. It's easy to blame static typing for this, but the .NET folks can do something similar, thanks to &lt;a href="http://msdn2.microsoft.com/en-us/library/bb397696.aspx"&gt;anonymous types&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So, I've yet to hit any major roadblocks. That's not to say that Erlang isn't without its quirks - the error messages it spits out can be quite cryptic, and the way it deals with strings seems clumsy. But so far no showstoppers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4752270077823643444?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4752270077823643444/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4752270077823643444" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4752270077823643444?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4752270077823643444?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/5So5viEK4jQ/erlang-initial-thoughts.html" title="Erlang: Initial Thoughts" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2007/11/erlang-initial-thoughts.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMEQ3w8eCp7ImA9WB9VFEQ.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-3927164888751066751</id><published>2007-01-29T12:00:00.000-06:00</published><updated>2007-12-01T01:16:42.270-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-01T01:16:42.270-06:00</app:edited><title>JRake and Raven</title><content type="html">&lt;p&gt; Just a quick note to let everyone know that Matthieu Riou (creator of Raven) and I have decide to join forces,   and are currently in the process of merging the JRake and Raven codebases. The work is being done in the &lt;a href="http://rubyforge.org/projects/raven/"&gt;Raven&lt;/a&gt; repository over at Rubyforge,   for those of you who would like to follow along. The combined effort will eventually become Raven 2.0. &lt;/p&gt; &lt;p&gt; Matthieu has a few more &lt;a href="http://mriou.wordpress.com/2007/01/29/jrake-and-raven-are-now-one/"&gt;details&lt;/a&gt; over at his blog, so check it out. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-3927164888751066751?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/3927164888751066751/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=3927164888751066751" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/3927164888751066751?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/3927164888751066751?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/FzvNCn6NHYw/jrake-and-raven.html" title="JRake and Raven" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2007/01/jrake-and-raven.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMEQ3w8eSp7ImA9WB9VFEQ.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-1472561133929306748</id><published>2007-01-08T12:00:00.000-06:00</published><updated>2007-12-01T01:16:42.271-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-01T01:16:42.271-06:00</app:edited><title>JRake, Part 4: Multithreading</title><content type="html">&lt;p&gt; First off, I'd like to thank everyone for all the feedback I've received  so far regarding JRake. It's been extremely helpful - please keep it coming. &lt;/p&gt; &lt;p&gt; One of the things I should probably clarify is that JRake,   in its current form,   is not really a standalone tool that people should be considering for real world use. It's a series of spikes, meant to kick JRuby's tires and to test out some ideas I've had for how to build a better build tool. I decided to blog about it as I was pleasantly surprised by how well it all worked,  and thought others might be interested in seeing the types of things you can now do with JRuby (it's come a long way). &lt;/p&gt;  &lt;p&gt; My hope is that JRake eventually &lt;b&gt;will&lt;/b&gt; evolve into a production-quality tool,  either as a standalone codebase or part of another project,  but we're not there yet. In the mean time, all the features I mention here should be considered "experimental". &lt;/p&gt;  &lt;p&gt; With that in mind, let's check out the next hack:  &lt;/p&gt;  &lt;h3&gt;Parallel JRake&lt;/h3&gt; &lt;p&gt; In the previous posts, we tackled performance by eliminating the number JVM startups during the build. In the end, we got that number down to zero, so we can't really follow that line of attack much further. &lt;/p&gt;  &lt;p&gt; The next logical step   (assuming we don't want to start tearing apart the actual &lt;code&gt;javac&lt;/code&gt; code)  is to start executing tasks in parallel, where possible. This won't help folks running on single-processor/single-core machines,  and in practice won't really help folks who just need to recompile one or two files;  but there's a potential big win for those doing clean builds on multi-processor/multi-core machines. &lt;/p&gt;  &lt;p&gt; The idea is not new,   and most existing build tools support at least some sort of parallel processing. For example, &lt;code&gt;ant&lt;/code&gt; has a &lt;a href="http://ant.apache.org/manual/CoreTasks/parallel.html"&gt;parallel&lt;/a&gt;   task that can be used to execute subtasks in parallel,   and (standard) &lt;code&gt;rake&lt;/code&gt; has a &lt;a href="http://rake.rubyforge.org/classes/Rake/MultiTask.html"&gt;multitask&lt;/a&gt; task. These are examples of &lt;b&gt;explicit parallelism&lt;/b&gt;,  as the user must declare exactly which tasks are to be run in parallel. &lt;/p&gt;  &lt;p&gt; The alternative is &lt;b&gt;implicit parallelism&lt;/b&gt;, which is supported by tools like &lt;code&gt;make&lt;/code&gt;  (via its &lt;code&gt;-j&lt;/code&gt; option, explained  &lt;a href="http://www.gnu.org/software/autoconf/manual/make/Parallel.html"&gt;here&lt;/a&gt;). In this case, the tool uses the dependencies specified in the build script to automatically infer  which steps can be run in parallel. For example, consider a simple client/server application with the following build targets: &lt;/p&gt;  &lt;p style="padding: 8px; text-align: center;"&gt;  &lt;img src="http://blog.foemmel.com/images/parallel1.png" align="middle" /&gt; &lt;/p&gt;  &lt;p&gt; Here we have a "test" target, which depends on both the "client" and "server" targets, which in turn depend on the "common" target. There is no direct dependency between the "client" and "server" targets, however. A build tool that supports implicit parallelism will note this, and build those targets in parallel if it can. This can lead to big improvements in build times, depending on the complexity of the build script (more targets equals more chances for parallelism) and the type of computer its running on (the more processors the better). &lt;/p&gt;  &lt;p&gt; Of the two, I believe implicit parallelism is the way to go.  Multicore machines are becoming the standard,  and the number of developers who will be able to take advantage of parallel builds is  going to increase quite a bit over the next few years,  so having that be the default makes sense. Plus it will be easier to bake it in from the start, than to try and tack it on later. &lt;/p&gt;  &lt;p&gt;  &lt;/p&gt;&lt;h3&gt;Implementation&lt;/h3&gt;  &lt;p&gt; To get it working, I ended up bypassing the normal Rake methods for determining execution order,  and created my own &lt;code&gt;TaskCoorindator&lt;/code&gt; class. It uses a pool of worker threads to handle task execution,  while the main thread does the work of figuring out which tasks to submit to the pool. The worker threads use a &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html"&gt;blocking queue&lt;/a&gt;  to communicate the results of each task execution back to the main thread. &lt;/p&gt;  &lt;p&gt; Here's the &lt;code&gt;TaskCoordinator&lt;/code&gt; code  (for whatever subset of you happen to be familiar with both JRuby/Rake and the &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-summary.html"&gt;java.util.concurrent&lt;/a&gt; package): &lt;/p&gt;  &lt;pre class="ruby"&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;TaskCoordinator&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;pool_size&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@thread_pool&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;util&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;concurrent&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Executors&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;pool_size&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;execute&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;root&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@result_queue&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;util&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;concurrent&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;LinkedBlockingQueue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@leafs&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Set&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt; &lt;span class="comment"&gt;# tasks with no prereqs&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@children&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{}&lt;/span&gt; &lt;span class="comment"&gt;# maps task -&gt; tasks it depends on &lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@parents&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{}&lt;/span&gt; &lt;span class="comment"&gt;# maps task -&gt; tasks that depend on it&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="ident"&gt;analyze_dependencies&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;root&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="attribute"&gt;@leafs&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;leaf&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;execute_task&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;leaf&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="comment"&gt;# Keep pulling task results from the queue, until we get to the root target&lt;/span&gt;&lt;br /&gt;   &lt;span class="comment"&gt;# Remove non-root targets from the dependency graph&lt;/span&gt;&lt;br /&gt;   &lt;span class="comment"&gt;#   (i.e. delete the relevent members in the children/parent hash maps)&lt;/span&gt;&lt;br /&gt;   &lt;span class="comment"&gt;# If this results in any tasks with no children (i.e. no more prereqs to execute)&lt;/span&gt;&lt;br /&gt;   &lt;span class="comment"&gt;#   then submit that task to the thread pool for execution.&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;while&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;result&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@result_queue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;take&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="punct"&gt;!=&lt;/span&gt; &lt;span class="ident"&gt;root&lt;/span&gt;&lt;br /&gt;     &lt;span class="attribute"&gt;@parents&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;result&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;posttask&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;       &lt;span class="ident"&gt;list&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@children&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;posttask&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;       &lt;span class="ident"&gt;list&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;delete&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;result&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;       &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;list&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;&lt;br /&gt;         &lt;span class="attribute"&gt;@children&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;delete&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;posttask&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;         &lt;span class="ident"&gt;execute_task&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;posttask&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;       &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;     &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;     &lt;span class="attribute"&gt;@parents&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;delete&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;result&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;shutdown&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@thread_pool&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;shutdown&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;private&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="comment"&gt;# Stores the relationship between the tasks involved in this &lt;/span&gt;&lt;br /&gt; &lt;span class="comment"&gt;# build, using the "children" and "parents" hash tables&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;analyze_dependencies&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;analyzed&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="constant"&gt;Set&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;analyzed&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;member?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="ident"&gt;analyzed&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;task&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="ident"&gt;prereqs&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;prerequisite_tasks&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;prereqs&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;&lt;br /&gt;     &lt;span class="attribute"&gt;@leafs&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;task&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;prereqs&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;child&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;       &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@children&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="constant"&gt;Set&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;child&lt;/span&gt;&lt;br /&gt;       &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@parents&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;child&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="constant"&gt;Set&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;task&lt;/span&gt;&lt;br /&gt;       &lt;span class="ident"&gt;analyze_dependencies&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;child&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;analyzed&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;     &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="comment"&gt;# Wraps the task in a TaskRunner and passes it to the&lt;/span&gt;&lt;br /&gt; &lt;span class="comment"&gt;# thread pool for execution&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;execute_task&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@thread_pool&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;execute&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;TaskRunner&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@result_queue&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="constant"&gt;TaskResult&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Struct&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:task&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:exception&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;TaskRunner&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lang&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Runnable&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="symbol"&gt;:task&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;task&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;queue&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;super&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="attribute"&gt;@task&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;task&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@queue&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;queue&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@exception&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;run&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;begin&lt;/span&gt;&lt;br /&gt;     &lt;span class="attribute"&gt;@task&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;execute&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="constant"&gt;Exception&lt;/span&gt; &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="ident"&gt;exception&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;exception&lt;/span&gt;&lt;br /&gt;     &lt;span class="attribute"&gt;@exception&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;exception&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;ensure&lt;/span&gt;&lt;br /&gt;     &lt;span class="attribute"&gt;@queue&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;add&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;TaskResult&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@task&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@exception&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt; &lt;/pre&gt;      &lt;h3&gt;Sample Output&lt;/h3&gt; &lt;p&gt; I set up a simple build script using the client/server dependency graph shown above. The following output demonstrates how the build script runs when we only use &lt;b&gt;one&lt;/b&gt; thread:  &lt;/p&gt;&lt;pre&gt;[pool-1-thread-1] common - begin&lt;br /&gt;[pool-1-thread-1] common - end&lt;br /&gt;[pool-1-thread-1] client - begin&lt;br /&gt;[pool-1-thread-1] client - end&lt;br /&gt;[pool-1-thread-1] server - begin&lt;br /&gt;[pool-1-thread-1] server - end&lt;br /&gt;[pool-1-thread-1] test - begin&lt;br /&gt;[pool-1-thread-1] test - end&lt;br /&gt;&lt;br /&gt;real    0m3.230s&lt;br /&gt;user    0m0.007s&lt;br /&gt;sys     0m0.013s&lt;br /&gt;&lt;/pre&gt;  Just as we would expect: the tasks are executed serially and there is no overlap.   &lt;p&gt; Below is the output when running with &lt;b&gt;two&lt;/b&gt; threads: &lt;/p&gt;  &lt;pre&gt;[pool-1-thread-2] common - begin&lt;br /&gt;[pool-1-thread-2] common - end&lt;br /&gt;[pool-1-thread-1] client - begin&lt;br /&gt;[pool-1-thread-2] server - begin&lt;br /&gt;[pool-1-thread-2] server - end&lt;br /&gt;[pool-1-thread-1] client - end&lt;br /&gt;[pool-1-thread-2] test - begin&lt;br /&gt;[pool-1-thread-2] test - end&lt;br /&gt;&lt;br /&gt;real    0m2.542s&lt;br /&gt;user    0m0.007s&lt;br /&gt;sys     0m0.012s&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt; As you can see, the "client" and "server" tasks overlap,  and the total build time is reduced.  &lt;/p&gt;  &lt;h3&gt;Danger&lt;/h3&gt;  &lt;p&gt; Unfortunately, Parallel JRake has the same problem as all multithreaded code: nondeterminism. With the exception of explicitly declared dependencies between tasks,  users can't control the order in which things get executed. &lt;/p&gt; &lt;p&gt;Using the example above, this means that sometimes "client" will get compiled first, sometimes "server" will get compiled first, and sometimes they will overlap. In theory, we shouldn't care. However, if there is actually &lt;b&gt;is&lt;/b&gt; some sort of dependency between the two tasks, but this hasn't been explicitly declared in the rakefile,  the build might succeed one time and fail the next. &lt;/p&gt; &lt;p&gt; There are couple ways that I can think of to lessen the impact of this, though it can't be completely eliminated. One way is to use &lt;a href="http://raven.rubyforge.org/use.html"&gt;Raven&lt;/a&gt; style tasks,  where the values for properties like "output directory" and "classpath" are automatically supplied by the tool itself  (by analyzing the tasks dependencies). That way, the only way to get things to compile is to declare your dependencies correctly. &lt;/p&gt;  &lt;p&gt; On top of this, it might make sense to introduce some nondeterminism even when running in single-threaded mode. For example, we could intentionally randomize whether we execute the "client" task or the "server" task first. That won't prevent bugs from occurring in the build script, but should help us catch them faster. &lt;/p&gt;  &lt;h3&gt;Source Code&lt;/h3&gt;  &lt;p&gt; Once again, I've made the complete project available via subversion, if anyone would like to play with it: &lt;/p&gt;  &lt;div class="svn"&gt;svn://svn.foemmel.com/blog/jrake/parallel&lt;/div&gt;  &lt;p&gt; To run the server, go to the project root directory and run: &lt;/p&gt; &lt;div class="cmd"&gt;bin/jraked&lt;/div&gt; &lt;p&gt; You can then build specific targets like this (you'll need to have &lt;code&gt;curl&lt;/code&gt; installed): &lt;/p&gt; &lt;div class="cmd"&gt;bin/jrake unit&lt;/div&gt; &lt;div class="cmd"&gt;bin/jrake clean&lt;/div&gt; &lt;p&gt; Alternatively, you can run builds from your browser, by visiting a URL like this: &lt;/p&gt; &lt;div class="cmd"&gt;&lt;a href="http://localhost:3030/unit"&gt;http://localhost:3030/unit&lt;/a&gt;&lt;/div&gt; &lt;div class="cmd"&gt;&lt;a href="http://localhost:3030/clean"&gt;http://localhost:3030/clean&lt;/a&gt;&lt;/div&gt;  &lt;h3&gt;Other Changes&lt;/h3&gt;  &lt;p&gt; I've also made a few other tweaks to JRake since my last post, in case anyone is interested:  &lt;/p&gt;&lt;ul&gt;&lt;li&gt;   &lt;p&gt;   You can now send an HTTP request to the JRake server to build any target,    using the URL syntax: &lt;code&gt;http://localhost:3030/&lt;i&gt;targetname&lt;/i&gt;&lt;/code&gt;.   The build log will be sent back to the client as &lt;code&gt;text/plain&lt;/code&gt;    so you can just use &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt; to run builds.   This eliminates the need for the JRake Shell.   &lt;/p&gt;  &lt;/li&gt;&lt;li&gt;   &lt;p&gt;    The autoreload servlet will now only perform a reload when the use hits "refresh" in their browser.    A "hard refresh" (&lt;code&gt;ctrl-F5&lt;/code&gt;) is required in IE, and it doesn't work at all with Safari.    So it still needs some work...   &lt;/p&gt;  &lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-1472561133929306748?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/1472561133929306748/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=1472561133929306748" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/1472561133929306748?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/1472561133929306748?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/yLZ506IZI8s/jrake-part-4-multithreading.html" title="JRake, Part 4: Multithreading" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2007/01/jrake-part-4-multithreading.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMEQ3w8eSp7ImA9WB9VFEQ.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-7044604051376787858</id><published>2006-12-18T12:00:00.000-06:00</published><updated>2007-12-01T01:16:42.271-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-01T01:16:42.271-06:00</app:edited><title>JRake, Part 3: Running</title><content type="html">&lt;p&gt;  So far, we've managed to &lt;a href="http://blog.foemmel.com/jrake/compiling.html"&gt;compile&lt;/a&gt;    and &lt;a href="http://blog.foemmel.com/jrake/testing.html"&gt;test&lt;/a&gt; our Java code using 100% JRuby.  Now we just need a way to run it. &lt;/p&gt; &lt;p&gt;  Assuming our application is web-based,   the obvious solution is to wrap it up in a &lt;code&gt;war&lt;/code&gt; file and deploy it into a container.  But as anyone who has worked on a large project knows,   creating and deploying &lt;code&gt;war&lt;/code&gt; files can be a painfully slow process.  Our original mission was to cut as much time as possible off our edit-compile-run cycle,   so this clearly isn't the right solution.  (We will need to generate &lt;code&gt;war&lt;/code&gt; files at some point, of course,   for deploying into production environments,   but we'll save that work for another day).  &lt;/p&gt;  &lt;p&gt;  Some app servers have an "autodeploy" feature that lets you deploy your app into an already running server,   which is a huge step in the right direction   - though things can still take a fair bit of time to deploy.  Even when autodeploy works,   it requires an extra alt-tab (command-tab for you mac users) over to the shell to kick off the build,   followed by an alt-tab over to the browser to see the results,    which wastes another second or two.  There's also timing issue:    if you hit "refresh" in the browser before the build and deploy have finished,   you won't see your changes   (or worse, you'll see &lt;em&gt;some&lt;/em&gt; of your changes).  So while autodeploy is definitely a big improvement, it's still not an ideal solution. &lt;/p&gt;  &lt;p&gt;  What we really want is for Java code to work the same way that dynamically typed languages do:   you change your code, hit "save", and view the results in the browser. &lt;/p&gt;  &lt;h3&gt;JRake Server&lt;/h3&gt;  &lt;p&gt; I think I've managed to achieve something close to this, by setting up a Jetty server to act as a kind of proxy for my main application. Whenever a request comes in, it performs the following steps: &lt;/p&gt;&lt;ol&gt;&lt;li&gt;      Calls out to the JRake script to compile any out-of-date code.    &lt;/li&gt;&lt;li&gt;      Creates a new classloader, and uses it to reload the application's main servlet class.    &lt;/li&gt;&lt;li&gt;      Creates an instance of the servlet and forwards the original HTTP request on to it.    &lt;/li&gt;&lt;/ol&gt;     &lt;p&gt; The upshot is that I can now make changes in my IDE,   and see those changes (almost) immediately in my browser. No new virtual machines are started up along the way,  no extra alt-tabs are needed,  and I don't have to worry about hitting the web page before the deploy is complete. These may seem like minor points,   but when you're doing this stuff eight hours a day,  those little annoyances can add up. &lt;/p&gt;  &lt;p&gt; Here's the code for the main program, which starts up the Jetty server: &lt;/p&gt;  &lt;pre class="ruby"&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;reload_handler&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;server&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;org&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;mortbay&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;jetty&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Server&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="number"&gt;3030&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;handler&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;ReloadHandler&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;lib/rakefile.rb&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:main_compile&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;tmp/main/&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;com.example.HelloWorldServlet&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;server&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;set_handler&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;handler&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;server&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;start&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Hit &lt;enter&gt; stop server&lt;span class="escape"&gt;\n\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt;&lt;br /&gt;&lt;span class="global"&gt;$stdin&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;readline&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;server&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stop&lt;/span&gt; &lt;/pre&gt;  &lt;p&gt; And the code for the reload handler: &lt;/p&gt;  &lt;pre class="ruby"&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rake&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;jrake&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;ReloadHandler&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;org&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;mortbay&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;jetty&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Handler&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="comment"&gt;# todo: Should extend AbstractHandler&lt;/span&gt;&lt;br /&gt; &lt;span class="comment"&gt;#   (need JRuby to support extending abstract classes)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;rakefile&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;target&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;classpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;classname&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;super&lt;/span&gt;&lt;span class="punct"&gt;()&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="attribute"&gt;@rakefile&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;rakefile&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@target&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;target&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@classpath&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;classpath&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@classname&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;classname&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;handle&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;target&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;request&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;dispatch&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;   &lt;br /&gt;   &lt;span class="keyword"&gt;begin&lt;/span&gt;&lt;br /&gt;     &lt;span class="comment"&gt;# Compile any out of date files&lt;/span&gt;&lt;br /&gt;     &lt;span class="constant"&gt;Rake&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;application&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;clear&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;load&lt;/span&gt; &lt;span class="attribute"&gt;@rakefile&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;target&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Rake&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;application&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lookup&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@target&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;     &lt;span class="keyword"&gt;raise&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;Target not found: &lt;span class="expr"&gt;#{@target}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;target&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;target&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;invoke&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;     &lt;span class="comment"&gt;# Load the servlet class, create an instance, and delegate to it&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;servlet_class&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;load_class&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@classpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@classname&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;     &lt;br /&gt;     &lt;span class="ident"&gt;servlet&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;servlet_class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new_instance&lt;/span&gt;     &lt;br /&gt;     &lt;span class="ident"&gt;param_types&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;to_java_array&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lang&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Class&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;br /&gt;         &lt;span class="ident"&gt;javax&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;servlet&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;ServletRequest&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;         &lt;span class="ident"&gt;javax&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;servlet&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;ServletResponse&lt;/span&gt;&lt;br /&gt;     &lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;method&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;servlet_class&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;get_method&lt;/span&gt;&lt;span class="punct"&gt;("&lt;/span&gt;&lt;span class="string"&gt;service&lt;/span&gt;&lt;span class="punct"&gt;",&lt;/span&gt; &lt;span class="ident"&gt;param_types&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;args&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;to_java_array&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lang&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Object&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;request&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;br /&gt;    &lt;span class="ident"&gt;method&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;invoke&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;servlet&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;     &lt;br /&gt;  &lt;br /&gt;   &lt;span class="keyword"&gt;rescue&lt;/span&gt; &lt;span class="constant"&gt;Exception&lt;/span&gt; &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="ident"&gt;e&lt;/span&gt;&lt;br /&gt;       &lt;span class="ident"&gt;trace&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{e.class}&lt;/span&gt;: &lt;span class="expr"&gt;#{e.message}&lt;/span&gt;&amp;lt;br/&gt;&lt;span class="expr"&gt;#{e.backtrace.join('&amp;lt;br/&gt;')}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt;&lt;br /&gt;       &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;writer&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;write&lt;/span&gt;&lt;span class="punct"&gt;("&lt;/span&gt;&lt;span class="string"&gt;&lt;br /&gt;         &amp;lt;html&gt;&lt;br /&gt;           &amp;lt;body&gt;&lt;br /&gt;             &amp;lt;pre&gt;{trace}&amp;lt;/pre&gt;&lt;br /&gt;           &amp;lt;/body&gt;&lt;br /&gt;         &amp;lt;/html&gt;&lt;br /&gt;       &lt;/span&gt;&lt;span class="punct"&gt;")&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="ident"&gt;request&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;handled&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;   &lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;start&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;stop&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;setServer&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;server&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@server&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;server&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;getServer&lt;/span&gt;&lt;br /&gt;   &lt;span class="attribute"&gt;@server&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt; &lt;/pre&gt;  &lt;p&gt; Again, I've set up a sample project that includes all the pieces - JRuby, Rake, Jetty, and associated scripts. You can get it from subversion here: &lt;/p&gt;  &lt;div class="svn"&gt;svn://svn.foemmel.com/blog/jrake/running&lt;/div&gt;  &lt;p&gt; If you're interested in the scripts but don't want to download all the third-party stuff, just check out the &lt;code&gt;lib&lt;/code&gt; directory: &lt;/p&gt;  &lt;div class="svn"&gt;svn://svn.foemmel.com/blog/jrake/running/lib&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-7044604051376787858?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/7044604051376787858/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=7044604051376787858" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/7044604051376787858?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/7044604051376787858?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/GTcnIu4Nqio/jrake-part-3-running.html" title="JRake, Part 3: Running" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2006/12/jrake-part-3-running.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMEQ3w8eSp7ImA9WB9VFEQ.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-6821596937708254290</id><published>2006-12-11T12:00:00.000-06:00</published><updated>2007-12-01T01:16:42.271-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-01T01:16:42.271-06:00</app:edited><title>JRake, Part 2: Testing</title><content type="html">&lt;p&gt; In my &lt;a href="http://blog.foemmel.com/jrake/compiling.txt"&gt;previous&lt;/a&gt; post I showed how to integrate JRuby/Rake with &lt;code&gt;javac&lt;/code&gt;,   in a way that eliminated the need to start up a new virtual machine for each compilation. This was fairly straightforward -   I used JRuby's high level &lt;a href="http://jruby.sourceforge.net/docs/java-overview.shtml"&gt;java integration&lt;/a&gt; to   load the compiler class from the system classloader,  then invoked its &lt;code&gt;compile&lt;/code&gt; method. &lt;/p&gt; &lt;p&gt; Next on my list was integration with JUnit,  which turned out to be a bit more complicated. I was able to load the main JUnit classes (e.g. &lt;code&gt;org.junit.runner.JUnitCore&lt;/code&gt;) using the same high level mechanism as before,   but for the actual &lt;em&gt;test&lt;/em&gt; classes,  I had to use a separate, non-system classloader.  That way I could specify the location of my test classes from within the rakefile      (as opposed to having to include those directories on the system classpath)  plus I could recompile and rerun my tests without having to start up a new virtual machine. &lt;/p&gt;  &lt;p&gt; In the end I got it working without too much trouble. The &lt;code&gt;junit&lt;/code&gt; method below takes two arguments:  the location of the directory containing the test classes,  and the classpath to use when running the tests. It then creates a new classloader for the specified classpath,   uses it to load the test classes,   and passes those test classes into JUnitCore for execution:  &lt;/p&gt;&lt;pre class="ruby"&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;junit&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;test_class_dir&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;class_path&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Append the test_class_dir to the class_path, if necessary&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;class_path&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;test_class_dir&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;class_path&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;member?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;test_class_dir&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Make sure test_class_dir has trailing slash&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;test_class_dir&lt;/span&gt;&lt;span class="punct"&gt;[-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;test_class_dir&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;test_class_dir&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Scan test_class_dir for test class files&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;class_names&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;&lt;br /&gt;&lt;span class="constant"&gt;FileList&lt;/span&gt;&lt;span class="punct"&gt;["&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{test_class_dir}&lt;/span&gt;/**/*Test.class&lt;/span&gt;&lt;span class="punct"&gt;"].&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;class_file&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;class_names&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;class_file&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;test_class_dir&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;class_file&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="ident"&gt;test_class_dir&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;.class&lt;/span&gt;&lt;span class="punct"&gt;'.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;gsub&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;/&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;fail&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;No test classes found&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;class_names&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Load the test classes via a new classloader&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;classes&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;load_classes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;class_path&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;class_names&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Run the tests&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;runner&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;org&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;junit&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;runner&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;JUnitCore&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;runner&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;add_listener&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;org&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;junit&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;internal&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;runners&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;TextListener&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;result&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;runner&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;run&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;to_java_array&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lang&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Class&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;classes&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;fail&lt;/span&gt;&lt;span class="punct"&gt;("&lt;/span&gt;&lt;span class="string"&gt;Unit tests failed&lt;/span&gt;&lt;span class="punct"&gt;")&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;result&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;was_successful&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;load_classes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;class_path&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;class_names&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Make sure directories have a trailing slash, otherwise URLClassLoader ignores them&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;class_path&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;element&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;element&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="constant"&gt;FileTest&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;directory?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;element&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="ident"&gt;element&lt;/span&gt;&lt;span class="punct"&gt;[-&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;!=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Convert classpath elements to URLs&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;urls&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;to_java_array&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;net&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;URL&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;class_path&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;element&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;net&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;URL&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;file:&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;element&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;})&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="comment"&gt;# Create a class loader for the specified class path&lt;/span&gt;&lt;br /&gt;&lt;span class="ident"&gt;loader&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;net&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;URLClassLoader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;urls&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;class_names&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;map&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;class_name&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;loader&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;loadClass&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;class_name&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt; &lt;/pre&gt;  &lt;p&gt; Unfortunately, I had to use the internal JUnit TextListener to display the results of the tests,  since JRuby doesn't currently support extending existing Java classes in such a way  that I could provide my own subclass of &lt;a href="http://junit.sourceforge.net/javadoc_40/org/junit/runner/notification/RunListener.html"&gt;RunListener&lt;/a&gt;. This will hopefully be fixed in a future version of JRuby. &lt;/p&gt;  &lt;p&gt; &lt;b&gt;Interactive JRake&lt;/b&gt; &lt;/p&gt;  &lt;p&gt; Now, while all these classloading tricks are nice,  they don't really help if all we do is start up the virtual machine, run the tests, shutdown, and repeat,  since the original goal was minimize those startups. I've therefore created an "interactive" version of JRake,  to keep the virtual machine warmed up between tests. All it does is sit in a loop, prompting the user for targets to build, and building them. A sample session looks like this:  &lt;/p&gt;&lt;pre class="ruby"&gt;jrake shell v0.0&lt;br /&gt;&lt;br /&gt;Valid targets:&lt;br /&gt; clean        - deletes all generated files&lt;br /&gt; main_compile - compiles the main code&lt;br /&gt; unit_compile - compiles the unit test code&lt;br /&gt; unit_test    - runs the units tests&lt;br /&gt; help         - displays this help text&lt;br /&gt; exit         - quits the application&lt;br /&gt;&lt;br /&gt;A blank line repeats the previous command.&lt;br /&gt;&lt;br /&gt;jrake&gt; &lt;em&gt;&lt;strong&gt;main_compile&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;compiling 1 java file(s)...done&lt;br /&gt;jrake&gt; &lt;em&gt;&lt;strong&gt;unit_test&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;compiling 1 java file(s)...done&lt;br /&gt;.&lt;br /&gt;Time: 0.009&lt;br /&gt;&lt;br /&gt;OK (1 test)&lt;br /&gt;&lt;br /&gt;jrake&gt; &lt;em&gt;&lt;strong&gt;clean unit_test&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;deleting 10 file(s)...done&lt;br /&gt;compiling 1 java file(s)...done&lt;br /&gt;compiling 1 java file(s)...done&lt;br /&gt;.&lt;br /&gt;Time: 0.002&lt;br /&gt;&lt;br /&gt;OK (1 test)&lt;br /&gt;&lt;br /&gt;jrake&gt; &lt;em&gt;&lt;strong&gt;exit&lt;/strong&gt;&lt;/em&gt; &lt;/pre&gt;   &lt;p&gt; You can download the entire sample project via subversion here: &lt;/p&gt;  &lt;div class="svn"&gt;svn://svn.foemmel.com/blog/jrake/testing&lt;/div&gt;  &lt;p&gt; Just run the "build.bat" or "build.sh" scripts with no arguments to bring up the JRake Shell. If any arguments are passed in, the behavior is the same as before i.e. the targets will be built then the script will terminate. &lt;/p&gt;  &lt;p&gt; &lt;b&gt;Next Steps&lt;/b&gt; &lt;/p&gt; &lt;p&gt; Now we just need to get the app up and running... &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-6821596937708254290?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/6821596937708254290/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=6821596937708254290" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/6821596937708254290?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/6821596937708254290?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/u4L1ZIWqebs/jrake-part-2-testing.html" title="JRake, Part 2: Testing" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2007/12/jrake-part-2-testing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMEQ3w8eip7ImA9WB9VFEQ.&quot;"><id>tag:blogger.com,1999:blog-1169663570013341555.post-4453196752538180115</id><published>2006-12-06T12:00:00.000-06:00</published><updated>2007-12-01T01:16:42.272-06:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-12-01T01:16:42.272-06:00</app:edited><title>JRake, Part 1: Compiling</title><content type="html">&lt;p&gt; For as long as I've been working with Java, I've been in search of a build tool that didn't drive me, and those around me, bat-shit crazy. I've come to realize I have two somewhat conflicting requirements for such a tool: &lt;/p&gt;&lt;ol&gt;&lt;li&gt;    &lt;p&gt;     &lt;b&gt;Must be based on a scripting language&lt;/b&gt;    &lt;/p&gt;    &lt;p&gt;     Builds get complicated,      and I need a tool that will let me do arbitrarily complex things,      preferably in a well-known scripting language with lots of supporting libraries.     &lt;/p&gt;   &lt;/li&gt;&lt;li&gt;    &lt;p&gt;     &lt;b&gt;Must run on the Java platform&lt;/b&gt;    &lt;/p&gt;    &lt;p&gt;     The Java virtual machine takes time to start up.     Since most tools that deal with Java code (e.g. &lt;code&gt;javac&lt;/code&gt;) are themselves written in Java,      this means there is a rather annoying interval between when you invoke them,       and when they start doing actual work.     This delay may seem insignificant,      but on a large project where a build consists of many calls to many tools and gets run many times a day,      all that wasted time can really harsh your flow.    &lt;/p&gt;    &lt;p&gt;     The solution is for the build tool to treat all of the supporting tools as libraries, not command line applications.     For example, instead of calling &lt;code&gt;javac&lt;/code&gt; directly,      just invoke the method &lt;code&gt;com.sun.tools.javac.Main.main(String[])&lt;/code&gt;.     That way you only incur the VM startup time once.     &lt;/p&gt;   &lt;/li&gt;&lt;/ol&gt;   &lt;p&gt; The tools I've used in the past don't meet these requirements, of course. Specifically:  &lt;/p&gt;&lt;ul&gt;&lt;li&gt;   &lt;p&gt;    &lt;code&gt;make&lt;/code&gt; fails on both counts (along with most other tools).   &lt;/p&gt;  &lt;/li&gt;&lt;li&gt;   &lt;p&gt;    &lt;code&gt;ant&lt;/code&gt; fails on the first count, as it's not based an existing scripting language.    While it's true that you can drop down to Java and do complicated things by writing your own tasks,     the context switch is pretty jarring.    One problem is that you have the extra step of compiling your tasks before you compile your code      - and therefore your build script now needs a build script.    But the real issue is that Ant tasks only let you put custom logic at the task level,     not at the level that actually &lt;em&gt;manages&lt;/em&gt; the tasks.    For example, if you want to define some dependencies between projects in one place,      and use that to drive a bunch of build steps for each project,      you're in for a tough time.     It can be done (we used XSLT to generate ant scripts on one project) but it ain't pretty.   &lt;/p&gt;  &lt;/li&gt;&lt;li&gt;   &lt;p&gt;    &lt;code&gt;rake&lt;/code&gt;, running on top of standard C-Ruby, fails to meet the second requirement.    However, Rake itself is a damn nice tool.    See Martin Fowler's &lt;a href="http://www.martinfowler.com/articles/rake.html"&gt;article&lt;/a&gt; for a good summary.   &lt;/p&gt;  &lt;/li&gt;&lt;/ul&gt;    &lt;p&gt; &lt;b&gt;Rake and JRuby&lt;/b&gt; &lt;/p&gt;&lt;p&gt; The solution, obviously, is to run Rake on top of JRuby (I will henceforth call this two-headed monster "JRake"). That way we have the power of Ruby at our disposal, with all the snappy goodness of a JVM based tool.  The trick is to get JRake to invoke the main &lt;code&gt;javac&lt;/code&gt; class directly, without starting a new VM. This actually isn't much of a trick, since integrating with Java is exactly what JRuby was designed for. &lt;/p&gt;  &lt;p&gt; And so, after a bit of trial and error, here is a rakefile that does just that. It compiles any out-of-date java files in the "src" directory and puts the resulting class files in the "tmp" directory: &lt;/p&gt;  &lt;pre class="ruby"&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:default&lt;/span&gt; &lt;span class="punct"&gt;=&gt;&lt;/span&gt; &lt;span class="symbol"&gt;:compile&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="ident"&gt;task&lt;/span&gt; &lt;span class="symbol"&gt;:compile&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;src_dir&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;src&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;dest_dir&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;tmp&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="constant"&gt;Dir&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="ident"&gt;mkdir&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;dest_dir&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="ident"&gt;exist?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;dest_dir&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="ident"&gt;javac&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;src_dir&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;dest_dir&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;javac&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;src_dir&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;dest_dir&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;java_files&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;get_out_of_date_files&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;src_dir&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;dest_dir&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;java_files&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;&lt;br /&gt;   &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;compiling &lt;span class="expr"&gt;#{java_files.size}&lt;/span&gt; java file(s)...&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span class="ident"&gt;args&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;-d&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;dest_dir&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;*&lt;/span&gt;&lt;span class="ident"&gt;java_files&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span class="ident"&gt;buf&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;io&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;StringWriter&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;com&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;sun&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tools&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;javac&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Main&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;compile&lt;/span&gt;&lt;span class="punct"&gt;(&lt;br /&gt; &lt;/span&gt;&lt;span class="ident"&gt;to_java_array&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lang&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;String&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;args&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;io&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;PrintWriter&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;buf&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt; &lt;span class="punct"&gt;!=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;FAILED&lt;span class="escape"&gt;\n\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="ident"&gt;buf&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;fail&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Compile failed&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;   &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="punct"&gt;"&lt;/span&gt;&lt;span class="string"&gt;done&lt;span class="escape"&gt;\n&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;"&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;get_out_of_date_files&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;src_dir&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;dest_dir&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;java_files&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;&lt;br /&gt; &lt;span class="constant"&gt;FileList&lt;/span&gt;&lt;span class="punct"&gt;["&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="expr"&gt;#{src_dir}&lt;/span&gt;/**/*.java&lt;/span&gt;&lt;span class="punct"&gt;"].&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;java_file&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;&lt;br /&gt;   &lt;span class="ident"&gt;class_file&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;dest_dir&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;java_file&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;src_dir&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;java_file&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="ident"&gt;src_dir&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt; &lt;span class="punct"&gt;-&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;.java&lt;/span&gt;&lt;span class="punct"&gt;'.&lt;/span&gt;&lt;span class="ident"&gt;length&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;.class&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;&lt;br /&gt;  &lt;br /&gt;   &lt;span class="comment"&gt;# todo: figure out why File.ctime doesn't work&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;exist?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;class_file&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt; &lt;span class="punct"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;io&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;class_file&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;lastModified&lt;/span&gt; &lt;span class="punct"&gt;&gt;&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;io&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;java_file&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;lastModified&lt;/span&gt;&lt;br /&gt;     &lt;span class="ident"&gt;java_files&lt;/span&gt; &lt;span class="punct"&gt;&lt;&lt;&lt;/span&gt; &lt;span class="ident"&gt;java_file&lt;/span&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;java_files&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;to_java_array&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;element_type&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ruby_array&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;java_array&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;java&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;lang&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;reflect&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;Array&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;newInstance&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;element_type&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;ruby_array&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;&lt;br /&gt; &lt;span class="ident"&gt;ruby_array&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_index&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;java_array&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;ruby_array&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;i&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;java_array&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt; If you want to play around around with this script but can't be bothered to install JRuby, Rake, and the other bits,   I've created a complete "Hello World" project that includes everything you need. You can get it from subversion here: &lt;/p&gt;  &lt;div class="svn"&gt;svn://svn.foemmel.com/blog/jrake/compiling&lt;/div&gt;  &lt;p&gt; Just checkout the project, make sure JAVA_HOME points to your JDK, and run the "build" script to see it go. &lt;/p&gt;  &lt;p&gt; &lt;b&gt;Next Steps&lt;/b&gt; &lt;/p&gt;  &lt;p&gt; I've gotten JUnit and a few other cool things working with JRake, which I'll write about in my next post. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1169663570013341555-4453196752538180115?l=blog.foemmel.com' alt='' /&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.foemmel.com/feeds/4453196752538180115/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=1169663570013341555&amp;postID=4453196752538180115" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4453196752538180115?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1169663570013341555/posts/default/4453196752538180115?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/foemmel/~3/Dn4Fvh1JaQ4/jrake-part-1-compiling.html" title="JRake, Part 1: Compiling" /><author><name>Matthew Foemmel</name><uri>http://www.blogger.com/profile/08260613691359381017</uri><email>blog@foemmel.com</email><gd:extendedProperty name="OpenSocialUserId" value="12664346887912552930" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://blog.foemmel.com/2006/12/jrake-part-1-compiling.html</feedburner:origLink></entry></feed>
