We’ve kicked off a new project that extends well beyond standard webapp frameworks and tools including significant instant messaging integration. Portions of the project will use Ruby on Rails for web functionality. I know that the open source Jabber software from Jive Software is excellent quality and feature rich. Would JRuby be a good tool for leveraging these Java libraries in a Ruby environment?
Here’s a toy chat client and parrot bot I wrote for the Google Talk service using JRuby and the Java Smack API from Jive’s open source portal ignite realtime.
Dependencies</b
I’m using a local install of JRuby 1.1RC1 and downloaded the Smack 3.0.4 API jars.
lib/ smack.jar smackx_debug.jar smackx.jar [/sourcecode]SmackHelper</b
I wrote a Ruby module (lib/smack_helper.rb) that provides a simple wrapper around the Smack classes:
require 'java' require 'smack.jar' require 'smackx.jar' require 'smackx-debug.jar' # Helper methods for Smack API module SmackHelper # Enable debug console. Do this before opening any connections def debug org.jivesoftware.smack.XMPPConnection.DEBUG_ENABLED = true; end # Connect to Google Talk service and log in def connect(username, password, server = 'talk.google.com', port = 5222, service = 'gmail.com') config = org.jivesoftware.smack.ConnectionConfiguration.new(server, port, service) @connection = org.jivesoftware.smack.XMPPConnection.new(config) @connection.connect @connection.login(username, password) @connection end # Start a chat with a user on Google Talk def chat(username) puts "Chatting with #{username}..." @chat = @connection.chat_manager.create_chat(username, StdoutMessageListener.new) end # Parrot back to the other participant in the current chat everything she says def parrot puts "Parroting #{@chat.participant}." @chat.add_message_listener ParrotMessageListener.new end # Send a message for the current chat def say(message) @chat.send_message message end # Disconnect from the Jabber service def disconnect @connection.disconnect @connection = nil @chat = nil puts "Disconnected." end # Implements MessageListener to parrot everything received class ParrotMessageListener include org.jivesoftware.smack.MessageListener # implement interface def processMessage(chat, message) # process_message does not work chat.send_message "Polly says '#{message.body}'?" if !message.body.empty? end end # Implements MessageListener to print message body to STDOUT class StdoutMessageListener include org.jivesoftware.smack.MessageListener def processMessage(chat, message) puts "#{chat.participant}: #{message.body}" if !message.body.empty? end end endThe only thing that tripped me up here was implementing the org.jivesoftware.smack.MessageListener interface. You implement a Java interface by including it in your Ruby class. At first I implemented 'process_message' instead of 'processMessage' which is not an entirely unreasonable thing to do given JRuby's nice conversion from CamelCase to Ruby conventions in all other cases. The bummer was that I got no errors with that implementation. My listeners just didn't receive messages. JRuby's proxy implementation should probably do a better job of handling this.
chat.rb</b
A script to use this module might look like:
$:<< File.dirname(__FILE__) + '/lib/' require 'smack_helper' include SmackHelper debug # enable connection debug console connect 'asalant', '****' # connect to Google Talk and log in chat 'myfriend' # chat with myfriend@gmail.com parrot # parrot everything they say # read messages from STDIN (empty message ends chat) while !(msg = gets.chomp).empty? say msg end disconnect exitA chat session might look like:
$ jruby chat.rb Chatting with myfriend... Parroting myfriend. You there? myfriend: I am! myfriend: Did you just repeat me? No, that's my parrot. myfriend: Oh. That's lame. Disconnected. $irb</b
Or you can use it from irb:
$ jruby -S irb irb(main):001:0> $:< ["/usr/local/jruby-1.1RC1/lib/ruby/site_ruby/1.8",..."] irb(main):002:0> require 'smack_helper' => true irb(main):003:0> include SmackHelper => Object irb(main):004:0> connect 'asalant', '****' => # irb(main):005:0> chat 'myfriend' Chatting with myfriend... => # irb(main):006:0> say 'you there?' => nil irb(main):007:0> myfriend: I am! say 'oh, hi.' => nil irb(main):008:0> disconnect Disconnected. => nil irb(main):009:0> exit