Skip to content
Print

JRuby Event Proxy

18
Feb
2007

One of the things which really which annoys me about JRuby is the lack of a clean syntax corresponding to Java’s anonymous inner classes. Now, Ruby has a neat little syntax trick called blocks. It really would be the ultimate solution if you could use Ruby blocks in things like Java event listeners instead of subclasses. Here’s a quick example:

test3.rb

require 'java'
 
JFrame = javax.swing.JFrame
JButton = javax.swing.JButton
FlowLayout = java.awt.FlowLayout
ActionListener = java.awt.event.ActionListener
 
class CustomActionListener < ActionListener
    def actionPerformed(e)
        puts 'Button Clicked'
    end
end
 
frame = JFrame.new 'Test Frame'
frame.layout = FlowLayout.new
 
button = JButton.new 'Click Me'
button.addActionListener CustomActionListener.new
frame.content_pane.add button
 
frame.setSize(200, 75)
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.visible = true

Notice anything annoying about the above code? We’re using this really nice, clean Ruby syntax with this incredibly ugly listener syntax (the custom class). Now, Ruby does have a slightly cleaner recourse called anonymous classes which is a little nicer, but still fairly ugly:

test2.rb

require 'java'
 
JFrame = javax.swing.JFrame
JButton = javax.swing.JButton
FlowLayout = java.awt.FlowLayout
ActionListener = java.awt.event.ActionListener
 
frame = JFrame.new 'Test Frame'
frame.layout = FlowLayout.new
 
button = JButton.new 'Click Me'
frame.content_pane.add button
 
listener = Class.new(ActionListener).new
def listener.actionPerformed(e)
    puts 'Button Clicked'
end
button.addActionListener listener
 
frame.setSize(200, 75)
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.visible = true

As you see, this is a little nicer primarily because there’s no extraneous named-class to be defined. However, it still lacks a truly clean syntax. What would be really nice is if we could write code like the following:

test.rb

require 'java'
require 'jruby_utils'
 
JFrame = javax.swing.JFrame
JButton = javax.swing.JButton
FlowLayout = java.awt.FlowLayout
ActionListener = java.awt.event.ActionListener
 
frame = JFrame.new 'Test Frame'
frame.layout = FlowLayout.new
 
button = JButton.new 'Click Me'
button.addActionListener proxy_method(ActionListener, 'actionPerformed') { |e|
    puts 'Button Clicked'
}
frame.content_pane.add button
 
frame.setSize(200, 75)
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.visible = true

I think that this is pretty clean. There’s no class definition anywhere. In fact, it’s all done in a block with the ActionEvent instance passed as a parameter.

This is cute and all, but it doesn’t seem to be very useful. To make this work, we’re utilizing a fictional method: “proxy_method”. Intuitively, this method should take a Class and a String defining a method name to override as parameters (along with a block), and then reflectively wrap the block into an overriding method within a subclass of the specified Class. In Java, this would be absolutely impossible – not to mention unnecessary and somewhat bizarre since Java a) defines no such “block” syntax, and b) Java already has anonymous inner classes, so there really isn’t a problem to solve. However, Ruby has a far more dynamic nature and it is actually possible for us to write proxy_method. The implementation looks something like this:

jruby_utils.rb

require 'java'
 
def proxy_method(superclass, name, &block)
    clazz = Class.new(superclass)
    instance = clazz.new
    instance.instance_eval do
        @name = name
        @block = block
 
        def method_missing(missing, *args)
            if missing.id2name == @name
                @block.call args
            else
                super
            end
        end
    end
    instance
end

As you can see, this is pretty much doing reflectively what we worked out mentally above. proxy_method accepts two parameters, a Class and a String. The third parameter is a special “block parameter” which is assigned by Ruby to the specified block wrapped into a Proc instance. (the specified block being the block used in conjunction with the proxy_method invocation in the example above) Then, the method dynamically creates an anonymous class with the specified superclass and assigns it to the clazz variable. clazz (which really represents a bona fide type) is instantiated and a dynamic code block is invoked. (a dynamic instance block is effectively a reflective body to a class) The name and block parameters are assigned to instance fields within the dynamic block and a new method is defined (or rather overridden).

Now, in Ruby, the method_missing method is a special method which is invoked by Ruby in the case of a method invocation which cannot be resolved against the specified instance. The default implementation (in Object) raises an exception, but by overriding this method, developers can easily add arbitrary methods to instances dynamically. In this case, we check to see if the missing method’s name matches the method name we were passed as a parameter of proxy_method. If the names do indeed match, then we invoke the block we were passed in the first place, passing all of the parameters which were intended for the missing method. Then, the value returned from the block is returned from method_missing. (though, in the case of an event listener this really isn’t necessary)

