Friday, June 09, 2006

Preparing for 0.9.0

Yes, I had intended to post at least once a week. Things have been moving along pretty fast with JRuby since JavaOne. Here's a quick update...I'll post a more detailed one soon.

  • RubyGems now installs perfectly, and works correctly. This is due in large part to Ola Bini's work on RbYAML and JvYAML, Ruby and Java YAML parsers which we are integrating into JRuby.
  • Gems install correctly from local files or from the network. They're a little slow, due to some bottlenecks we're ironing out, but they work. Ola's post shows it in action.
  • The "rails" script for generating a base application works. This is the first step in Rails development, and it ended up just kinda working without any additional effort.
  • Tom and I have been working on performance, putting in 5-10% speedup fixes here and there. There's a lot more to do, but we're making great progress.
  • We have gotten approval from matz to include the full complement of Ruby's .rb libraries in the next release of JRuby. This means all you will need for a working JRuby install is the release archive. We will also likely pre-install rubygems, so we're on par with a typical Ruby install in that regard.
  • Due to networking and IO fixes from Evan Buswell (especially his work to make 'select' function) we are very close to running Webrick in JRuby. Along with this would come the "server" script in Rails, which allows you a simple development deployment for Rails development and testing.
  • We will be making the ActiveRecord JDBC connector available to the public concurrent with this release. It is unknown yet whether it will be a gem, a Rails plugin, or whether we can get the Rails guys to include it in the main release.
  • A number of people are interested in beginning work to make Rake work well in JRuby for doing Java builds. I am one of those people, and time permitting I will start trying to make this happen.
  • I am also interested in implementing the win32ole library in JRuby. There are various plugins for Java that allow calling OLE components, and it should not be difficult to wrap one of those libraries. This would allow things like WATIR to work seamlessly in JRuby.

And here's a little funny that Tom just pointed out: Ed Burnette's JRuby Photo

Tom is on the left, looking into the future, and I'm on the right, staring down the camera.

Wednesday, May 24, 2006

JavaOne Wrap-up

It was a great JavaOne for JRuby! Now that Tom and I are back home and I've recuperated from a long and busy week, here's an update on the great JRuby JavaOne Adventure.

The Press Conference

