Blog

Scripting Java Libraries With JRuby

By | August 20th, 2009 at 11:08AM

One of the most powerful features of JRuby (and one that’s unique to JRuby) is the fact that you can call out to any Java library as it if were just another piece of Ruby code. In fact, other than a few small details, you might not even realize you’re using Java libraries. Today I’ll walk through a quick example of “scripting” the MIDI support built into the Java platform.

Accessing Java

Because JRuby always strives to be a solid Ruby implementation first, we don’t normally expose nonstandard features like Java integration. That said, it’s easy to pull it in… just require the ‘java’ library:

require 'java'

Alternatively, you can -rjava on the command line. And yes. That’s all there is to it. You might want to consider taking a break at this point, so your manager doesn’t realize just how simple it was to make this happen. Grab a snack, play some video games, and once you’re done with all that hard work, we’ll be ready to pull in some Java APIs!

Importing Java Classes

Once you’ve loaded Java support, all the Java classes that the JVM can see are available to you as though they were normal Ruby classes. You can access them directly:

java.lang.System.out.println "hello, world"

This “package” access works for any Java classes in packages starting with “java”, “javax”, “org”, or “com.” In order to avoid polluting the top-level namespace (we actually define Kernel methods for “java”, “javax”, “org”, and “com” to provide this feature), other packages need to be scoped under the Java module:

reader = Java::jline.ConsoleReader.new
obj = Java::SomeClassWithNoPackage.new

You can actually “import” these classes in a couple different ways. First, you can simply assign them to a constant:

System = java.lang.System

You can also import them with the “java_import” Kernel method (also available as “import”, but that frequently conflicts with libraries like Rake):

# This is basically the same as "JButton = javax.swing.JButton"
java_import javax.swing.JButton

Calling Methods

Once you have access to the Java classes, there’s really nothing tricky about using them. Call the methods you want to call, and you’ll get back the results you’d expect to get:

# display current system ns-granularity timer value
puts System.nanos

# Java constructors are just Class#new, as in Ruby
frame = JFrame.new "My window!"

# notice we allow setSize to work as set_size
frame.set_size 300, 300

# set* and get* properties work like Ruby attributes
frame.always_on_top = true
frame.visible = true

button = JButton.new "Press me!"
frame.add button

A MIDI Keyboard

Ok, let’s put it all together using a more entertaining library: Java’s MIDI API. The MIDI API is located under the javax.sound.midi package, which contains all the classes you’d need to load, construct, and play MIDI sequences.

In this first MIDI example, we’ll listen for a line of text at a time and convert that text into a playable MIDI sequence. First we’ll load Java support and import the classes we need:

require 'java'

midi = javax.sound.midi
import midi.MidiSystem
import midi.Sequence
import midi.MidiEvent
import midi.ShortMessage

Notice I assigned the javax.sound.midi package to the ‘midi’ local variable in order to shorten the import lines a bit. It’s all just objects, after all…

Next up we’ll want a simple gets loop with an exit clause:

while (line = gets) !~ /exit/

Ok, now we get to the meat of our app: building a sequence. For every line, we’ll construct a new sequence. It’s not the most efficient way, but it’s nice and simple for now. Then for each character in the sequence we’ll add a NOTE_ON event using the character code as the note. Finally we’ll add a STOP event to stop playing the sequence.

  # Construct a new sequence using 2 beats per quarter note
  seq = Sequence.new Sequence::PPQ, 2
  track = seq.create_track

  # add each character into track as a note
  for i in 0...line.length do
    msg = ShortMessage.new
    # args here are (message, data1, and data2 according to MIDI spec)
    # NOTE_ON, character code, and a mid-range note velocity of 64
    msg.set_message(ShortMessage::NOTE_ON, line[i], 64)
    track.add MidiEvent.new(msg, i)
  end

  # add a STOP to end the track
  msg = ShortMessage.new
  msg.message = ShortMessage::STOP
  track.add MidiEvent.new msg, i + 1

Finally we’ll play the sequence and end the loop. There’s also a hard JVM System.exit call here, to shut down the internal event threads that MIDI support starts.

  # open the sequencer and play the sequence
  seqer = MidiSystem.sequencer
  seqer.open
  seqer.sequence = seq
  seqer.start
end
java.lang.System.exit(0)

Give it a try! Here’s the full source to our first MIDI script.

Making it More Interactive

Given what we’ve learned, perhaps we can make our MIDI keyboard app a little more interactive. Ideally we’d like it to respond to keystrokes live,turning the note on when the key is pressed and turning it off when the key is released. For that, we’ll launch a GUI window to receive keystroke events.

Our preamble this time only imports three classes, since we won’t be constructing sequences this time.

# MIDI keyboard take two!
require 'java'

import javax.sound.midi.MidiSystem
import javax.swing.JFrame
import java.awt.event.KeyListener

In order to make this interactive, we’re going to access the system synthesizer directly. Here we retrieve the default synthesizer, open it, and grab its channel number 0:

# Prepare the synth, get channel 0
synth = MidiSystem.synthesizer
synth.open
channel = synth.channels[0]

There are a few ways to receive keystroke events directly at the command line, but a lot of them are system-specific or require special libraries. For simplicity, we’ll launch a GUI frame and just set it up to receive keystroke events.

# Prepare a frame to receive keystrokes
frame = JFrame.new("Music Frame")
frame.set_size 300, 300
frame.default_close_operation = JFrame::EXIT_ON_CLOSE

Here we’re going to use another feature of JRuby: implementing an interface using a block of code. Every Java interface has a class method “impl” that receives a block and produces an object that implements that interface using the given block. The block itself is called for every interface method, receiving the name of the method and the original arguments as parameters.