The a vitally important part of our code is the else statement within method_missing. This call passes the method missing invocation up the hierarchy if it doesn’t match the method name with which we are concerned. This is important because this allows either the runtime to throw an exception (in case of a truly missing method), or another method_missing implementation in a super-class to also check for method to reflectively invoke. In our case, the method_missing call will get sent on to the JavaObject class (defined by JRuby) which will check for any corresponding methods in the associated Java class. If none are found, it too will send the method_missing invocation up the hierarchy where it will eventually reach Object, which will raise an exception.

Just as an aside, proxy_method is also suitable for usage in other situations such as creating Java threads directly in JRuby. Consider the following example:

test4.rb

require 'java'
require 'jruby_utils'
 
JThread = java.lang.Thread
thread = proxy_method(JThread, 'run') { puts '...in another thread' }
thread.start

This code will print “…in another thread” once to stdout. But the cool bit is this is actually equivalent to the following code:

test4.rb

require 'java'
 
JThread = java.lang.Thread
class ThreadImpl < JThread
    def run
        puts '...in another thread'
    end
end
 
thread = ThreadImpl.new
thread.start

Notice how much cleaner it is with proxy_method? :-)

Hopefully, you find this to be a useful overview of Ruby method reflection in general, and more specifically, a useful method which can greatly simplify any JRuby code using Java APIs designed for use with anonymous inner classes.

