JRuby on Eclipse RCP

From JRubyWiki

Jump to: navigation, search

This page will follow a project developing an airport fuelling system. We will try to describe the different components used, and some of the pitfalls detected, but more importantly focus on working examples and best practises.

EVERYONE is allowed to comment, add or edit to this page! The more views the better!

Contents

Purpose

AIFUDIS offers information about flights at one airport to a Dispatcher controlling the fuel trucks at the airport. The dispatcher then assigns operators and their trucks to specific flights. The operators then fuel the aircrafts and report progress and the finished transaction back to the central system and the dispatcher.

At the end of the day reports on all transactions and the state of the trucks and fuel tanks are used to send invoices and order more fuel.

Architecture

Dispatcher

  • Eclipse RCP 3.4
  • Java 6
  • JRuby 1.1.2 in selected parts.
  • Firefox 3
  • Spring 2.0

Operator

  • Eclipse RCP 3.4
  • Java 6
  • JRuby 1.1.2 in selected parts.

Administration

  • Ruby on Rails 2.1
  • JRuby 1.1.2
  • Java 6
  • PostgreSQL 8,3

Communicator

  • Ruby on Rails 2.1
  • JRuby 1.1.2
  • Java 6
  • ActiveMessaging Plugin
  • ActiveMQ 5.1
  • PostgreSQL 8,3

Flight Importer

Polls flights from an Oracle 10 database using ActiveRecord.

  • Ruby on Rails 2.1
  • JRuby 1.1.2
  • Java 6
  • ActiveMessaging Plugin
  • ActiveMQ 5.1
  • PostgreSQL 8,3


Progress

0.0.0 2008-05-15 Infrastructure

  • All the main components of the system are up and running with no functionality. That means all infrastructure is in place. Woohoo!


0.1.0 2008-06-16 Basic models, Working Flight List, Administration of base data.

  • The release went well and the customer is happy! Below is a screen shot of the flight list.

Currently, JRuby is used to fetch data from an external Oracle database, post flight updates to an ActiveMQ topic, handle all administration using a Rails application, and to unmarshal a YAML stream of flight updates on the dispatcher.

Image:dispatcher_0.1.0.jpg

0.2.0 2008-08-03 First operator functions

  • Due to customer priorities we started working on the vehicle system. We set up the basic GUI and the integration with the fuel meter.
  • Eclipse RCP was dropped on the Vehicle system since it added complexity, made build and deployment more difficult and we really did not need any of the functionality Eclipse RCP offers. The result is straight JRuby with SWT + JFace + ActiveMQ.
Enlarge
Enlarge
Image:Operator 0.2 2.jpg Image:Operator 0.2 3.jpg

0.3.0 2008-09-05 First LIVE transaction

  • We did it! (barely) Our first transaction was generated using the vehicle system integrated to the fuel meter through an RS422 interface, and sent to the server by ActiveMQ and stored in the PostgreSQL database where it can be displayed usign the JRubyOnRails web application.
  • JRuby 1.1.4 was released with lots of nice bug fixes, including some reported by us. A big THANK YOU to the JRuby team! I am impressed by their responsiveness and willingness to fix an issue in direct response to a developer request.
  • JRuby 1.1.4 included some refactorings that broke
  • Derby DB / JavaDB was introduced with ActiveRecord for persisting local data on the vehicle system. This has been a great success and can be recommended for others.
A couple of problems have occurred for advanced use and have been reported in http://jira.codehaus.org/browse/JRUBY-2995 and http://jira.codehaus.org/browse/JRUBY-2949
  • ActiveRecord Migrations introduced for schema handling. A couple of issues reported on this: http://jira.codehaus.org/browse/JRUBY-2996
  • Serial communication using ruby libraries did not work.
    • Reading from /dev/ttyS0 as a file {noformat}File.open('/dev/ttyS0', 'r+'){noformat} gave illegal seek error. This has been reported as http://jira.codehaus.org/browse/JRUBY-2979
    • Reading from /dev/ttyS0 blocks, and there is no time-out mechanism.
    • Timeout.timeout does not work in JRuby, at all.
    • IO.popen returns wrong PID, so trying to put serial communication in a child process and kill it after time-out has passed did not work. This has been reported as http://jira.codehaus.org/browse/JRUBY-2977
  • Final solution to serial communication was to use the Java library RXTX 2.1 (without the javax.comm API). This has worked very well and can be recommended.
  • Introduced an ActiveMQ server on the vehicle system with the in-built bridging component to implement persistent messaging. Messages are stored locally by ActiveMQ until they can be sent to the server. Likewise messages received from the server are stored until the application can receive them. This ActiveMQ server is running inside the same JVM as the main application to simplify start-up/shut-down and monitoring.
  • Printing implemented for the receipt for a transaction. We use an Epson thermal printer with an RS232 interface for reliability and physical robustness. Some interesting pitfalls regarding invisible carriage-returns (\r) in here-docs in the source code that are required by the printer to work :)

0.4.0 2008-10-03 Complete the vehicle system for production use

  • In progress...

Using JRuby in an Eclipse RCP Plugin

Package JRuby as a plugin

TODO: can you please provide some input on how to do that?

  • create plugin with PDE, name: org.jruby, version: $JRUBY_VERSION$, I guess
  • use jruby-complete.jar or package the normal jar + the ruby stdlib in a jarred plugin?
  • in the Manifest, do you use Eclipse-BuddyPolicy=dependent to allow other plugins to run their scripts ? (by exposing their classpath to org.jruby)

Thanks, --InGer 06:06, 17 September 2008 (PDT)

Activators, Editors, views, etc.

When defining plugin activators, editors, views etc., you do this in the plugin.xml file. The classes mentioned here are loaded using reflection. We have not found a way to load ruby classes this way, so all these classes need to be Java classes. This does continually push development towards using Java instead of Ruby.

We would like a way to use Ruby classes directly as Editors, Views, etc. Please comment if you have an idea how to do this.

Comment from DominikM I don't think it would help with activators but for everything that works with the ExtensionRegistry there is a way of getting external non-java implementations to work: IExecutableExtension

See: http://help.eclipse.org/stable/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IExecutableExtension.html

By implementing something like a JRubyExtensionAdapter which is used in extension points like this: xxx.yyy.JRubyExtensionAdapter:/scripts/views/MyRubyView.rb you could get what you want with only a little generic java code.

Everything that is put after the double-colon is provided to the IExecutableExtension via setInitializationData(). Hope that helps :) Good luck on your project and please continue to update your progress.

Comment from Craig: Have you considered looking at Anton Arhipov's work during the Google Summer of Code 2007? It is called Add the ability to write plugins in jruby or groovy. He uses something like DominikM's suggestion, with a class called ScriptExtensionProxy that implements IExecutableExtension and allows your plugin to define its extension using a syntax like class="org.eclipse.soc.scripting.contributor.proxy.ScriptProxy:org.eclipse.ui.IViewPart/view.rb" (this was cut from an example plugin.xml extension to org.eclipse.ui.views, and says that the class in 'view.rb' implements the java interface org.eclipse.ui.IViewPart, and can be accessed through the IExecutableExtension class ScriptProxy.

Organizing ruby code.

The Eclipse RCP SDK gives good guidelines on how to organize your Java code. A similar guide is needed for organizing the Ruby code. A project specific guide will emerge, but we would like to see if this could be used across projects.

We have a few ideas on where to put the Ruby code:

  • Separate /rubysrc folder.
  • A /src/ruby folder.
  • /lib
  • Together with the Java code that uses the Ruby code. If my.domain.JavaClass uses file my_ruby_class.rb, the the my_ruby_class.rb file id placed together with the JavaClass.java file in the /src/my/domain folder. This enables the Java class to load the Ruby class using Classloader.getResourceAsStream().


If you have any experience with organizing ruby code in a Java project, please share here!


Sending objects over the network

We use ActiveMQ with STOMP as transport for messaging, supported by the excellent ActiveMessaging plugin.

Here is an example of how we send objects over the network. Only the object fields are sent, no code, and we use YAML for this since it is simple, readable, and has parsers on both Java and Ruby platforms. *flight* is an ActiveRecord model.

JRuby sender

 require 'activemessaging/processor'
 
 class FlightUpdater
   include ActiveMessaging::MessageSender
   extend ActiveMessaging::MessageSender
 
   QUEUE = :flight_updates
 
   publishes_to QUEUE
 
   def self.update flight
     payload = YAML.dump flight.attributes
     publish(QUEUE, payload)
   end
 
 end


Java receiver

 ...
 jruby.defineReadonlyVariable("$flight", JavaEmbedUtils.javaToRuby(jruby, flight));
 Object map_as_ruby_object = jruby.evalScriptlet("YAML.load($flight)");
 @SuppressWarnings("unchecked")
 Map<Object, Object> flight_attributes = 
         (Map<Object, Object>) JavaEmbedUtils.rubyToJava(jruby, (org.jruby.runtime.builtin.IRubyObject) map_as_ruby_object, Map.class);
 ...

This is a bit more complicated than necessary, but we wanted to try the Ruby-To-Java conversion mechanism.

Please leave a comment if you would like more details.

Personal tools