Tim Bray continues to support and promote the JRuby project, along with other alternative JVM languages. He set up a press conference on Tuesday the 16th for us along with Sam Heisz from Caucho (presenting Quercus, Caucho's GPLed PHP implementation for the JVM) and Roberto Chinnici from Sun (presenting Phobos, a scripting-language-based web framework for JSR223-compatible languages). Sam presented first, then us, then Roberto.

My rough count puts the number of journalists and interested parties at 15-20 (some came in late or left early).

We were given ten minutes to make our case for JRuby. Tom kicked it off with a quick overview of what Ruby and JRuby are, and I made a quick statement about Ruby's potential on the JVM. Then I demoed building a tiny Swing app through IRB in under two minutes before handing it back to Tom for his JRuby on Rails demo.

I think both demos were well-received. With the press Ruby and Rails have been getting, JRuby tends to sell itself.

The inevitable questions about performance and business cases were mostly deflected by Tim, and for that I thank him. Comparing performance is somewhat pointless at this point in JRuby's development, since we've only recently begun to explore optimizations. The business case for JRuby is also simply the business case for Ruby; if the language suits your problem, JRuby is a good option.

There were also questions asking us to compare JRuby to the other languages and implementations out there. These we mostly punted on, avoiding a fight. Folks seem to really want the alternative languages implementors to fight, and this became a recurring theme during the week.

At the end of the press conference, a number of Sun folks talked to us about building a community around JRuby, and possibly having a presence on java.net. We're very enthusiastic about growing the JRuby community and will weigh the various options for some minimal java.net presence.

Presentation for InfoQ.com

InfoQ.com is a new online tech magazine currently launching. We were invited by Floyd Marinescu and Obie Fernandez to participate in the launch by presenting our JRuby talk, sans live demos and JavaOne branding, to be videotaped and published on the InfoQ site. This talk we gave on Wednesday the 17th at noon.

Floyd is one of the original founders of TheServerSide, and we were happy to help him kick off this new venture--especially given its broad acceptance of alternative development and web technologies like Ruby and Rails.

The presentation took place in the upstairs theater of the Thirsty Bear, a typical conference-goer's hangout near the Moscone convention center. The room was set up with rows of chairs, a projector, a podium, and a video camera. Upon starting, we had only a few attendees, but several more trickled in during our presentation. According to the InfoQ guys, we had the best turnout of any talks so far that morning.

A number of good questions came up, which Tom and I answered on camera. It was a great dry-run for our "real" JavaOne presentation later that day.

Look for the final presentation to be posted on the InfoQ site within the next week.

The Main Event

Some time before the InfoQ presentation I stopped practicing. I'm not one for scripted presentations; I do better with a general presentation outline and a couple practice rehearsals. I approached my RubyConf presentation this way and it worked out well, so I followed the same pattern for JavaOne.

In total, I think I rehearsed my part of the presentation only 4 times. I ran through the IRB demo many times, however, to make sure I had the sequence of commands memorized.

All that said, I spent the early afternoon relaxing. I hung out in the pavilion for a while, relaxed in the gaming area, and then attended the Groovy talk around 2:45. The basics of the Groovy presentation seemed much like in past years. The slides were mostly the same, and the content seemed to vary only slightly. There was an extensive demo using ActiveX to control an Excel document; I was not quite sure whether this was intended to be a Groovy demo or an ActiveX demo, since the same operations could be done by Java or any other scripting languages just as easily. At any rate, it was a good prep for our presentation which came immediately after.

I was obviously there early, and Tom showed up well ahead of time as well. We got mic'ed up, talked a bit on how ready we were, and then relaxed while folks streamed into the auditorium. I estimate it was perhaps 70% full, for around 700 people, but we'll get an official count sometime soon.

We decided on the following order for the presentation:

Introductions and opening words: Me
Agenda, intro to JRuby and Ruby: Tom
IRB demo: Me
Rails demo: Tom
JRuby in the Wild, JRuby's Future, and Conclusion: Me
Q/A: Tom and Me

Up until Wednesday morning, I had no plans for the introduction. I knew I wanted to ask a few introductory questions, and eventually settled on the following two:

- How many of you have heard of Ruby or Ruby on Rails? (Perhaps 75-80% of the attendees raised their hands
- How many of you heard of them for the first time in the past year? (Almost all the same hands)

"That's momentum. That's the momentum behind Ruby and Rails right now".

I proceeded to hold up the current Dr Dobbs Journal, with a cover story "Ruby on Rails: Java's Successor?". Most of the audience chuckled at the title, and I agreed that perhaps it wasn't the right question. I mentioned that the article described Rails as a potential tipping point in web development patterns and called out the fact that several new frameworks had sprung up in the Java world aping Rail's design. Then I handed off to Tom.

Tom gave a very quick and clean introduction to Ruby, JRuby, and features of both. I've read on several blog postings about the presentation that this intro was welcomed by attendees; though many had heard about Ruby, most had no experience with the code. The JRuby features also called out Java integration scenarios, which are the obvious reason why you'd want to run JRuby in the first place. Tom did a great job on these slides, which you can imagine were not terribly exciting for us to present. Then it was my turn.

The IRB demo was our first really cool thing to show off. I started off demonstrating some of the basic of Ruby and IRB, entering basic statements, defining a method, defining a class. Then I went straightaway into the fun stuff: building that little Swing app. Tom commented after the press conference and again after this presentation how impressive he thought it was, and there were certainly murmurs, gasps, and nods from the attendees. It really provided a good feel for how easy it is to call Java code from Ruby.

Tom came next, providing the "big surprise" for JavaOne attendees: a fully working Rails app, running under JRuby and using JDBC for database access. The wow factor was not as apparent here, since it just looked like a web application; however, several folks in the audience with more than a basic knowledge of Rails were notably impressed. Through follow-up questions and later blog posts, the gravity of this milestone has begun to be felt.

I gave a quick overview of RDT, RadRails, JEdit, and DataVision, four apps that make use of JRuby internals. Then I ran through the various plans and future milestones for JRuby, adjusting them based on recent developments (since our original slides were submitted in March, and mountains have moved since then).

We finished with about fifteen minutes to go, which I think was refreshing for most of the attendees. We covered everything we wanted to cover, and we knew people were having difficulty moving from session to session. We also wanted plenty of time for Q/A.

Questions ranged from performance to rails to "can I run normal Ruby code" to "can I use Java objects in Ruby scripts". We patiently answered all questions as well as possible; there was a lot of interest in using JRuby for its Java integration abilities.

Both Tom and I thought the presentation went extremely well. We were very happy with the turnout and we're excited to take the next steps with JRuby.

Sunday, May 14, 2006

JRuby Meet-up, San Francisco, May 19 4:00PM

UPDATE: The correct date is May 19th

After a crazy week at JavaOne, Tom and I will be settling in at the Chieftain in San Francisco to unwind. We'd love to have any JRuby enthusiasts stop by and chat a bit. Tom's blog has all the details. RSVP if you like, so we we'll know to look for you, but show up regardless!

Friday, May 12, 2006

And They Said JRuby was Dead...

Things are looking very good for JavaOne.

The past several months have been a period of furious development on the JRuby project, with milestone after milestone flying by. Four months ago we couldn't run most basic Ruby apps and were still wrestling with basic 1.8 compatibility issues. Since that time we've gotten IRB working, Rake running, RubyGems almost working, and then managed to process a simple Rails request. All the while, our existing libraries have grown in stability and completeness, and we've attracted many new contributors to the project. This stone is gathering no moss.

JavaOne is a going to be a big event for us, but we wanted something more. What would the week before a major conference be without some buzz-worthy announcements to make?

Hold on to your hats.

Where We've Been

JRuby has been around for around five years. It was originally based on Ruby 1.6 C code, and was pretty much a direct port from C to Java. Over those years, up until Fall of 2005, the focus was mainly on basic compability issues, updating libraries and language semantics to 1.8 levels, and generally just making things work well enough for use. All told, it was a pretty good port, but there was a long way to go. There were a few Java apps that used it for scripting, but the tide was turning away from JRuby with the release of ever-more-complex Ruby applications that were increasingly difficult to run on a partial implementation.

Starting this past Fall, and really gaining steam since the start of 2006, our focus shifted from simple 1.8 compatibilities and interpreter tweaks to much more ambitious goals. We began to tackle the killer apps for Ruby one by one, fixing what broke and getting a much more intimate knowledge of both Ruby and JRuby internals than we had before. First came early runs of Rake. They worked, but required changes to the way external scripts launched. Then something a bit more lofty: I disappeared into my office for a week to get IRB working...and work it eventually did. Then we realized that RubyGems would be a great target; while it's not yet working 100%, it's now really close.

Then came Rails.

The Big Ticket

There is little doubt that Rails is the app that's selling Ruby today. Ruby has become, in many circles, synonymous with Ruby on Rails. While there's a discussion to be had about whether a single killer app should hold so much sway, it's impossible to argue that Ruby would be where it is today without Rails dragging it into the spotlight. Rails has, more than any other app, brought Ruby to the world.

We just had to get a bite of that apple.

Getting Rails to run in JRuby is a very challenging task. For those of you that know what Rails does, imagine the code that's required to do that. Think of all the magic necessary to get things so slick and transparent. Look at the size of the Rails codebase which, while far smaller than a beastly Java library, is positively enormous compared to the typical Ruby app. If you've ever seen the code, you know what kinds of tricks and traps there are in the Rails code, and how many dark alleys you might venture down when debugging a problem deep within its guts.

Now imagine running it on an interpreter that breaks in weird and indeterminate ways.

We have fixed hundreds of issues on the road to getting Rails working. Every step of the way, we have had to pore through the Rails code, digging for that one irreducible test case that demonstrates a bug, sometimes sifting through tens of files, thousands of lines of code. It has been an extreme challenge. However...

What Would You Do With JRuby on Rails?

Today, we can announce that a simple end-to-end Rails application has successfully run under JRuby.

As reported on Tom's blog, a little CRUDified cookbook application can now deliver a form and process submissions correctly. Using an ActiveRecord-to-JDBC adapter (yes, you read that right) donated by Nick Sieger, the app is able to generate a form, display a table of results, and process creates and deletes. All this magic, all working on JRuby. We're ecstatic.

Can It Truly Be?

See for yourself. It's even wrapped in a little servlet that invokes Rails FastCGI-style, keeping it loaded in memory for additional requests. It's the culmination of all our experiments, bug fixes, and hard work over the past several months. And it sure feels good.



Onward and Upward

Of course this demo is slightly canned. We coded to the test, and the average Rails app still isn't going to work out of the box. There's plenty of little things missing, like reliable session management, more ActiveRecord magic, and various rendering flaws, to list a few we know about. But as a technology preview, running Rails essentially unmodified--even on a simple end-to-end app--is our biggest milestone yet.

With the recent successes, we plan to target the end of summer for full, general-purpose Rails support. With more contributors and more time to work, that timetable could move up, but there's also the compiler work that has begun in earnest. It's going to be a great summer for coding, and a great year for dynamic(-typed) lanaguages on the JVM.

Oh, and one last note for the perennial skeptics and naysayers out there: Rails on the JVM is coming sooner than you think.

A Few Other Updates

Here's another couple items to hold you off until the next round of milestones:

IRB glitches resolved
A remaining IRB bug that caused declared classes to be scoped under Kernel (as in class A would be scoped as Kernel::A) has been resolved. I had to get this fixed to demo IRB at JavaOne

RubyGems works, sorta
Tom discovered that if a known good RubyGems install (from a C Ruby installation, for example) was copied into JRuby's dirs, gems would require and load correctly. So although our RubyGems and gem install code isn't quite there yet, actually using gems seems to be working. Huzzah!

Improved codegen DSL
John Lam, of the RubyCLR project, pointed me to a nice little eval trick that has helped simplify my code-generation DSL a bit. By eliminating most of the block params, it's a bit less verbose and certainly prettier:

class_bytes = ClassBuilder.def_class :public, "FooClass" do
def_constructor :public do
call_super
return_void
end

def_method :public, :string, "myMethod", [:void], [:exception] do
call_this GenUtils.array_cls(:string), "getStringArr"
call_this :string, "getMessage"
return_top :ref
end

def_method :private, :string, "getMessage", [:void] do
construct_obj :stringbuffer, [:string] do
constant "Now I will say: "
end

call_method :stringbuffer, "append", :string, :stringbuffer do
constant "Hello CodeGen!"
end
call_method :string, "toString", :void, :stringbuffer
return_top :ref
end

def_method :public, GenUtils.array_cls(:string), "getStringArr" do
construct_array :string, 5 do |i|
constant "string \##{i}"
end

array_set 2, :string do
constant "replacement at index 2"
end

return_top :ref
end
end
I'll be going full-speed-ahead on compilation after JavaOne, you can count on that.

YAML parser improved?
One of our favorite contributors, Ola Bini, now has a working version of a YAML 1.1-compliant parser which is able to load fairly large files. There's still some work to do, but if all goes well we may be able to migrate to it in the near term.

Dynamic languages press conference
The JRuby team (Tom and I) will be part of a JavaOne Day-1 press conference on the future of dynamic languages on the JVM. Along with the Quercus guys from Caucho (PHP for the JVM) and Roberto Chinnici from Sun (doing cool stuff with JSR223), we'll have our own ten minutes to show the press how cool JRuby really is. It oughta be a fun time, and should help get JRuby a bit wider exposure.

This is just too much fun.

Scripting and services top Sun software chief's list

Scripting and services top Sun software chief's list:

"Support for scripting languages in Java, squeezing revenue from a set of long-promised - but strangely MIA - services, and more open source initiatives are among top priorities for Sun Microsystems' newly appointed software chief.…"

Seems like a good time to be working on an open-source "scripting" language implementation, no?

Tuesday, May 09, 2006

A DSL for Bytecode Generation

Having discovered the power and magic of bytecode generation, it occurred to me that none of the existing libraries have the subtle elegance that most code generation tasks really deserve. I believe there's a simple reason for this: they're written in Java. Some of them really try to make things easier, and perhaps come close to succeeding, but they're all still cumbersome, clunky, and very, very verbose.

I have been writing JRuby's compiler in pure ruby by calling out to a Java-based bytecode generation library. My initial attempts were fairly straightforward calls: push this, call that, pop the other. Very linear, very boring, very verbose, and not a great deal simpler than the equivalent Java code. It seemed such a shame to waste an expressive language like Ruby on such a menial task, I've decided to build a domain-specific language for Java bytecode generation.

A short sample of what works today (basically the operations I needed for my test compiler):

class_bytes = ClassBuilder.def_class :public, "FooClass" do |c|
c.def_constructor :public do |con|
con.call_super
con.return_void
end

c.def_method :public, :string, "myMethod", [:void], [:exception] do |m|
m.call_this GenUtils.array_cls(:string), "getStringArr"
m.call_this :string, "getMessage"
m.return_top :ref
end

c.def_method :private, :string, "getMessage", [:void] do |m|
m.construct_obj :stringbuffer, [:string] do |p|
p.constant "Now I will say: "
end

m.call_method :stringbuffer, "append", :string, :stringbuffer do |p|
p.constant "Hello CodeGen!"
end
m.call_method :string, "toString", :void, :stringbuffer
m.return_top :ref
end

c.def_method :public, GenUtils.array_cls(:string), "getStringArr" do |m|
m.construct_array :string, 5 do |p,i|
p.constant "string \##{i}"
end

m.array_set 2, :string do |p|
p.constant "replacement at index 2"
end

m.return_top :ref
end
end


This approach has a number of advantages over others:

  • The structure of the generator is very similar to that of the generated code
  • Method parameters and array initializers (or the code to make them available) are logically associated with the eventual call or array they'll apply to
  • The builders maintain some internal state, and will be able to count stack depth, validate typing, automatically attempt casts, and automatically return the correct types
  • It's far easier to read
  • It's far more fun
Some notes on the code above:
  • The param-building blocks (with |p| params) are in all cases optional. If omitted, method calls will assume you have prepared all params, array creation will create an empty array, and array sets will assume the value is already present on the stack.
  • The core bytecode operations (dup, etc) are still present and callable on the MethodBuilder m. This allows you to fall back to linear-style when necessary.
  • Various classes (perhaps eventually all classes) from java.lang are aliased as symbols like :string and :object. At the ClassBuilder level, it is also possible to "import" classes, as in c.import "javax.swing.JFrame", :jframe and use the aliased symbol throughout this generation (much like import in a .java file)
  • I'm looking for a better way to handle arrays. GenUtils is only used internally except for array types, and I'd like to hide it completely.

I'm tossing this working snippit out to the world for comments and critique...and perhaps as a little teaser of things to come. I'm planning to add a few more operations and port the early v1 compiler over to this soon...then both will develop together. I see this DSL/library as having huge potential for other projects that want a simple, elegant way to do bytecode generation.

Thoughts? Comments?

My JavaOne Calendar

I've entered all the sessions I'm planning to attend into Google Calendar, available at the following URLS:

- RSS
- ICal
- You can search for "Headius at JavaOne" in GCal too.

These are negotiable if there's folks out there that want to grab a lunch, dinner, or 30 minutes and a cup of coffee. Do let me know: email

I'm arriving late Monday and leaving late Saturday...the rest is on the calendar.

Sunday, April 30, 2006

More v1 Compilation Experiments

Since it's turned out to be so easy to generate bytecodes based off the Ruby AST, I tackled another trouble spot for us: literal array creation.

The current interpreter, in order to follow a generic, iterative model, has a large overhead for instantiating literal arrays. For example, to create the following array:

['this', 'is', 'an', 'array', 'of', 'strings']

There are at least seven AST nodes to process: the array node and six nodes for the elements (technically, there's more that one node for each element, but we'll call it one for simplicity). The recursive way to process these nodes would be to visit the array node, and then recurse to process each of the elements. However, we're trying to escape recursion, so a different model was necessary.

The current interpreter avoids recursion by maintaining its location in the AST on a stack. As nodes are encountered, they are pushed onto that stack, and their instructions--rather than recursing--expand the subnodes by also pushing them onto the stack. Once the expansion reaches a termination point (such as a literal value or the last element in a block), the expanded nodes are processed one at a time.

The instructions associated with each node perform whatever JRuby operations are required to process that node. They instantiate classes, define methods, assign variables. For this reason, they actually are of the type Instruction internally, and this is where we can plug in the compiler.

Compiler Design Version One: Microcompilation

Because the interpreter simply traverses the AST, retrieving and caching instructions as each node is encountered, we can short-circuit this process by pre-populating the appropriate instruction for a parent node with a compiled instruction based on its children. When the interpreter reached that node and sees an instruction has been prefetched, it executes that rather than continuing to traverse. As a result, we can selectively compile branches of the AST for targetted speed gains. I call this microcompilation.

The benefit to this approach is that the compiler can be written a piece at a time, tested incrementally as those pieces come together. It also has the huge benefit of allowing compiled code to run within the existing interpreter engine without any modifications to JRuby.

There is, however, a downside to my current approach. Instead of pushing toward a green-threadable and continuation-able iterative model, this simple compiler will still deepen the stack. However, given that no applications we are currently working to support seem to require continuations or threads, I believe this is a good initial approach. It may also prove true that this simpler compiler is fastest (while perhaps not the most Ruby-compliant), and for many embedded Ruby applications it will continue to be useful into the future.

Processing The Array


So back to our six-element array. In order to handle it iteratively, rather than recursing to process each element, a complicated transformation happens. The instructions elements of the array are mixed with instructions for aggregating the results, and another instruction is added at the end to finally build the array. So the sequence of instructions processed, after initially processing the array node, goes something like this:

String Node: 'this'
Aggregate (push down result accumulator)
String Node: 'is'
Aggregate
String Node: 'an'
Aggregate
String Node: 'array'
Aggregate
String Node: 'of'
Aggregate
String Node: 'strings'
Build Array (deaggregates all results and constructs a ruby array)

For more complex elements, this approach also works; if one element requires a method call or a variable lookup, that branch of the AST is processed inline, with the end result placed on the top of the accumulator stack. Processing then continues with the next element.

The added overhead of this approach is not without reason. Yes, it would have been possible to modify the interpreter engine to understand arrays and handle them specially, but my goal originally was to create a generic iterative interpreter (and perhaps to prove that all nodes could be processed in this way). However, that design mostly proven out, it is now time to address its deficiencies.

Compiling The Array

Compiling the array turns out to be a simple matter, especially considering that the only element type supported by the compiler right now is a literal string. The compiled code instantiates a new IRubyObject[] of the appropriate size, then visits the compilers for each of the elements. The String node compiler I wrote for the "foo" test is reused here, and as each element is encountered bytecodes are generated to construct ruby String objects and insert them into the array. Finally, an operation is added to construct the ruby array instance, and our work is done.

During this round of work, it has also become apparent that although we make heavy use of JRuby internals from the compiled code, large portions of the interpreter itself can go away: for example, with a handy-dandy operand stack available in bytecode mode, we would no longer need to maintain state on a separate result accumulator. The "v1" compiler is turning out to be a fun and easy affair.

The Numbers

The results of these little microcompilations are perhaps anti-climactic...after all this noise I post a handful of numbers for you to look at. Even if the numbers are great, it's a small payoff for listening to my rambling. Sorry! That's how it is!

The test methods take the same form:

def foo_arr; ['this', 'is', 'an', 'array', 'of', 'strings']; end

The foo_arr method is our compiled version, and bar_arr is the uncompiled version. And so after another 1_000_000.times { foo_arr } and { bar_arr } we have:

foo_arr time: 32.628
bar_arr time: 93.31700000000001

This equates to a roughly 60% speedup from what is, again, a fairly simple operation even in the current interpreter.

Coming up in future posts: more v1 microcompilation and numbers, v2 and v3 compiler designs, and more...

Friday, April 28, 2006

In The Beginning: Early Returns on JRuby Compilation

I'll put a big fat disclaimer here stating that these results are extremely preliminary, and mean practically nothing. I'm just very tired and very excited that after seven hours of work, I've already made some progress...I sorta, kinda compiled some Ruby code to Java bytecodes. Yes, I've been up all night. Yes, I have to work tomorrow. Yes...I wish I could work on this during the day. C'est la vie!

The Contenders

In order to soften any growing excitement you may be feeling, I must first introduce the scripts in question. The script to be compiled is quite a whopper:

def foo; 'bar'; end

Hey now, everything has to have a beginning. Even the above script is not the whole story; the only code actually being compiled are the four AST nodes that make up the method's body: a Newline (to start the line), a DStr (dynamic string), another DStr, and a Str (the actual string), all nested one inside the other.

Now as anti-climactic as that may sound, this is early proof-of-concept work here...I don't intend to write a compiler for all of Ruby at the same time. This was the smallest non-trivial script I thought I could handle in a first attempt. I do not compile the nodes that define the foo method, nor do I compile any dispatches to the foo method. I just compile the body.

In order to have an uncompiled version, we have another identical method:

def bar; 'baz'; end

The bar method functions exactly as the foo method, but we will not compile this one. It will be the control.

Now since only a portion of the AST nodes are being compiled, it seemed appropriate to measure a method that does nothing, so that the cost of dispatching could be isolated, further narrowing the test. For that, we have baz:

def baz; end

By running the same tests against baz, we can get a better idea how much compilation is helping. There are also other interpreter bits and pieces that will skew the results a bit, but early results are early results; we want to know whether it will be worth the effort.

The Tools

For this round, I chose to use ObjectWeb's ASM bytecode generation library. It seemed best suited to the visitor pattern we use to traverse the AST (since it folows the visitor pattern itself), and it's itty bitty.

Now for the fun part: I wrote the compiler in Ruby using JRuby's Java integration support. Some time ago, Tom added the ability to parse a string and get back a reference to the AST within a Ruby script under JRuby. Since we already have visitor interfaces for the interpreter, and since JRuby does such a bangup job of implementing interfaces, I figured I'd do the whole thing in Ruby. Naturally, if we continue down this route, we would hope to eventually compile the compiler and add "self-hosting" to our list of buzzwords. However, I digress.

The basic model was simple: parse foo, retrieve the body node from the AST, convert the body to bytecodes, and replace the body with the newly-compiled . This allows our interpreter to continue doing its normal interpretation, but execute this one method's body natively. During my interpreter redesign last Fall we hoped to make this legerdemain invisible...and it seems we've succeeded thusfar.

The foo method's body was transformed into a class with one method, execute, implementing the same Instruction interface we use in the interpreter. The difference, of course, is that we would now have one bytecode-based instruction rather than four.

It's also worth pointing out that this model does not turn a Ruby class or a Ruby method into an accessible Java class or method. It turns snippits of Ruby code into tidbits of Java code...and that Java code continues to live as part of the interpreter. This is the most digestable model of compilation for Ruby, and has been followed by most other projects of merit. We may be able to take things further in the future (especially with the potential dynamifying of the JVM), but this level of compilation has massive potential right now.

The Test

Given that the scripts are so simple, the test should be equally simple:

1_000_000.times { foo }

...and equivalent tests for bar and baz. These tests are parsed, processed, and timed in the exact same way (other than the compilation step):

require 'java'
require 'jruby'
include_class "org.jruby.Ruby"

my_ruby = Ruby.default_instance

foocode = JRuby.parse("def foo; 'bar'; end")
#compilation done here
footest = JRuby.parse("1_000_000.times {foo}")

my_ruby.eval(foocode)
t = Time.now
my_ruby.eval(footest)
p Time.now - t


And again, equivalent tests for bar and baz. That's all folks! Have a nice weekend!

...

Ok, ok. You'd like to know what the times are. That's fair. I've led you this far, and you'd like some payoff for reading my technojargon gobbledygook.

Naturally I wouldn't be writing this if the results were poor.

The Results

Now did I mention these are very early, very preliminary numbers? Don't go off half-cocked talking about JRuby's new compiler, ok?

The bare method baz demonstrated that a large amount of time is spent dispatching in JRuby; perhaps an inordinate amount of time. We have plans to correct this, and have a few optimizations being tossed about.

The baz method took 3.8s to invoke one million times on this machine (Opteron 2.6GHz, Gentoo Linux 2.6.15). That may not seem bad, but of course it wasn't doing anything. Also, C Ruby does the same million dispatches in about 3 tenths of a second. An order of magnitude worse, we are.

The bar method, which represents the uncompiled foo, clocked in at 12.5s to complete a million calls. Now you start to see the real cost from traversing all those AST nodes. Adding only four nodes to the mix (and the side effects that result, of course) quadrupled the amount of time. Minus the method invocations, the body took roughly 8.7s, about 70% of the total run.

As you might expect, foo did better than bar. One million invocations of foo, our bar-with-a-compiled-body, took only 6.5s, almost 50% faster than bar. Subtract the method invocation hit and we're looking at around 2.7 seconds, a 60% improvement over bar.

baz: 3.8 seconds
bar: 12.5 seconds, or 8.7 seconds excluding method invocation
foo: 6.5 seconds, or 2.7 seconds excluding method invocation

So there you have it.

If we only get a 60% improvement I would be very pleased. However, given that Newline, DStr, and Str nodes are some of the least interpreter- and processor-intensive nodes in the AST, I'm certain we'll do far better.

Thursday, April 27, 2006

JRuby Compiler Will Happen

Things have really been clicking along on JRuby. Tom has been knocking down Rails unit tests left and right, and more and more stuff is working. I found a nice little performance booster that gives us as much as 30-40% speed increase when starting up. Other folks have been working on updated YAML parsers, ActiveRecord-to-JDBC connectors, and in general helping us test JRuby in more and more scenarios. The future looks pretty good.

However...

I am still far from satisfied with the performance of JRuby. Even with recent cleanup and minor optimizations, a basic CGI-servlet Rails request takes a second or two to process. Much, much longer than it should have to, especially considering we're not hitting a persistent store yet. With JRuby moving ever closer to its goal of C Ruby 1.8 compatibility, why not start looking at those performance issues in more depth?

First Time for Everything

I will admit, I've never written a compiler. Heck, until JRuby, I'd never worked on a language interpreter, much less written one. Of course, a lot has changed in a year, and I'm now one of the two people in the world who know the most about JRuby's internals. It has been quite a lesson in interpreter, language, and VM design...a lesson I've wanted to learn for the better part of my life.

I believe it is time for us to start exploring options for compiling JRuby to intermediate "JRuby bytecode" or to Java bytecode in many cases. Other dyn-typed langages for the JVM continue to hold their compilers up as a reason to choose something other than JRuby. I think JRuby's time has come.

I have been studying Ruby's AST, JRuby's implementation of its interpreter and core libraries, and the C Ruby code, and I am now confident we can do some level of compilation in the short term. The redesign of the core interpreter I committed last Fall has opened the door for more flexible traversal of the AST, including the ability to partially compile some scripts.

And So It Begins...

With Rails, Spring, Swing, IRB, and our other demos for JavaOne basically ready to go (ahead of schedule!), I will be spending the next week or two researching and exploring options for a preliminary Ruby compiler. I believe I can have something by JavaOne; something primitive, sure, but perhaps something that can provide numbers to go on. Stay tuned!

Wednesday, April 19, 2006

JRuby on Rails

After seeing David Heinemeier Hansson ask the "Groovy on Rails" project not to use that moniker (since it isn't Ruby, it isn't Rails, and it has a completely separate codebase), I figured I'd play it safe and ask him directly whether "JRuby on Rails" would be acceptable. We intend to run Rails unmodified, and we're trying to be as "Ruby" as possible, so it seemed to me that since JRuby on Rails is the same under the covers, the extra "J" wasn't that big a deal. David agreed:


As long as Rails remains virtually unaltered and fully functional, I suppose JRuby on Rails isn't too bad. I'm a tad protective of the term Ruby on Rails, though, and I want to make sure that there's no confusion about what it refers to. But I can't immediately see any confusion points here.

So the JRuby on Rails name should stick, and we have cautious approval from the Rails creator himself to use it. Huzzah!

Eclipse UnsatisfiedLinkError: libswt-pi-gtk-3139

I ran into this a bit ago and found no solutions online. I figured it was my duty to provide at least one posting on how I solved it, since there seems to be a lot of frustrated people out there. My eventual solution was to run with 1.5 instead of 1.4.

Upon starting Eclipse 3.1.1 or higher (or other SWT-based apps, it seems) under AMD64-based Linux, many people have reported the following error:

!ENTRY org.eclipse.osgi 2006-04-08 13:53:48.407
!MESSAGE Application error
!STACK 1
java.lang.UnsatisfiedLinkError: /usr/local/src/eclipse/configuration/org.eclipse.osgi/bundles/85/1/.cp/libswt-pi-gtk-3139.so: /usr/local/src/eclipse/configuration/org.eclipse.osgi/bundles/85/1/.cp/libswt-pi-gtk-3139.so: cannot open shared object file: No such file or directory
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1586)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1495)
at java.lang.Runtime.loadLibrary0(Runtime.java:788)

