Java Integration - Can you create Anonymous Interfaces/Subclasses in ruby?

I am experimenting with JRuby/SWT/Mozilla/JavaXPComm. It is a lot of
fun, but one of the things that has bothered me a bit is how to create
listeners for UI events. I want to specify listeners for events using
blocks and not have to create ruby classes for every listener
interface or listener adapter. Are there any helper libraries out
there for constructing GUI’s from jruby and/or are there any documents
on integration patterns for jruby?

The rest of this email describes 2 approaches I used for implementing
callbacks/listeners.


I am using an org.eclipse.swt.browser.Browser object, which has an
addProgressListener method. There is a ProgressListener interface and
a ProgressAdapter which has empty methods for the ProgressListener
methods. This is similar to various swing listeners. When the
browser has completed loading some document, I want to run some code.
(It might do things like find html elements and attach ruby event
handlers to them…)

I can create a ruby subclass of ProgressAdapter like so:

class OnCompleted < Java.org.eclipse.swt.browser.ProgressAdapter

def initialize(&block)
@block = block
end

def completed(event)
@block.call(event)
end

end

In my application class, I can then use the OnCompleted ruby class to
register a block of code like this:

@browser.add_progress_listener(OnCompleted.new do |event|
  on_completed
end)

This works, but feels so clunky. There are many different types of
listeners in SWT and I’d like a dynamic way to create these inline,
similar to java’s anonymous classes. I’d like to supply a hash of
blocks to implement an interface or subclass an abstract class.

Here is an example of subclassing ProgressAdapter with method
overrides given as a hash of blocks.

@browser.add_progress_listener(ProgressAdapter.java_callback(
  :completed => lambda { |obj,event| on_completed }
))

I implemented this by adding a method, java_callback, to Module.

class Module

def java_callback(methods={})
if self.respond_to? :java_class and self.java_class.interface?
a_class = Class.new
a_class.send :include, self
else
a_class = Class.new self
end
methods.each do |mname, block|
a_class.send(:define_method, mname) do |*args|
block.call(self,*args)
end
end
a_class.new
end

end

This is working for me right now, but I am not creating many listener
instances yet. I imagine that creating dynamic classes would
eventually become a memory problem, since I doubt that classes are
garbage collected in Ruby.

I then tried to get around the memory issue, by caching a single
dynamically created subclass per listener interface/adapter. I was
then hoping to override methods on the object instance of the subclass
after it was constructed. This can be accomplished through the method
instance_eval. However, these object instance methods did not seem
visible to java.

Anyway, I’m hoping somebody out there had some clever, yet clean ways
of implementing listener interfaces.

Thanks.


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

On Mon, Jun 16, 2008 at 11:01 PM, Bryan Castillo
[email protected] wrote:

Anyway, I’m hoping somebody out there had some clever, yet clean ways
of implementing listener interfaces.

See this write-up in the wiki for one example:

http://wiki.jruby.org/wiki/Calling_Java_from_JRuby#Closure_conversion

We don’t have this documented nearly enough yet apparently, because I
don’t see too many people writing about it publicly, but this approach
has been working since shortly after the 1.0 release.

/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

That document says that the block is called for any method called on
the interface. What happens with multiple methods on the interface?
Is there a way to determine which method is being called when you are
in the block?

On Tue, Jun 17, 2008 at 11:35 AM, Nick S. [email protected]
wrote:

We don’t have this documented nearly enough yet apparently, because I


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

On Tue, Jun 17, 2008 at 12:12 PM, Bryan Castillo
[email protected] wrote:

That document says that the block is called for any method called on
the interface. What happens with multiple methods on the interface?
Is there a way to determine which method is being called when you are
in the block?

No. It’s best suited for single method interfaces. There is a module
called Proc::CatchAll in JRuby that implements the behavior [1].

If you wanted to know what method was being called, you could use an
adapter as follows. The catch is that you need to include/extend the
java interfaces ahead of time, otherwise the CatchAll behavior will be
mixed in for you, overriding any method_missing that you implement.

class MultiProc
def initialize(*args, &block)
args.each {|a| extend a if Module === a }
@proc = block if block
@proc = args.last if Proc === args.last
end

def method_missing(method, *args)
@proc.call method, *args
end
end

mp = MultiProc.new(java.awt.event.ContainerListener) {|meth,*args|
puts “You called #{meth} with #{args.inspect}”}
=> #<MultiProc:0x742397 @proc=#Proc:0xe3bfb6@:4(irb)>
mp.componentAdded nil
You called componentAdded with [nil]

Cheers,
/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

On Tue, Jun 17, 2008 at 1:23 PM, Nick S. [email protected]
wrote:

No. It’s best suited for single method interfaces. There is a module
called Proc::CatchAll in JRuby that implements the behavior 1.

Sorry, forgot 1:

/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email