Comments

  1. Incidentally, neither Java thread example above will work with JRuby 0.9.2 because JRuby 0.9.2 does not allow method overriding from superclasses.

    Just thought I’d head off any comments at the pass. :-)

    daniel Sunday, February 18, 2007 at 10:12 pm
  2. This is an interesting post. There’s so many ways in JRuby we could make working with Java interfaces and classes easier, and your samples are evidence of that. And the beauty of Ruby is that you don’t even have to modify JRuby to add these features, unlike many other Java-based dynamic languages. We’re very interested in improvements like this, and really the only thing holding back evolution of our Java integration APIs and syntax are good examples and use cases like you’ve presented here.

    Charles Oliver Nutter Sunday, February 18, 2007 at 10:57 pm
  3. I think the way of proxying the Java Listener classes is a great way of keeping the code clean and ruby-like. But isn’t there possibly an even cleaner way of creating anonymous classes out of JRuby.

    module Swing
    include_package ‘javax.swing’
    include_package ‘java.awt’
    include_package ‘java.awt.event’
    end

    button.add_action_listener(Swing::ActionListener.new do
    def actionPerformed event
    puts “Click!”
    end
    end)

    Is it possible to provide the Java-Proxy-Classes with a block, which is then evaluated to add the defined methods to the proxy?
    This would be similar to the concept of anonymous classes in Java. The lower code makes it possible to define methods via block evaluated by the initialize-method, but these are private, but it shows the concept. Methods defined via Module::define_method are public. Maybe that can be used to solve this problem.

    I think a way that does not need thousands of Listener classes like the way with the proxy_method is good, but a way without calling a helper-method is even better. A way of defining anonymous classes
    in a way that uses blocks seems best, because it is easy an can even be understood by Java-guys.

    Is this possible or am I on the wrong way?

    PSEUDO_CODE (does not work, but shows what I mean):
    ——————————————

    class JavaProxy
    def initialize &block
    block.call # evaluates the block
    end
    end

    include_class ‘java.awt.event.ActionListener’ #ActionListener is one of the JavaProxies

    ActionListener.new do
    def actionPerformed # define the method in a block, which is then evaluated
    puts “CLICK!”
    end
    end

    Martin Plöger Saturday, March 10, 2007 at 9:55 am
  4. @martin

    Honestly, I wasn’t aware of that particular JRuby syntax trick. That does have certain advantages, but it’s still a little bulky syntactically. It would depend on my mood which I would prefer (proxy_method or JavaProxy). :-)

    Incidentally, you can make this work like so:

    module JavaClassProxy
    def initialize(&block)
    block.call
    end
    end

    ActionListener = java.awt.event.ActionListener

    class ActionListener
    include JavaClassProxy
    end

    ActionListener.new do
    def actionPerformed(e) # we do need the method parameter
    puts “clicked”
    end
    end

    I haven’t tested the above code, but afaik it should work just fine.

    Daniel Spiewak Saturday, March 10, 2007 at 1:32 pm
  5. @daniel

    Thanks for your reply. I was just searching for an easy way of defining anonymous classes.
    And, maybe I can’t hide my java-roots but this syntax seemed easy to understand and easy to define without any auxiliary method or library. (Okay, a little change like that what you posted is still necessary). I believe that I would use the proxy_method in some cases and the other in some other cases. Both seems acceptable to me.

    Martin Plöger Monday, March 12, 2007 at 3:21 pm
  6. I’m glad I found this post because I’m trying to integrate JRuby with the ThinWire framework, which has been working well, but after getting all the bindings panned out I realized that event listener binding was pretty cumbersome. The samples provided take the very verbose approach that you first outline, which is a smack in the face coming from Java and anonymous classes.

    In any case, I’ve worked extensively with the Mozilla Rhino JavaScript engine in the past and it provides a very clean binding layer for listeners/anonymous classes that I thought I’d mention here:

    IN JavaScript:

    comp.addActionListener(function(event) {
    //do what you need to do for the actionPerformed method
    });

    I would imagine that with JRuby, an equivalent would be:

    comp.add_action_listener {|event|
    //do what you need to do for the actionPerformed method
    }

    It should be possible for the JRuby engine to determine that the required type for addActionListener is of type ActionListener and that there is only one method on the interface definition, therefore the block should be bound to that.

    I hope JRuby integrates something like that in the future. I’ll use the anonymous syntax described by Martin in the meantime.

    Joshua Gertzen Thursday, May 17, 2007 at 1:36 am
  7. Hi i have a question.
    For example i had a JTextField and a Button inside my JFrame.
    I want to print the text inside the JTextField when the button is pressed.
    But my problem is that i cant access the JTextField instance inside the actionPerformed method.
    Ive tried this based on test3 and test2.

    I then use @@textfield and it worked but i dont think it is a good way of doing this.
    How can i do the same thing but without using @@

    @@tfTexto = JTextField.new 15

    listener = Class.new(ActionListener).new
    def listener.actionPerformed(e)
    puts @@tfTexto.text
    end

    frame = JFrame.new ‘Test Frame’
    frame.layout = FlowLayout.new
    button = JButton.new ‘Click Me’
    button.add @@tfTexto
    button.addActionListener listener

    frame.content_pane.add button

    frame.setSize(200, 75)
    frame.default_close_operation = JFrame::EXIT_ON_CLOSE
    frame.visible = true

    virux Tuesday, July 3, 2007 at 11:53 am
  8. @virux

    You’ll have to do something with a block, like in the event proxy shown in the post. The problem here is that the anonymous class you created doesn’t have access to variables which exist solely within the scope of the Kernel override (which is what a non-class script is). Your other options is to encase the entire example in a class and make the anonymous class a top-level class in the same way as the example class. Not a terribly good solution IMHO. If I were you, I’d use the block proxy.

    Daniel Spiewak Tuesday, July 3, 2007 at 11:58 am
  9. @daniel

    Block remembers context in which it was defined. So when you finally invoke:

    block.call

    in your example you’ll most probably notice that ’self’ isn’t necesserily the instance of ActionListener itself. Your example works fine however, it will cease to do so whenever you create instance of ActionListener inside some other class’ method. I’m trying to figure out how to make it work in such cases too. Any ideas?

    Marcin Olak Monday, January 7, 2008 at 12:32 pm
  10. Hmm, I’m not sure what you mean by a case where it fails. Are you talking about creating an instance of ActionListener elsewhere (independently of the component instantiation) and then carrying it around until needed?

    Replacing @block.call with “instance_eval @block” should solve the context problem you outlined.

    Daniel Spiewak Monday, January 7, 2008 at 12:38 pm
  11. Or you could create a new method in the JButton class,

    button.on_clicked {
    p ‘clicked good’
    }

    if you set it within the JButton class
    see:
    https://gist.github.com/705822

    roger Thursday, November 18, 2010 at 3:46 pm
  12. http://www.ruby-forum.com/topic/479162#962470

    Martin Harrigan Monday, November 22, 2010 at 7:59 am

Post a Comment

Comments are automatically formatted. Markup are either stripped or will cause large blocks of text to be eaten, depending on the phase of the moon. Code snippets should be wrapped in <pre>...</pre> tags. Indentation within pre tags will be preserved, and most instances of "<" and ">" will work without a problem.

Please note that first-time commenters are moderated, so don't panic if your comment doesn't appear immediately.

*
*