...etc

The .so in question is present, but it seems that when running under Sun's JDK 1.4.2, which does not provide an AMD64 version, this library can't be successfully loaded. I'm not saying this is a Java issue or an Eclipse issue, but I resolved it by running Eclipse under Sun's JDK 1.5 built for AMD64.

Hopefully this helps anyone else out there searching for a solution.

Tuesday, April 18, 2006

A Personal Blog

I've decided to add a personal blog, since many other tech writers do the same (and it seemed like a good idea).

Pop over to Headius@Home.

Thursday, April 13, 2006

The Beginning of JRuby on Rails

It must be something of a debate in the blogosphere as to whether titles should be descriptive, possibly giving away a great secret hidden within an entry's text, or whether titles should only hint at the truth, enticing a curious and diligent reader to venture onward. The former perhaps produces a better tagline for newsfeeds, where a less-descriptive title may not be as attention grabbing. The latter is certainly more suspenseful, allowing a careful author to draw readers toward a mounting climax. I prefer the former, and so this entry's title once again gives away the Big Secret:

Today, Rails handled a simple request under JRuby.

Now perhaps this monumental event deserves uproarious fanfare, and perhaps it does not. I will let you be the judge.

We really WERE close!

For several weeks now, Tom and I have been saying back and forth to each other that we thought Rails was very close to working. Developer optimism, perhaps, but we've poured a lot of effort into fixing the last interpreter issues preventing Rails from running successfully. In the process, we've accomplished many peripheral goals like getting IRB running, improving RubyGems support, reparing untold interpreter and core class bugs, and getting a round education in the internals of Rails. I've personally traced through more Rails code than I ever hoped to, and committed patches upon patches to achieve success. Tom has also been cranking out fixes, and our growing community of contributors have sent some amazing enhancements our way. In short, there's real momentum behind JRuby right now, and we had a feeling Rails was almost there.

