[JRuby][Warbler]Rubyで書いたソースからJARを作成する

2014.12.02

はじめに

t.hondaです。Rubyでサクっと書いたスクリプトからJARを作成し、Javaが動く環境に配置して動かすことができたら便利だと思い、その方法を調べてみました。以下に、その実現方法とサンプルソースを書きたいと思います。

実行環境について

以下の実行環境にて開発、動作確認をしてみました。

  • jruby 1.7.16.1
  • java version "1.8.0_25"

またJARの作成には、WarblerというGemを使います。
以下、Gemのバージョンです。

  • aws-sdk-core 2.0.11
  • warbler 1.4.4

ソースと開発方法について

では、開発手順とソースについてです。先ほども書きましたが、流れとしては

  1. プロジェクトを作成する
  2. Warblerを使用し、JARを作成する

という感じとなります。

1.プロジェクトの構成について

プロジェクトの作成手順について書く前に、今回の構成について書いておきます。Warblerを使用してJARを作成するには、プロジェクトのフォルダを工夫する必要があるためです。

├── Gemfile
├── Gemfile.lock
├── WarblerSample.jar ・・・JAR作成時に出力される
├── app                ・・・メインとなる処理を記述する
│   ├── main.rb
│   └── models
│       └── sample.rb
├── bin
│   └── init.rb        ・・・起動時に実行する処理を記述する
├── config
│   └── warble.rb
└── vendor
    └── bundle
        └── jruby
            └── 1.9
                ├── gems
                │   ├── aws-sdk-core-2.0.11
                │   ├── builder-3.2.2
                       (・・・以下略・・・)

「bin」フォルダ内にアプリ起動時の処理を記述したinit.rbを作成しました。「bin」フォルダが存在しないと、WarblerにてJARを作成する際にwarble aborted! No executable script foundというエラーとなります。

「app」フォルダ内にアプリの主な処理を記述しました。上述したinit.rbから、このフォルダ内の処理を呼び出すようにしています。

「vendor/bundle」以下には、bundlerにてインストールしたgemが配置されています。

2.プロジェクト作成

以下の流れでプロジェクトを作成します。

以下のコマンドで、Gemfileの雛形を作成します。

$ bundle init

Gemfileが作成されるので、使用するgemを記述します。

# A sample Gemfile
source "https://rubygems.org"

gem 'warbler'
gem 'aws-sdk-core'

# gem "rails"

以下のコマンドでgemをインストールします。「--path〜」でプロジェクトのフォルダ内にgemをインストールしていることに注意してください。(このため後で工夫が必要になります。)

$ bundle install --path vendor/bundle

「config」フォルダをプロジェクトフォルダ内に作成します。

$ mkdir config

以下のコマンドでWarblerの定義ファイルの雛形を作成します。

$ bundle exec warble config

「warble.rb」というファイルがconfigフォルダ内に作成されるので、Warblerに読み込ませるフォルダを記述します。

Warbler::Config.new do |config|
  (中略)

  # Application directories to be included in the webapp.
  # config.dirs = %w(app config db lib log script vendor tmp)
  config.dirs = %w(bin config app)
  
  (中略)
end

3.プログラム

今回書いたプログラムについてです。AWS SDK for Ruby を使い、S3にアクセスしてみました。先にも書きましたが、bin/init.rbにてアプリの起動処理を、appフォルダ内が主なロジックとなります。

bin/init.rb
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../"))

Dir.glob(File.expand_path('vendor/bundle/jruby/*/gems/*/lib')).each{|path|
  $LOAD_PATH << path if !$LOAD_PATH.include?(path)
}

require 'app/main'

main

1〜5行目で、JRuby、およびJARの作成に必要な実行パスを「$LOAD_PATH」に追加しています。1行目はアプリフォルダの絶対パスを、3〜5行目ではvendor/bundle以下のgemのパスを、それぞれ追加しています。gemのパスについては、これを追加しないとLoadエラーとなったのですが、Global領域にgemをインストールした場合は必要ないかもしれません。

7行目以降は、appフォルダ内のロジックを呼び出しています。

app/main.rb
require 'app/models/sample'

def main
  Sample.new.start_method
end

こちらは単純に、app/models/sample.rb内のメソッドを呼び出しています。

app/models/sample.rb
require 'aws-sdk-core'

class Sample
  def start_method
    puts 'called Sample.start_method.'

    s3 = Aws::S3::Client.new(region: 'ap-northeast-1', profile: 'your-profile-name')
    resp = s3.list_objects(bucket: 'your-bucket-name')
    resp.contents.each do |object|
      puts object.key
    end
  end
end

ここでaws-sdk-coreを使用し、S3にアクセスしています。今回はS3のバケットを指定し、中のオブジェクト名を出力してみました。

なお、AWSのアクセスキー、シークレットキーは~/.aws/credentialsに定義しておく必要があります。

JARの作成と実行

最後に、JARを作成して実行してみます。

以下のコマンドでJARを作成する。

$ bundle exec warble jar . #最後のピリオドを忘れずに!

作成したJARを実行する。

$ java -jar WarblerSample.jar

以下のように出力されました。

called Sample.start_method.
lsyncd/
lsyncd/名称未設定.txt

AWS SDK for Rubyを使い、JVM上でS3にアクセスすることが出来ました!!

まとめ

別のRubyファイルをrequireした場合、JARだと読み込まれないのを解決するのに苦労しました。JRubyで開発する際に、何かの参考になれば幸いです。

参考サイト

Ahead-of-time compiling JRuby, packaging a Java Jar, creating a Gemspec, using Warbler

追記

もう少し細かいポイントについて続編を書きました。
[JRuby][Warbler]Rubyで書いたソースからJARを作成する - (2)定義ファイルの参照と、その他のポイント