SwingJRuby

Swing with JRuby: Developing a Desktop Application with the JRuby and Java Swing APIs

by Teera Kanokkanjanarat

Contents


Tutorial Requirements

Before you proceed, make sure you review the requirements in this section.

Prerequisites

This tutorial assumes that you have some basic knowledge of, or programming experience with, the following technologies.

  • Java Swing API
  • Basic knowledge of Web service and WSDL
  • Basic Ruby programming

Software Needed for This Tutorial

Before you begin, install the following software on your computer:

  • NetBeans IDE 6 (Milestone 10 or Beta) with Ruby
  • Swing Layout Extension (swing-layout-1.0.2.jar). You can find a JAR file of the Swing Layout Extensions library in the following location on your system: <NETBEANS_HOME>/<Netbeans platform version>/modules/ext/swing-layout-<installed_version>.jar or you can download from project home page on java.net website
  • JRuby 1.0.1 release. Version 1.0.1 is bundled with Netbeans 6 beta 1, or you can download from project home page

Notations Used in This Tutorial

  • <NETBEANS_HOME> - The NetBeans IDE installation directory
  • <PROJECT_HOME> - The directory that contains the project you create
  • <JRUBY_HOME> - The directory that contain extracted JRuby jar files


Setting up Your Environment

The first step is to make sure that you have JRuby installed correctly on your machine.

Pointing NetBeans to nan installed JRuby interpreter

1. By default, Netbeans 6.0 beta 1 ships with JRuby interpreter version 1.0.1. If you want to use another version of JRuby, you will have to point the IDE to the downloaded JRuby binary location.
2. Start the Netbeans IDE.
3. Go to the Ruby configuration setup panel.

   * On Windows, go to the main menu and choose  Window > Options. This will open the Options dialog box. Select Ruby.
   * On Mac OS X, go to the main menu and choose Netbeans > Preferences. This will open the Options dialog box. Select Ruby.
   

Figure 1: Pointing Netbeans to an installed JRuby interpreter

Image:jrubyswing1_SwingJRuby.png


4. On the Ruby Options panel, make sure that Ruby interpreter points to the correct location of the installed JRuby binary executable.
5. For more detailed information on how to setup the JRuby and Ruby plugins on Netbeans, click here.

Checking out the CurrencyConverter Web Service

Inspecting CurrencyConverter web service and its WSDL

1. On your favorite web browser, navigate to the url http://www.webservicex.com/CurrencyConvertor.asmx.
2. You will see that this service offera one operation which is ConversionRate and also a list of valid currency codes.
3. Next, you can navigate to http://www.webservicex.com/CurrencyConvertor.asmx?WSDL to inspect the WSDL description of this web service. We will later create a Ruby web service client based on this WSDL file.


Creating a Ruby NetBeans Project and a SOAP Web Service Client

Creating a new Ruby project

1. In the main menu, choose File > New Project> Ruby > Ruby Application.

Figure 2: Creating Netbeans Ruby project 'JRubyCurrencyConverter'

Image:jrubyswing2_SwingJRuby.png


2. Explore the project structure. In the project panel, create the following folders under 'Source Files' folder: service, view, module, model, java, action, log.
3. Under the 'service' folder, create new ruby class. Name the class 'CurrencyConverterService' and name the file 'currency_conversion_service.rb'. This class will be used as client component consuming the web service.

Figure 3: Setting up the source folder structure and creating the CurrencyConverterService Ruby class

Image:jrubyswing3_SwingJRuby.png


4. Open the currency_conversion_service.rb file, add the code "require 'soap/wsdlDriver'" at the top. We need SOAP WSDL module from Ruby standard library to make SOAP web service call.

5. Optional: add the code "attr_accessor :wsdl, :from_currency, :to_currency" to this service class to provide access to service's attributes.

6. In the constructor of CurrencyConverterService class, add two parameters that take in the converting currency code and desired currency code. We will also initialize the web service proxy and SOAP message log file location .


Code Example 1: CurrencyConverterService class constructor


 require 'soap/wsdlDriver' #loading SOAP/WSDL Ruby standard library module

 class CurrencyConversionService

 attr_accessor :wsdl, :from_currency, :to_currency #optional variable accessors

 #constructor
  def initialize(from_currency, to_currency)
    @wsdl = 'http://www.webservicex.com/CurrencyConvertor.asmx?WSDL'
    @from_currency = from_currency
    @to_currency = to_currency
    @driver = SOAP::WSDLDriverFactory.new(@wsdl).create_rpc_driver #initialize the call
    @driver.wiredump_file_base = './log/currency_conversion_service_log' #set the log file location
  end

  ...

  end


7. Next, create another method that makes a call to the web service operation via the proxy we have created in the constructor.