The last time I really burned the midnight oil was probably over a week ago. With SourceForge CVS down for an entire weekend, family business to attend to, and a pre-JavaOne presentation at the Minnesota Object Technology Users Group's Java Special Interest Group (whew!), there hasn't been a lot of time for late-night coding jags. That changed today.

With all other distractions behind me (the Java SIG presentation was last night), I set out today to finally accomplish our biggest goal

I resolved that by night's end, Rails on JRuby would handle a request.

Expecting a long night, I put on some music (somafm.com is excellent...listen and donate), grabbed a few snacks and beverages, and dug in.

How did I do it?

Now I know that should probably say how did "we" do it. Tom has obviously put in a substantial amount of time on JRuby, and our contributors deserve their props. My intent here is just to describe the final steps leading up to Rails working, to allow you to better judge our success and to document steps necessary for others to replicate my work.

Step 1: Apply all outstanding patches

Over the past several weeks, a number of core bugs have been resolved. In many cases, we had not yet committed those changes to CVS, preferring to review and clean them up. I committed several such fixes this evening:

- A patch to allow Kernel#system and backquote calls to stay in-process if executing a Ruby script. Previously, this caused a new interpreter to be launched (in a new JVM), which was obviously a bad thing for a Java app to do. This fix is hopefully a temporary modification, pending a better solution in RCR 328.
- A rejiggering of the Main and CommandlineParser classes in JRuby to better allow Multi-VM support in the future. These two classes originally used System.x input and output streams and called System.exit on error. For an interpreter that's intended to run in controlled environments, these obviously had to be fixed. The new versions won't kill off a VM and will only use the streams provided to them.
- A small fix for String#split to allow for splitting on ? characters (kinda important for URLs, no?). I have an improved fix for split to be committed tomorrow.
- A new ENV implementation that takes advantage of Java5's new System.getenv method, and falls back to other hacks on earlier Java versions. Originally, without any way to retrieve env vars, JRuby had an empty ENV hash. This fix was necessary in order to run Rails in my preferred test configuration. (Can you guess why?)
- Process#times was not implemented (because it can't be supported under Java). I added it to return a Tms with all zeros.
- File#flock was not implemented. I added it, using java.nio file locking support.

A few of these fixes came out of my playing with Rails this evening, and I stopped when Rails started working.

Step 2: Set up Rails in the most traditional way

Rails is a very CGI-style web framework. In its simplest form, it is a basic CGI script, and all hacks to improve its performance build around that idea. Given that Rails wanted to be CGI, it made sense to set it up that way...with JRuby executing it.

dispatch.cgi is the main CGI script for Rails. There's also a .fcgi version for FastCGI. Eventually, JRuby will provide a "CGI servlet" of some kind to wrap Rails requests, as well. For now, default.cgi was the order of the day.