In this case, we’re going to handle the keyPressed and keyReleased methods on the KeyListener interface by turning notes on and off, again using the character code as the note:

# Listen for keystrokes, play notes
frame.add_key_listener KeyListener.impl { |name, event|
  case name
  when :keyPressed
    channel.note_on event.key_char, 64
  when :keyReleased
    channel.note_off event.key_char
  end
}

And finally, we’ll show the frame to get the whole thing started:

# Show the frame
frame.visible = true

Here’s the source for our MIDI keyboard.

Using JRuby, a whole new world of libraries are at your fingertips, and you never have to write a line of Java code. This is just one use case, but there really are an unlimited number of great ways JRuby allows you to leverage technologies that are otherwise out of the picture for Rubyists.

In the coming weeks and months we’ll be working on a series of posts about how you can use JRuby, why we love it, and what you can look forward to in the future. If you’ve got a hankering for something specific, leave a comment—we aim to please :D

  • richy

    If I were able to call out to Ruby library in Java, it would be more wonderful.

  • Anthony Eden

    Is it not possible to embed the JRuby interpreter in a Java application? It used to be possible back in the days of BSF (is anybody even using that anymore) but I have no idea if that sort of embedding has been retained in recent versions of JRuby.

  • Anthony Eden

    Thanks for publishing this post Charles, and more importantly thanks goes out to the entire JRuby team who have made this integration so seemless. I have been using the integration layer between JRuby and Java quite effectively lately to wrap libraries in Java with Ruby interfaces (see http://github.com/aeden/jruby-http-reactor and http://github.com/aeden/jruby-typica for examples). The integration layer is quite easy to use and makes wrapping thread-safe Java libraries a lot easier than I thought it would.

  • http://blog.headius.com Charles Nutter

    Certainly…the standard Java scripting APIs work fine (Bean Scripting Framework (BSF) or javax.script in Java 6), and very soon it will also be possible to just generate normal Java classes from Ruby code that you can compile against and call like any other Java class. Lots of great things are coming, especially as relates to calling Ruby from Java.

  • http://blog.headius.com Charles Nutter

    Thanks a lot Anthony…what I've seen of your projects looks really awesome, and I hope we're able to keep improving JRuby's Java integration to ease some of the stickier areas. In general, we find that it works really well too…but I know where the cracks are and can't wait to fix them.

  • richy

    I know a little about javax.script, but to be honest, it's painful to use. However, scripting Java from JRuby is no pain. Though, codes like 'rubyEngine.eval("puts 2 + $label", context);' make it possible to call some Ruby in Java, libaries like activerecord can't be called in the way.
    If activerecord can be called in Java as easily as scripting Java from JRuby, I believe SSH will soon change to SSA with JRuby and the whole Java Web world will change.

  • Mihael Konjević

    It would be great if you could show some communication between Rhino and JRuby. I tried to use it but got into some problems with argument types.

  • http://www.parachange.com Ian Farbrother

    Nice article. Thanks !!!

  • http://blog.headius.com Charles Nutter

    Actually Yehuda Katz has probably done the most playing around with integrating JRuby and Rhino…perhaps we should have him write up a blog post on it?

  • Mihael Konjević

    Yeah, that would be great.

  • James

    <quote>One of the most powerful features of JRuby (and one thats unique to JRuby)" is the fact that you can call out to any Java library as it if were just another piece of Ruby code</quote>

    What am I missing here? There are plenty of languages that run on the JVM that call Java code seamlessly. Groovy, Scala,, to name a couple. In fact, why would you put a language on the JVM if not to have access to the Java SDK?

  • http://blog.headius.com Charles Nutter

    I suppose I could have made it clearer, but I meant "unique to JRuby among Ruby implementations." There are bridges from MRI to Java, but they're nowhere near as seamlessly integrated as JRuby's Java integration.

  • http://jrubyist.wordpress.com Dan Tylenda-Emmons

    Can I contribute to the list of examples? I use jruby on a regular basis for several projects in a corporate environment. Each of these applications use jruby to interact with other live java app servers, using third party java libraries, and also C libraries. ~jrubyist (Dan Tylenda-Emmons)

  • http://blog.headius.com Charles Nutter

    Certainly! You can post things to the wiki, which is probably the best way, or post your blog entries to the JRuby list and we'll try to start aggregating a list of them somewhere.

  • Mathias

    The JRuby MIDI-example is great. Thanks for that! Is there any way to get MIDI-in working in such an easy way, too? Trying to figure it out myself, but not sucessful yet…

  • http://blog.headius.com Charles Nutter

    I've never tried using MIDI-in myself, but it ought to work as easily. Perhaps start a thread on the JRuby mailing list and we can try to figure out how to make it work together?

  • Mathias

    Thanks Charles, I got it up and running now – I found some code how to open up MIDI-input here:
    http://www.jsresources.org/examples/MidiInDump.ht…
    Then I implemented the Java-Interface in JRuby by "include javax.sound.midi.Receiver" in my own class providing an open, send and close-method.

  • Mathias

    I built a very basic JRuby I/O class (notes, controllers, pitchbend, aftertouch), you can find it here:
    http://gist.github.com/189610 with examples http://gist.github.com/189611 and http://gist.github.com/189613 tested with ruby-processing, see:
    http://github.com/jashkenas/ruby-processing

  • http://citizen428.net/?p=396 Using Java libraries from Clojure – citizen428.blog()

    [...] reading Scripting Java Libraries With Ruby on the Engine Yard blog, I decided to translate the two example programs to Clojure. Please keep in [...]