Code Example 2: Making the SOAP web service call


 require 'soap/wsdlDriver' #loading SOAP/WSDL Ruby standard library module

 class CurrencyConversionService

 ...

 #calling 'ConversionRate' web service operation
  def call
    parameters = {:FromCurrency => @from_currency, :ToCurrency => @to_currency}
    result = @driver.ConversionRate(parameters) #make the call 
    puts "result = #{result[ConversionRateResult]}"
    return Float(result[ConversionRateResult].to_s)
  end

 end

Note: 'ConversionRate' operation takes two parameters and returns the value of type double


Creating Ruby Modules for Java API

Including Swing Layout Extension Java library (.jar files) in Ruby project

1. There are two ways to include Java libraries in a Netbeans Ruby project.

   * Under the project properties panel > Java, you can specify which Java libraries are to be included in the project during compile time. Once you specify the library here, it will be accessible from anywhere in the project

Figure 4: Including Java libraries in Netbeans Ruby project

Image:jrubyswing4_SwingJRuby.png


   * Another option is to include the jar files in the project source folder. Simply copy swing-layout-<version-number>.jar and paste it somewhere in your project source code. If you choose this option, you will have to include the file directory when you want to use it. In this tutorial I use swing-layout-1.0.2.jar and I put it under 'Java' folder created earlier.

Note #1: Where should I put jar files in my Ruby project ?


You can actually put the .jar files anywhere in the project. When you want to access it, you just have to the relative path from 
the root directory to that jar file. For example, if you put Foo.jar under "<NETBEANS_PROJECT>/lib/folder1/", when you want to
refer to this class libraries in ruby code, you'll have to add

require 'java'
require 'folder1/Foo.jar'

I usually put all class libraries and everything related to java in one place ('java' folder) for easy access


Creating Ruby modules for the Java API

1. Under the 'module' folder, create a new Ruby module called 'Swing' with file name 'swing.rb'.
2. Including Java packages and classes for Java Swing API that we will use.

Code Example 3: Swing Module


    require 'java'
    require 'java/swing-layout-1.0.2.jar'

    module Swing

    include_package 'javax.swing'
    include_package 'javax.swing.event'
    include_class 'org.jdesktop.layout.GroupLayout'
    include_class 'org.jdesktop.layout.LayoutStyle'
    JOptionPane = javax.swing.JOptionPane

    end


3. Under the'module' folder, create a new Ruby module called 'Awt' with file name 'awt.rb'. Include java.awt and java.awt.event packages in this module.

Code Example 4: AWT Module


    require 'java'

    module Awt
    include_package 'java.awt'
    include_package 'java.awt.event'
    end


4. Under 'module' folder, create a new Ruby module called 'Lang' with file name 'lang.rb'. Include java.lang package in this module.

Code Example 5: Lang Module


    require 'java'

    module Lang
    include_package 'java.lang'
    end


Note #2: Do I need to create module for all Java APIs ?


You don't have to create Ruby module to wrap around a Java API. With JRuby, you can access Ja ava class directly from the Ruby code.

For example, you can do frame = javax.swing.JFrame.new directly. I found that Java package names are often very long! Creating
a wrap around module can help you make the call easier. As you will see in the next section, wrapping javax.swing in Swing module
allows me to create a JFrame object with shorter and cleaner code, frame = Swing::JFrame.new.


Creating a Currency Object

Since we will be displaying a list of currencies with names of countries, it would be convenient to have a Currency object to encapsulate that information.

1. In the 'model' folder we created earlier, create a new Ruby class called 'Currency', file name 'currency.rb'.

Figure 5: Creating Currency object

Image:jrubyswing5_SwingJRuby.png


2. This Currency class will hold the currency code and country name.

Code Example 6: Ruby Currency object class


class Currency
    attr_accessor :code, :name

    #constructor
    def initialize(code, name)
        @code = code
        @name = name
    end

    #called by combobox model to display country name to user
    def to_s
        return @name.to_s
    end
end


Creating the application UI with Swing

We will go ahead and implement the user interface in Ruby with Swing API.

1. In the 'view' folder we created, create a new Ruby class called ConverterView, file name 'converter_view.rb'.
2. At the top of the class file, we need to explicitly specify the required module for swing. We also would like our ConverterView class to subclass Swing's JFrame.


Code Example 7: Including Swing module in the view and subclassing JFrame


require 'java'
require 'module/swing'
require 'module/awt'
require 'module/lang'
require 'model/currency'

class ConverterView < Swing::JFrame


3. Next we will add the implementation detail of the view class. Some details are omitted here for brevity. For the full code listing please refer to the source file.