#!C:/jrubywork/jruby/bin/jruby.bat

require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT)

# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like:
# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired
require "dispatcher"

ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) ifFile.directory?(dir) } if defined?(Apache::RubyRun)
Dispatcher.dispatch



Yes, running a Java interpreter on each CGI hit is gross. No, I don't expect anyone to run like this in production. It was simply the easiest way to get this working. Now we can go forward.

Step 3: Configure Apache

I'll admit, working in application servers from dawn to dusk I don't have to tweak Apache configs much. This config probably isn't the most spectacular thing in the world, but it does what it needs to. The biggest change from a standard Rails config is the env vars provided for JRuby.


DocumentRoot "C:/rails-1.0.0/public"

<Directory "C:/rails-1.0.0/public">
SetEnv JRUBY_HOME C:\\jrubywork\\jruby
SetEnv JAVA_HOME C:\\j2sdk1.5.0_06
Options Indexes ExecCGI FollowSymLinks
AddHandler cgi-script .cgi
AllowOverride all
Allow from all
</Directory>



Step 4: Turn off what doesn't work

Ok, you knew there had to be a catch. Since there's still a lot of work to be done on Rails, there was at least one area I discovered that wasn't going to work correctly today. To save myself staying up all night, I disabled session support; with it enabled, the script got stuck in a neverending loop somewhere I didn't feel like investigating. For the sake of this test, I added the following line to rails_info_controller.rb:


module Controllers #:nodoc:
class RailsInfoController < ApplicationController
session :disabled => true



We'll circle around to session management and get it working, so don't fret.

Step 5: Finally, test out our very narrow, very basic request

As you've probably gathered, the request I got Rails to handle was a simple info dump. Calling /rails_info/properties on a standard Rails install just dumps some version numbers and path information. It does, however, exercise the full Rails request handling and dispatching mechanism. Having it working is a big deal...it means that Rails is actually able to handle requests and display a result.

(as an aside, during the final stages of debugging I also saw the default error page come up, fully rendered and containing stack traces and error info...so even that's pretty cool).

On my system, /rails_info/properties outputs:

Ruby version1.8.2 (java)
Rails version1.0.0
Active Record version1.13.2
Action Pack version1.11.2
Action Web Service version1.0.0
Action Mailer version1.1.5
Active Support version1.2.5
Application rootC:/rails-1.0.0/public/../config/..
Environmentdevelopment
Database adaptermysql

The (java) up there represents the first time you or anyone else has seen Rails running in a JVM. Mark this day on your calendar.

Where to now?

It is may or may not be safe to say that Rails "runs" on JRuby. There's obviously a number of other subsystems to get working, and without sessions a web app would be pretty dumb. Saying that Rails works, except X and except Y, basically means it doesn't work without a whole bunch of asterisks--but handling an end-to-end request of any kind represents a major milestone.

What would be safe is to say that this represents the birth of Rails on JRuby. This is the first time a request has successfully been handled by Rails in a JVM. The next steps are obviously to get all normal use cases working, get the rest of the Action Pack functioning, and as always, speed JRuby up to be a viable deployment option.

To all those out there who have supported us and believed in us, we on the JRuby team give our thanks. This milestone would not have been possible without you.

To all those out there interested in Ruby, Rails, JRuby, or any combination of the three, I say this: You ain't seen nothing yet.

Thursday, March 30, 2006

A Ruby in the Rough

Feeling More and More Ruby with Gems

Things are speedily moving along. We have been continuing our work on Rails, but because of an unusually active JRuby community, we recently changed direction for a bit. We decided to tackle RubyGems.

RubyGems, if you are unfamiliar with it, is a packaging system for bundling up and installing Ruby applications into your local Ruby distribution. It works with both file-based and name-based installs, so that you can either "gem install rails-x.x.x.gem" or "gem install rails" and it will fetch the appropriate archive for you. It manages depedencies and updates seamlessly. It basically does for Ruby what apt-get and rpm do for Linux.

There's not a terribly high level of complexity to the RubyGems code, like there is with IRB or Rails, but it exercises many APIs the former two do not. In our case, the biggest issue was Zlib.

Ruby of course has full Zlib support, with gzip, inflate, checksums, and a nice stream-like API. However, as with many other bit, byte, or math-intensive libraries, Ruby's Zlib is implemented in C code. This certainly speeds up the processing of compressed archives, but it presents a continuing problem for JRuby: we can't inherit *all* of Ruby's libraries just by copying over the lib/ruby/1.8 dir. In this case, luckily, we can do what Ruby does.

Java has included java.util.zip, support for compressed archives, since the 1.1 release, with streams for zip, gzip, and deflate compression and decompression. It also includes the same checksums Ruby includes, CRC32 and Adlter32. It provides probably 90% of what we need in Ruby's Zlib library.

The availability of a pure-Java implementation of a Ruby library certainly cuts down our workload. Since we already have significant capabilities for calling Java code from Ruby, there's usually only a bit of Ruby code required. However everything takes time, and since Rails has been our primary focus we had not spent much time getting Zlib implemented.

Enter our new friend Ola. Ola recently appeared on the jruby-devel mailing list, interested in helping out with JRuby and contributing fixes whereever possible. He also expressed an interest in making RubyGems work. We rattled off a couple items, with Zlib in the list explicitly because it was the major roadblock for RubyGems, and within about a day Ola returned with a preliminary Zlib implementation. I was personally very impressed.

Now it goes without saying that his implementation wasn't perfect the first time, but this really got us going. We switched gears for a few days and worked back and forth with Ola to get Zlib and RubyGems working more smoothly. I think I spent 12 hours on Saturday hacking through Zlib, YAML, and RubyGems code digging for failures. The end result was RubyGems's setup.rb install script running to completion, but it wasn't quite there. Ola came to the rescue to resolve a few more minor issues, Tom committed some additional fixes today, and although we didn't get RubyGems working in time for the 0.8.3 release, we did get a lot done.

At this point, we are able to successfully install RubyGems into the JRuby distribution directory. Huzzah!

Now of course you knew there would be caveats.

Java as a Platform

When running Ruby, there are a number of constants pre-set for you. Among these is RUBY_PLATFORM, which identifies the underlying platform on which Ruby is running. Ruby under Windows reports "mswin". Under linux it reports "linux", unsurprisingly. For JRuby, it made sense to report "java", since we do not match any other platform exactly. Java is its own platform.

The availability of the RUBY_PLATFORM constant allows scripts to change their behavior depending on the platform. Since Windows doesn't directly support symbolic links, discovering a RUBY_PLATFORM of "mswin" means you shouldn't try to create them. Discovering a platform of "linux" means you probably shouldn't generate externally-consumed file paths using backslash characters. Given that even a cross-platform language and implementation must interact with its host environment, these cases make sense.

Unfortunately, Ruby's libraries only know about the platforms that the C implementation supports. Hence, there's a problem when running under JRuby. Since we report our platform as "java", any code that uses RUBY_PLATFORM to turn off unsupported features does not have any effect. The specific case I ran into was within the fileutils library.

fileutils provides a number of convenience methods for manipulating or querying files. There's methods like cp for copying files, uptodate? to check if a file or files' modification times are later than another specified file, and other similar functions. It is with cp that JRuby ran into problems.

fileutils' cp implementation copies one or more source files to a target file or directory. During this process, it also checks whether the target file is equivalent to the source file. This it does in one of two ways:

- On a UNIX variant, fileutils compares the device and inode for the file, using File::Stat#dev and #ino
- On a Windows variant, where device and inode have no real meaning, fileutils compares absolute paths

In order to know which method to use, RUBY_PLATFORM comes into play:


def fu_windows?
/mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
end

The fu_windows? method obviously returns true on a Windows platform or on any platform where dev and ino will not be available. Notice that Java is not in that list?

Device and inode have even less meaning in Java, which abstracts away the concept of a "file" into the most generic definition possible. In order to keep Java code running the same on all platforms--without platform checks or code changes--Java's File must cater to the lowest common denominator. That's great for Java code, but for us poor suckers trying to get Ruby to run in Java, it's an issue. We need Ruby's libraries to understand that Java doesn't support the same things a UNIX variant does, and doesn't support other things that a Windows variant does. In short, we need Ruby to understand the Java platform's capabilities.

Now we aren't going to be able to solve that issue today. It would be a little unusual for Ruby to include a "java" platform whereever platform is being queried, and I don't doubt we'd get some pushback on that. We're kicking around possible solutions, ranging from implementing our own versions of those libraries that use RUBY_PLATFORM extensively to including modified Ruby libraries in the base JRuby distribution. In the short term, we chose to locally modify fileutils as follows:

def fu_windows?
/mswin|mingw|bccwin|wince|emx|java/ =~ RUBY_PLATFORM
end

With this change, RubyGems is able to successfully install and copy all required files to the JRuby distribution dir. It's not perfect, but it's a start.

Installation != Working

Aside from the very simple case of installing the sources gem, RubyGems is not quite working correctly in JRuby. With RubyGems installed, previously-functional Rails scripts start breaking in new and exciting ways. Network-based gems do not install, owing to our bare-minimum Socket implementation and our lack of marshalling code for appropriate Ruby classes. File-based gems attempt to install, but get "stuck" somewhere during processing. All are frustrating issues, but we're very close.

Small Moves

Along with RubyGems nearly working, it was a very productive week. A lot of the contributions for RubyGems will be helpful in many other areas, and we have continued work on Rails as well.

- We now have a working Zlib library.
- We have made more progress toward getting Rails to work, by running dispatch.rb/cgi and fixing as we go.
- We finally got out a new release after a long delay.
- We are fairly confident we'll be able to demo everything we want at JavaOne.

Things are looking great, and I'm very excited.

Monday, March 27, 2006

Ultra

It seems that JRuby is more than just an entertaining and challenging project; it's also getting attention from folks that matter at Sun Microsystems.

Tim Bray, Sun Web Technologies director and XML specification co-editor, has recently taken an interest in JRuby. He's corresponded by email with both Tom and myself, and has offered useful insight here and there. It's good to know people are watching JRuby and wishing us well.

Of course, a couple free machines never hurts either.

Tim managed to "jar loose" a couple beautiful Sun Ultra 20 machines, one each for Tom and I. I can't speak for Tom, but considering the dismal condition of my home data center, the new machine is a welcome addition.

I generally have nothing but good things to say about the Ultra 20. We received the base "Large" model as listed on Sun's store; I'll save you a click and paste specs here:

Sun Ultra 20 Workstation
1 AMD Opteron Model 152 Processor
1-MB On-Chip L2 Cache
2-GB Memory
1 250-GB 7200 RPM SATA Disk Drive
1 Quadro FX 1400 Graphics Card
1 DVD-Dual Drive
1 10/100/1000 BaseT Ethernet Port
6 USB Ports
2 IEEE 1394a Ports
1 PCI-Express x16 Slot
2 PCI-Express x1 Slots
4 Conventional PCI Slots (32-Bit/33-MHz)
Sun Studio Software Pre-Installed
Sun Java Studio Creator Software Pre-Installed
Sun Java Studio Enterprise Software Pre-Installed
Solaris 10 Operating System Pre-Installed
3 Year Warranty, 3 Year Parts Exchange

Any review I might give would be impossibly biased by my complete lack of experience with recent hardware. Before the Ultra 20, my fastest machine was my 1.6GHz Pentium M Dell laptop, which overheated when running unit tests too vigorously (producing no end of colorful metaphors from yours truly). I had no desktop, having converted my poor old 1GHz Celeron homebuilt into the headius.com server. Suffice it to say that the Ultra 20 is by far the fastest, quietest, and most impressive workstation I've had the pleasure of using.

Before this machine, the stories of AMD chips trouncing the top-end Intel equivalents were merely anecdotal. Now I get it. The Intel machines I use most days almost seem broken in comparison. Given that AMD topping Intel only means Intel will work harder to create more competitive chips, this bodes extremely well for the next crop.

My only disappointment was that my old 17" Mitsubishi monitor, the only remaining working monitor in my house (don't ask about the 21" Viewsonic that met its untimely demise at the hands of a leaky basement window) was unable to work out-of-the-box with the pre-installed Solaris 10. I was keen to give it a shot, but it seems it will have to wait until I upgrade to a better screen. Hopefully, that will happen in the next week or two.

I would whole-heartedly recommend this machine (or even one of the lower models) to anyone that needs a new box. Even the base "Small" model is still far faster of a machine than I've ever used, and it seems to be priced well. Again, take my star-struck (Sun-struck?) opinion with a grain of salt, but having a powerful, inexpensive computer with a Sun logo on the front seems like a pretty sweet deal to me. YMMV.

In the end, however, a blazing fast machine means faster development, faster test runs, and faster progress on JRuby. Who knows, perhaps I'll even have time for the other 9000 projects I'd like to tackle.

Thank you Tim Bray, and thank you Sun Microsystems.

Monday, March 20, 2006

Enterprise Sour Grapes

Recently a post by James McGovern on Why Ruby Isn't Enterprise Ready floated my way. I felt it necessary to offer up a response. My disclaimers: I am not familiar with McGovern's past work, I am not (yet) using Ruby in the "enterprise", and I am far from a Ruby or Rails expert. I am, however, trying to bring Ruby the capabilities of Enterprise Java using JRuby, and I've done a bit of Enterprise work during my ten years as a Java developer.

Point-by-Point Responses

McGovern's points are not all bad, but most of them are either poorly realized or amount to chicken-and-egg arguments: Ruby isn't in the enterprise now, so it's not ready for the enterprise. I'll try to blaze through the nonsense and tackle the substantive issues more directly.

Point 1: Books for Ruby Suck and are too introductory
Nonsense. Ruby as we know it today is young and has only garnered attention over the past few years. Books are written to be sold, and since almost everyone doing Ruby work has started very recently, the market for introductory books is largest. There are, however, more and more people interested in enterprise Ruby, and so the books are starting to follow. It will take time, just as it did for Java, but it doesn't mean Ruby isn't ready to make the jump to "enterprise" software.

Point 2: Huh? (i.e. poorly-written nonsense)
I couldn't really glean out any specific point being made here. I'm not familiar with "insulting" firms, though perhaps that's a crude attempt at humor. Assuming he means "consulting" firms, he's only partially right; obviously if an organization already has an "enterprise" plan, they won't be told how to rewrite it. However, any consulting firm worth its mettle will try to follow the same best practices when building out a new architecture or conforming to an existing one, and those practices aren't specific to any language or platform. This has nothing to do with Ruby.

Point 3: Ruby isn't enterprise ready because consulting firms aren't doing it
Nonsense. Consulting firms have a vested interest in keeping their technology portfolio tailored to the demands of clients. They also have an interest in telling clients what they actually want, so those demands continue to fit their portfolio--just like Enterprise Thought Leaders have a vested interest in convincing others their platform of choice is the best solution. Regardless, Java has a huge presence in the enterprise space, and has become the perfect vehicle for consulting...a well-understood, boring, accepted platform. .NET is well on its way to "boring" as well. Ruby will have work to do to "break into" this world, but its absence there says nothing about its viability as an enterprise platform. It's a small fish in a big pond...but fish grow.

Point 4: Magazines read by enterprise architects don't cover Ruby
Nonsense. This doesn't even make sense, since I doubt McGovern can speak for all "enterprise architects". If he means "enterprise architects that only use Java or .NET or don't read about languages", then he may be right. Otherwise he's making a blanket statement that's obviously impossible to prove and is at best totally wrong.

Point 5: Fortune 500 company-employed architects don't blog about Ruby
If this is true, then they are at risk of being passed up by others that do read and blog about Ruby. However this is another statement that says nothing about Ruby's enterprise capability. It only continues to say that "Ruby isn't enterprise now, so it's not ready for the enterprise."

Point 6: Large enterprises like big vendors (i.e. the same old Open Source FUD)
Ruby is garnering more and more attention from the "big vendors", but they're naturally cautious about leaping into a new language that may or may not be the right way to go. However, this argument could have been said for languages like Python, which now sees extensive use in enterprise apps and for which Microsoft has been funding IronPython for their CLR. Ruby is simply a tool; the eventual platform we will use to build enterprise applications is being built and will be built upon that tool, and it will in many cases be "big vendor"-driven.

Point 7: Big vendors can't make money off Ruby
Sun does not make a lot of money off Java, if they make any at all. Microsoft does not make a lot of money off the .NET languages. In either case, what little income they gain is offset by the massive research and development staff making those platforms possible. They make their money by selling products related to those platforms, such as toolsets and server software and hardware. Nobody is making money "selling Java" or "selling C#", just like nobody makes money "selling C" or "selling Ruby". As in the real world, the market for tools pales in comparison to the products of using and deploying those tools effectively. And again, this says nothing about Ruby's technical merits.

Point 8: Legal transparency is more important than software development productivity
This has nothing to do with Ruby or anything else, except to say that "so what if there's productivity gains to be made...I'm too busy dealing with the lawyers". Certainly, the cost of answering a subpoena may offset productivity gains elsewhere in an organization. However, McGovern does nothing to demonstrate any causal relationship between the two. I'm not entirely sure that he's demonstrated anything at all.

Point 9: People, then process, then tools
If tools matter least of all, then whether the tool is Ruby or some other language is entirely irrelevant. This point basically states that all his other concerns about Ruby in the enterprise are moot. Huzzah!

Point 10: Do not talk about Fight Club.
There is no point 10 in McGovern's post. I'm not sure if this is intentional or due to carelessness but judging by the rest of the post I'd expect the latter.

Point 11: More ranting about productivity
The absurd productivity gains claimed by some in the Ruby community are a concern, but these same sorts of numbers have been used for every Next Big Thing. Undoubtedly, Ruby will have an effect on productivity, and I believe it will have a strong positive effect, but this point has nothing to do with Ruby in specific.

Point 12: More ranting about books
Complaints about a particular magazine's method of giving out "awards" or publisher's use of those "awards" to market books has nothing to do with Ruby. There's plenty of SD Magazine "award-winning" books in the Java and .NET worlds too.

Point 13: Productivity gains are outweighed by increased contract negotiation time
Clients are interested in getting the best value for their dollar, but are also interested in tried-and-true technologies. As mentioned earlier, Ruby is still young, but its youth says nothing about its capability in the enterprise. I remember plenty of companies that shunned Java early on for the same reasons...if I'd followed their advice I'd be looking for COM+ and Microsoft DNA work. It's just another chicken-and-egg argument that has affected every other language and platform since there have been such things.

Point 14: Agile methodologies should emphasize code generation and the agile community doesn't get it
Other than a brief mention of Ruby, this doesn't seem to have anything to do with the core thrust of his post. It's worth mentioning that the poster child for Agile development, Rails, generates a large amount of its code before and during deployment.

Why Respond to such Drivel?

I've heard many folks ask this question about responses to such misguided "thought leaders" as Mcgovern. Why respond to nonsense? Why give air to FUD?

There's probably a few good answers.

First off, not responding implies to many that there are no counterpoints to be argued. Responding to a well-written and well-thought-out post is probably less important than responding to FUD, since the latter is generally filled with lies and vitriol. This is the case with McGovern's post; the points are not valid and generally say nothing about Ruby, but the title and thrust of the posting implies such. Other "enterprise architects" that skim through such a posting may use it to solidify their own prejudice and bias toward specific tools and platforms, which only hurts the evolution of software and software development. It is for this reason that I choose to respond and choose to publicly call out such bogus claims as well as I am able.

Second, such posting make us folks actively working on Enterprise Ruby more than a little pissed off, because the content is so weak and the facts are so twisted. Consider this post my outlet for such frustration. Edmund Burke said it best: "The only thing necessary for the triumph [of evil] is for good men to do nothing." Perhaps McGovern is not evil, and perhaps I am not good, but I will not stand by and do nothing. If roles are in actuality reversed, I would expect the same from McGovern.

Third, we like to talk. This is certainly true, but does not necessary imply any malice. I like to write...so sue me.

Rails' Generators Are Working

I considered making a post last week, to keep up my at-least-one-posting-per-week schedule. However, I had dug myself deep into Rails' generator script and the built-in generators and resolved myself to finally get them running. There were a number of more complicated issues to solve, but it felt like I was very close to having it working. Any of you all-night hacker types know the feeling; success is just around the corner...maybe this is the last bug...maybe this run will complete without errors.

I can now announce that as of this evening, all the built-in Rails generators appear to be running and generating correctly using JRuby.

This has certainly been a hard-fought battle, and the last week had some big fixes:

  1. As mentioned in Pickaxe, Object does not actually define any instance methods; instead, it mixes them all in from Kernel. However, the original design of JRuby had followed Pickaxe in form rather than substance, defining those methods on Object. While this did not typically affect the functioning of normal Ruby code, it did break one library in particular: delegate. DelegateClass, in delegate, uses Kernel's list of public instance methods to select which methods on the target class are to be delegated. Rails uses it internally for, among other things, delegating some behavior for generator commands to a generator base class. Fixing the issue meant redefining the Object instance methods as Kernel module methods...a fairly major change, but one that does not appear to have caused any other regressions.
  2. My recent addition of binding support had a small flaw. The current chain of method calls in JRuby to do an eval is long and winding (much longer than I would like), and one link in that chain I did not inspect caused two issues: evaluating with a binding did not correctly set "self", and completion of that eval did not correctly reset it. In lieu of cleaning up the eval chain (which I commit myself to eventually do), I made a few modifications so "self" would work correctly.
  3. Enumerable#collect should work without a block; this is not documented in Pickaxe and finding this issue from deep within the bowels of 'generate' was a painful chore. This is a perfect example of a miniscule bug that causes massive trouble; the fix was less than a line of code, but the bug prevented 'generate' from correctly mapping and executing any actions. And why would you want to collect without a block? Answer: if you only have "each" defined and wish to turn your Enumerable into a simple array.
  4. JRuby's Module code inexplicably defined a singleton "new" method. This prevented Module subclasses from defining their own initializers that call "super". I'm still not sure why this was there, but it has been removed.
  5. Module#ancestors failed to include singleton classes.
  6. Java does not support the concept of a process-wide "current directory" as Ruby does. In order to fake this behavior, JRuby originally modified the system property "user.dir" to point at JRuby's new current directory. This was not only a dangerous thing to do, but was also not sufficient to make current directories work correctly. It also would drastically affect all other code running in the same JVM that depended on "user.dir" being correct. My modification was to introduce into JRuby a runtime-wide "current directory". In addition, I fixed a problem in Dir.chdir where failures in the provided block prevented chdir from resetting the dir back to its original location.
  7. Dir.mkdir dir not handle multiple levels of dir creation. The simple fix was to use Java's File#mkdirs instead of File#mkdir. How delicious...an easy bug.
  8. IO#read returned nil at EOF, instead of the correct "".
  9. File#join did not clean up multiple dir separators in a row, resulting in invalid paths like "app//models".
Busy, busy, busy. On top of these fixes I also helped resolve a few regressions discovered by a hardcore JRuby user (who also happens to be a team member). If only I had another eight hours a day to work on this stuff. But, I digress.

At any rate, you're here to read about the generators.

Rails Generators

Part of what makes Rails so agile and powerful is its beautifully simplistic code generation capability. By using the "generate" script, you can generate perhaps 90% of a working web application. With the 1.0 release, there are generators built in to create models, controllers, mailers, plugins, web services, and database and session migration code. There are also third-party generators for quickly generating other bits and pieces of a typical web app.

The generator code is fairly extensive, but unsurprisingly it does not exercise all of Rails' code. It does, however, represent the typical "first step" into the Rails world, and so I set out to get it running in JRuby. After many tests and fixes, documented in my other blog entries and immortalized in CVS, generators now work.

A few test runs to demonstrate:

C:\rails>jruby script\generate scaffold "myapp/Account" open close balance
create app/controllers/
create app/helpers/
create app/views/open
create test/functional/
dependency model
create app/models/
create test/unit/
create test/fixtures/
create app/models/account.rb
create test/unit/account_test.rb
create test/fixtures/accounts.yml

C:\rails>jruby script\generate web_service User add edit list remove
create app/apis/
exists app/controllers/
exists test/functional/
create app/apis/user_api.rb
create app/controllers/user_controller.rb
create test/functional/user_api_test.rb

C:\rails>jruby script\generate plugin SiteMinderAuthentication
create vendor/plugins/site_minder_authentication/lib
create vendor/plugins/site_minder_authentication/tasks
create vendor/plugins/site_minder_authentication/test
create vendor/plugins/site_minder_authentication/README
create vendor/plugins/site_minder_authentication/Rakefile
create vendor/plugins/site_minder_authentication/init.rb
create vendor/plugins/site_minder_authentication/lib/site_minder_authentication.rb
create vendor/plugins/site_minder_authentication/tasks/site_minder_authentication_tasks.rake
create vendor/plugins/site_minder_authentication/test/site_minder_authentication_test.rb

And so on. It's a pretty big milestone to finally have these generators working, and it means that one more step in the Rails development process now works under JRuby. I'm very pleased.

There are a few caveats (of course), but all told they're fairly minor. Rest assured they'll be remedied forthwith:
  1. Among other block arg tricks, specifying an index into an array or hash as a block arg is still nonfunctional. This will require interpreter work and possible parser changes.
  2. There are a couple warnings that display while running 'generator', but they are safely ignored. I believe they are overzealous warnings within the parser, left over from Ruby 1.6.
  3. I can neither confirm nor deny that the generated code and content is correct; however, it looks correct to my untrained eye.
  4. Not all the above fixes are committed; not all fixes committed are guaranteed not to cause regressions.
  5. I'm no Rails expert, despite swimming in the deepest parts of its ocean. I will be putting my Pragmatic 'Rails' book to heavier and heavier use now that we're finally putting JRuby on Rails.
The next big step will be continuing on to get Rails proper running with JRuby. The fixes I've contributed should help speed that process along, and Tom is already well into it. After wrapping up those last minor issues, I will endeavor to help him.

So there you have it. Great progress has been and is being made, and I'm having fun making it happen. Hopefully Rails actually running is coming very soon...stay tuned.

Thursday, March 09, 2006

Progress Update March 9

It's been a busy few days. We've made more good progress, so I figured an update was due.

Last Class

Tom discovered over the weekend that we had a problem setting the "last class" in the interpreter. This interfered with "super" calls working correctly.

The "last class" is used to point at the appropriate class hierarchy for the currently-executing code. We were setting "last class" to the actual class or module the current object was an instance of. This is correct for classes, but it was completely wrong for modules. In Ruby (both C and J), modules are internally inserted into the inheritance chain using module "wrappers". These wrappers become an implicit superclass of the class doing the include, and the wrapper's superclass points at the class's original superclass. So in the following example...

module X; end
class Y; end
class Z < Y; include X; end


..the explicit class hierarchy shows Z extending Y, while internally Z's superclass is a wrapper for X, whose superclass is then Y.

Having "last class" set appropriately in the interpreter is important because super calls must always be able to traverse the appropriate hierarchy. If, for example, you created a module-based "initialize" method (which some apps do to mix-in initialization behavior), and that initialize method called super, it must go to the appropriate superclass's initialize. Since modules do not have an initialize (and are uninstantiable), our original "last class" pointer at the module itself failed miserably. The initialize method was called correctly on the module, but super pointed at nothing.

Once Tom had figured out how we ought to fix this, I whipped up a patch. We modified the method-calling pipeline to send the appropriate "last class" through to execution. This allows module methods to now run within the context of the includer's hierarchy, rather than in their own hierarchies. With that fixed, super calls are now working correctly.

Array#unshift

Array#unshift now accepts an empty arg list. This is perhaps a Ruby 1.8.4 change. We had been forcing it to be at least one argument, as is documented in pickaxe. I noticed this problem while re-testing IRB with the "last class" fixes.

eval + Binding fixes

Binding is a fairly recent addition to JRuby. We had not ever supported binding up until a few weeks ago, which killed apps like IRB and Rails in the cradle. Adding it opened up a world of opportunity.

However, yesterday I discovered that code eval'ed with a Binding was not getting "self" correctly. Throughout the interpreter, there's one universal constant: "self" points to the current object. In a pure OO language like Ruby, all code is executed in the context of an object. Code at the top level executes within the context of the top-level Object instance, instance methods execute within the object they are called against, and so on.

When calling eval, you can choose to specify a binding or not. eval("code") evaluates the code in the current object (i.e. the current "self"), as well as within the current call frame, scope, class, etc. eval("code", some_binding) evaluates the code within another context entirely, with a different "self", frame, and friends.

Our eval, though properly setting up the bound context's frame and friends, was not correctly setting self. It was always making self the current object. I fixed it to set self to the correct object when called with a binding, and it appears to be working correctly.

IRB

With my update to Ruby 1.8.4 code and the "last class" fixes, IRB suddenly stopped working. Bummer. To make matters worse it started failing late last night, and I couldn't find all the issues.

However, on the bus ride to work today, I fixed both the eval and Array problems that were preventing IRB from working. Starting up IRB again I received a lovely surprise:

C:\JRubyWork\jruby>jirb
irb(main):001:0>


It is the good old IRB prompt, rather than the hideous IRB::Workspace::Nonsense!

The "self" issue prevented IRB from running at all with the "last class" fixes in place, and was the cause of two other issues with IRB: the need for --single-irb mode, and the ugly prompt.

With the eval issue fixed, IRB now runs without the need for --single-irb mode, and I have updated our "jirb" scripts to reflect this. It also runs with the appropriate prompt. This will make me much happier and our future IRB demos much prettier.

Rails

My goodness Rails is a gigantic thing.

We are continuing to make good progress on Rails. With all the above fixes, the generate script now runs through to initialization of the Routing subsystem. There it fails with an argument error that could be an interpreter or API issue.

I have also ventured a bit beyond this--by commenting routing out--and ran into a Ruby syntax we do not yet support:

x = {}
[1].each {|x[:foo]|}

Ruby at some point (probably in 1.8) added the ability to specify an array or hash index as the target for a block argument. While I can see the usefulness of this shortcut (rather than {|v| x[:foo] = v}) I must say this syntactic sugar is a bit sweet for my tastes. At any rate, it's there and we need to support it.

Rails so far only uses this syntax when printing out usage for the generate script. I modified that script to use the longhand version, and it was able to continue.

With routing commented out and the block arg syntax modified, the generate script ran to completion. We're getting closer!

Executing the generate script with a specific generator caused some other issues, whereby JRuby sees a yield without seeing a block to yield to. It's not functional yet, but it's moving right along.

Tom is also working on the other end of things, running Rails in "CGI mode" to test execution of an actual Rails request. At this point he's stuck on the same argument issue that's preventing routing from starting up, so that's what we'll work on next.

JavaOne 2006

As most of you will know, we will be presenting JRuby at JavaOne this year. We have almost finished our slides and will be sending them to Sun tomorrow. We obviously can't show you the slides themselves (since we want you to come see them in person) but we do hope to do most of the following five demonstrations:

  • Interactive JRuby with IRB
  • JDBC in Ruby
  • Swing in Ruby
  • Spring in Ruby
  • Rails
Obviously the Rails demo will only happen if there's something to show...but we're banking on having something working by then. We may have to drop one of the others for time, but I hope to touch on them all. We hope to see you all at JavaOne!

Thursday, March 02, 2006

IRB is GO!

I should post about things not working more often.

Within a few hours after my previous post, where I showed the world how IRB now starts up successfully in JRuby but does not work, I was back at it trying to fix the next few bugs preventing it from working. The first issue was a NullPointerException deep in the interpreter, when executing an "until" block. Our parser, for right or for wrong, was producing an AST "UntilNode" with no body. While this could be correct or incorrect behavior--since the "until" in question actually did have an empty body--we still were not handling it correctly. The interpreter assumed that all "until"s would have bodies, and when a body turned up null...kaboom. A fix to check for null and not attempt to evaluate the body was an easy, if not entirely kosher, way to fix it. Done.

However, nothing could prepare me for what followed.

C:\JRubyWork\jruby3>jruby C:\ruby\bin\irb
irb(#<IRB::WorkSpace:0x5a9c6e
>):001:0> x = 1
=> 1

I expected that the "until" bug would go away...that much was easy. however, I did not expect the variable assignment to work. "Ok," I thought, "that's better progress than I expected, but let's try something more complicated."

irb(#<IRB::WorkSpace:0x5a9c6e>):002:0> puts x
NameError: undefined local variable or method 'x' for
#<IRB::WorkSpace:0x5a9c6e>
from (irb):1:in `method_missing'
...

Ahh, there's the comforting disappointment I was used to. The 'x' variable had been declared and assigned, but for whatever reason, it was not visible in the current scope.

Normally, I would have continued on to fix this scoping issue, which certainly would have involved a complicated dive into JRuby internals, hunting for mishandled scopes, bindings, frames, and wrapper objects. In this case, however, I decided to give IRB's "single IRB" mode a try, which simplifies the logical scoping of the IRB workspace. What follows is a series of annotated IRB sessions running--yes, running successfully--under JRuby.

This first demo shows something basic: a multiline do/end array iteration.

C:\JRubyWork\jruby3>jruby C:\ruby\bin\irb --single-irb
irb(#<irb::workspace:0x175ace6>):001:0> [1, 2, 3].each do |i|
irb(#<irb::workspace:0x175ace6>):002:1* puts i
irb(#<irb::workspace:0x175ace6>):003:1> end
1
2
3
=> [1, 2, 3]

This confirmed several things:

  • method calls were working just fine
  • array instantiation and integer literals were ok
  • multi-line constructs were being handled correctly
It is this last one that surprised me a bit. I had not expected multi-line constructs to work so well and without any problems, but there it was. Playing around a bit more, I discovered some other surprises:
  • line editing was working successfully, and I could arrow-key left and right to correct mistakes
  • command history was also working, so that up and down arrow would retrieve the next and previous lines, respectively
  • tab completion does not work
Excluding the tab completion issue (hitting tab just inserts a "tab" character into the current line), the perfectly working line editing and command history totally blew me away. I have NEVER seen a console-mode Java application do such things so seamlessly, much less one running an interactive shell. It appears that IRB's fallback "StdioInputHandler" is far less "dumb" than I expected. It was making Java do things I didn't know Java could do. Excited, I pressed on.

This next demonstration tests the declaration and instantiation of a multiline class, another area I thought would never work correctly.

irb(#<irb::workspace:0x175ace6>):001:0> class MyClass
irb(#<irb::workspace:0x175ace6>):002:1> def hello
irb(#<irb::workspace:0x175ace6>):003:2> "Hello from IRB!"
irb(#<irb::workspace:0x175ace6>):004:2> end
irb(#<irb::workspace:0x175ace6>):005:1> end
=> nil
irb(#<irb::workspace:0x175ace6>):006:0> x = MyClass.new
=> #<myclass:0x1621fe6>
irb(#<irb::workspace:0x175ace6>):007:0> puts x.hello
Hello from IRB!
=> nil

Once again, JRuby (and IRB) thoroughly surprised me. Defining a class over multiple lines worked perfectly, just as you'd expect from IRB running under C Ruby. At this point, IRB was running so well I began to have some doubts. Could it be that IRB had called out to an external C Ruby process for running the interactive portion of the shell? Such a thing would not be unheard of; Rake launches external Ruby processes to run test cases, though you might never notice such a thing. There was, however, a simple way to confirm that I was actually seeing JRuby at work and not C Ruby: call Java code.

JRuby's greatest strength lies, unsurprisingly, in its ability to neatly tie Ruby and Java code together. For what other purpose would we want Ruby running in the JVM than to take advantage of the wealth of libraries the Java world has to offer? The integration is improving more and more with each release, and has become extremely powerful, usable, and above all very Ruby-like.

This next demonstration shows IRB calling Java code.

irb(#<irb::workspace:0x175ace6>):001:0> require 'java'
=> true
irb(#<irb::workspace:0x175ace6>):002:0> include_class "java.lang.System"
=> ["java.lang.System"]
irb(#<irb::workspace:0x175ace6>):003:0> System.out.println("Hello from Java")
Hello from Java
=> nil

Now some of you may not realize what this means. The ability to interactively script and exercise Java code from within an IRB session has huge potential for testing Java code, debugging JRuby (perhaps that's more exciting to me...oh well), and providing all the interactive goodies that Rubyists have taken for granted with the power and variety of Java's capabilities.

So, a final demonstration is in order.

irb(#<irb::workspace:0x175ace6>):001:0> require 'java'
=> true
irb(#<irb::workspace:0x175ace6>):002:0> include_class "javax.swing.JFrame"
=> ["javax.swing.JFrame"]
irb(#<irb::workspace:0x175ace6>):003:0> include_class "javax.swing.JButton"
=> ["javax.swing.JButton"]
irb(#<irb::workspace:0x175ace6>):004:0> frame = JFrame.new("my frame")
=> javax.swing.JFrame[long desc omitted...]
irb(#<irb::workspace:0x175ace6>):005:0> button = JButton.new("my button")
=> javax.swing.JButton[
long desc omitted...]
irb(#<irb::workspace:0x175ace6>):006:0> frame.contentPane.add(button)
=> javax.swing.JButton[
long desc omitted...]
irb(#<irb::workspace:0x175ace6>):007:0> frame.setSize(200, 100)
=> nil
irb(#<irb::workspace:0x175ace6>):008:0> frame.show
=> nil

The result:

It works! It really works!

So there you have it. With a few small caveats (like --single-irb), IRB is actually up and working in JRuby, far sooner than I expected. This is turning out to be a really good week.