Code Example 8: View implementation


    #constructor
    def initialize(title)
        super(title)
        initComponents
    end

    #control the display of this UI
    def display(is_visible)
        self.setVisible(is_visible)
    end

    def get_user_input
        return {'value' => @txtConvertingValue.getText,
            'from' => @ddlFromCurrency.getSelectedItem().code,
            'to' => @ddlToCurrency.getSelectedItem().code}
    end

    #display conversion result
    def show_converting_result result
        @lblResult.setText("#{result} #{@ddlToCurrency.getSelectedItem()}")
    end

    private

    def initComponents

        ...... # detail omitted here for brevity

    end

    #Adding currency we would like to convert
    def create_currency_code_model
        model = Swing::DefaultComboBoxModel.new
        model.addElement Currency.new('USD', 'US $')
        model.addElement Currency.new('CAD', 'Canadian $')
        model.addElement Currency.new('CNY', 'Chinese Yuan')
        model.addElement Currency.new('EUR', 'Euro')
        model.addElement Currency.new('JYP', 'Japanese Yen')
        model.addElement Currency.new('KRW', 'Korean Won')
        model.addElement Currency.new('THB', 'Thai Baht')
        model.addElement Currency.new('AUD', 'Australian $')
        return model
    end


4. And that's it! Now we would like to test the UI. We can do this by modifying main.rb to construct the ConverterView object and pop it up.

Code Example 9: Modify main.rb to test our UI



    require 'view/converter_view'
    puts "Swing with JRuby!"

    view = ConverterView.new 'JRuby Currency Converter'
    view.display(true)


5. If you run the main.rb source file attached with this tutorial, this will show up.

Figure 6: Running the JFrame UI

Image:jrubyswing6_SwingJRuby.png


Note #3: Creating a great looking GUI without having a headache


Developers who are not familiar with Swing may find it quite a challenge taking on Swing (especilly from Ruby!). I myself am not
Swing expert by any measures. 

Fortunately, JRuby allows developers to port much of Java code to the Ruby side by simply changing the syntax and style. 
The APIs most often remain the same. Netbeans IDE ships with Matisse GUI Builder. So, why not leverage
the tool we have? In this example, I create a simple Java GUI project, create the converter UI using Matisse, and simply port
the code directly to JRuby! 


Putting the Pieces Together!

So, now we have our logic (web service call), model (Currency), and view (ConverterView). It's time to add the last piece of the puzzle, the button action!

1. Under the folder 'action' we created earlier, create a new class call "CurrencyConverterAction_ConvertButton_Clicked", file name "currency_converter_action.rb".
2. This action class will be fired when the user clicks the 'Convert' button.
3. This action class will subclass ActionListener and override its actionPerformed method.

Code Example 10: CurrencyConverterAction_ConvertButton_Clicked


    class CurrencyConverterAction_ConvertButton_Clicked
    include Awt::ActionListener

    #constructor
    def initialize(view)
       @view = view
    end

    #Overriding this class
    def actionPerformed(event)
       user_input = @view.get_user_input

       converting_value = user_input[value]

       #assigning empty value to zero
       if converting_value.to_s.strip == ''
          converting_value = 0
       end

       #validate
       begin
          converting_value = Float(converting_value)
       rescue ArgumentError
          Swing::JOptionPane.showMessageDialog(@view, "Error: #{converting_value} is not a valid numerical value.", 'Error: Invalid input',          Swing::JOptionPane::ERROR_MESSAGE)
          return
       end

       converting_from = user_input[from]
       converting_to = user_input[to]
       puts "value = #{converting_value}, currency = #{converting_from}, to = #{converting_to}"

       #same currency, don't even bother making web service call!
       if converting_from == converting_to
          @view.show_converting_result converting_value.to_s
          return
       end

       #Calling the service and send result back to the view
       @view.show_converting_result round(CurrencyConversionService.new(converting_from, converting_to).call.to_f * converting_value, 3)
    end

    private
       #Rounding float value to specific decimal digit
       def round(float, num_of_decimal_places)
          exponent = num_of_decimal_places + 2
          float = float*(10**exponent)
          float = float.round
          float = float / (10.0**exponent)
       end

    end


4. As you can see, when a user clicks the 'Convert' button, the actionPerformed method is fired. The code will proceed to validate user input, call a web service for conversion, and send the result back to the view for display.
5. In this last step, we hook up this event to the button in the view.

Code Example 11: Wiring action event to the view's initComponents method


    def initComponents
       .....
       @btnConvert.setText 'Convert'
       @btnConvert.addActionListener CurrencyConverterAction_ConvertButton_Clicked.new(self)
       ....
    end


6. And that's it!

Figure 7: JRuby Currency Converter in action
Image:jrubyswing7_SwingJRuby.png

Summary

This tutorial walks you though how you can leverage JRuby, Swing API, and Netbeans IDE to create a desktop application with the Ruby programming language. Developing desktop applications with JRuby run on the Java Virtual Machine. So I strongly believe that JRuby will help Ruby make the transition from web (on Rails framework) to take advantage of the desktop platform.

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo