sifue's blog

プログラマな二児の父の日常

ScalaでEclipseデバック可能な実行jarを作るまでの手順 (例:特定のハッシュタグを含むツイートをファイルに保存し続けるアプリ)

最近Scalaでちょっとしたjarアプリを作ることが多いのでその手順をまとめておきます。まだ発展途上とは言え2.0.2になったことでScala IDE for Eclipseも実用に耐えうるところまできているように思います。あとは、リファクタリング機能とクイックフィックス機能がもう少し良くなると良いですが、コンパイルエラーが出た際に、どうするべきかということがポップアップメニューで出るのでその点で助かっています。
ちなみに今回この記事を書こうと思ったのは、今までSbt0.11.2まで利用できた実行jarを作るproguardというプラグインが利用できなくなってしまったこともあり、sbt-assemblyを利用したものを再度書くことにしました。

環境確認

まず、環境情報の確認です。Macのパッケージ管理システムで利用できる2012/07/28現在でできる限り最新安定版を利用しています。


Javaは、Macの場合は

$ java -version

と実行するとインストールされ、バージョンが確認できます。ソースコード付きのJDKを入れたい方は、この記事を参照。
Homebrewはリンク先を参照してインストールの事。XCodeとCommand line toolsのインストールも必要です。
Scalaとsbtのインストールは

$ brew install scala
$ brew install sbt

で完了です。バージョンアップの際には、

$ brew update
$ brew upgrade scala
$ brew upgrade sbt

を実行します。Eclipse 3.6.2とScala IDE for Eclipse 2.0.2のインストールはリンク先のインストール方法に従って下さい。


というわけで、環境は整ったということでお題であるEclipseデバッグ可能な外部jarライブラリを内包する実行可能jarを作ってきます。お題は、


特定ハッシュタグを含むツイートをファイルに保存し続けるアプリ」


です。すでに実装したものが、GitHubの方にMITライセンスでpushしてありますので、ソースコードだけが欲しい場合はこちらをcloneしてお使いください。

プロジェクトの作成

まず、プロジェクトの作成です。ここではtweet_recorderとします。有りそうな名前ですねw

$ mkdir tweet_recorder
$ cd tweet_recorder

次にjarを作るまでに雛形を作成します。なお基本的にこのtweet_recorderディレクトリから操作していきます。

$ mkdir -p src/main/scala
$ echo 'object TweetRecorder { def main(args: Array[String]) = println("Hi!") }' > src/main/scala/TweetRecorder.scala
$ vim build.sbt

build.sbtを編集

name := "tweet_recorder"

version := "1.0"

scalaVersion := "2.9.2"

次に念のためにsbtもバージョン指定しておきます。

$ mkdir project
$ vim project/build.properties

project/build.propertiesを編集

sbt.version=0.11.3

とここまでで、プロジェクトのひな形は完成。



というわけで早速sbtでビルド、実行してみます。

$ sbt
> compile
[success] Total time: 15 s, completed 2012/07/29 9:00:22
> run
Hi!
[success] Total time: 1 s, completed 2012/07/29 9:00:36
> exit

これで大丈夫なことを確かめます。

実行jarの作成

次にjar化できるようにします。他にもjar化させるものはいくつかありますがsbt-assemblyが非常に良い出来だったので、これを使います。

$ vim project/plugins.sbt

project/plugins.sbtを編集

resolvers += Resolver.url("artifactory", url("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.8.3")
$ vim build.sbt

build.sbtを編集

import AssemblyKeys._ // put this at the top of the file

assemblySettings

name := "tweet_recorder"

version := "1.0"

scalaVersion := "2.9.2"

mainClass in assembly := Some("TweetRecorder")

他にも設定が、github内に書いてありますが、ここではmainメソッドのあるクラスの定義だけ設定します。
そして早速jarを作ってみます。assemblyコマンドでjar作成とのことです。
早速、jarを作成、実行してみましょう。

$ sbt
> assembly
[info] Done packaging.
[success] Total time: 107 s, completed 2012/07/29 9:14:19
> exit
$ java -jar target/tweet_recorder-assembly-1.0.jar
Hi!

これでOKそうですね。

実行jarへの3rdパーティjarライブラリの梱包

ちなみにもうライブラリとしてtwitter4jを使うことがわかっているので、twitter4jの必要なライブラリとソースをプロジェクトにコピーしてビルドしてみましょう。tmpディレクトリに一度ダウンロードし、削除します。

$ mkdir lib
$ mkdir lib-src
$ mkdir tmp
$ cd tmp
$ curl -O http://twitter4j.org/en/twitter4j-2.2.6.zip
$ unzip twitter4j-2.2.6.zip
$ cp lib/twitter4j-core-2.2.6.jar ../lib
$ cp lib/twitter4j-stream-2.2.6.jar ../lib 
$ cp twitter4j-core/twitter4j-core-2.2.6-sources.jar ../lib-src 
$ cp twitter4j-stream/twitter4j-stream-2.2.6-sources.jar ../lib-src
$ cd ..
$ rm -rf tmp
$ sbt
> assembly
[info] Including twitter4j-core-2.2.6.jar
[info] Including twitter4j-stream-2.2.6.jar
[success] Total time: 1 s, completed 2012/07/29 9:32:14
> exit
$ java -jar target/tweet_recorder-assembly-1.0.jar
Hi!

これでライブラリの実行jarへの内包も大丈夫そうです。


Eclipseへのsbtプロジェクトのインポート

次に、Eclipseにインポートできるようにします。sbteclipseを使います。

$ vim project/plugins.sbt

project/plugins.sbtを編集して、末尾に追記します。

resolvers += Resolver.url("artifactory", url("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.8.3")

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0")

編集後、eclipseコマンドを実行します。

$ sbt
> eclipse
[info] Successfully created Eclipse project files for project(s):
[info] tweet_recorder
> assembly
[info] Including twitter4j-core-2.2.6.jar
[info] Including twitter4j-stream-2.2.6.jar
[success] Total time: 1 s, completed 2012/07/29 9:32:14
> exit
$ java -jar target/tweet_recorder-assembly-1.0.jar
Hi!

eclipseプロジェクトの作成も、実行jarの作成もOKそうです。


次に、Eclipseへのインポートです。ScalaパースペクティブのPackage Explorerビューの右クリックメニューにて。Import > General > Existing Projects into Workspaceを選択して、Nextを押します。


Select root directoryで、プロジェクトのフォルダを選択して、Finishボタンを押します。この際に、copyをせずにそのままそのフォルダを利用します。これは今後sbtでビルドを利用するためです。

Twitter4jのStreamingAPIを使った実装とビルド

これでプロジェクトのインポートが終わったので、早速ちょっとコードを書いてデバッグで止めていましょう。
早速サンプルコードを書いてみます。

TweetRecorder.scalaを編集して

import twitter4j.conf.ConfigurationBuilder
import twitter4j.TwitterStreamFactory
import twitter4j.FilterQuery
import twitter4j.StatusListener
import twitter4j.Status
import twitter4j.StatusDeletionNotice
import java.io.PrintWriter
import java.io.FileWriter
object TweetRecorder { 
  def main(args: Array[String]) = {
    val conf = new ConfigurationBuilder().setUser(args(0)).setPassword(args(1)).build
    val twitterStream = new TwitterStreamFactory(conf).getInstance()
    twitterStream.addListener(new Listener)
    twitterStream.filter(new FilterQuery().track(args.slice(2, args.length)))
  }
  class Listener extends StatusListener {
    override def onStatus(status: Status) = {
      val tweet = status.getCreatedAt().toLocaleString() +
      	"," + status.getUser().getScreenName() + 
      	"," + status.getText() 
      val writer = new PrintWriter(new FileWriter("tweet_recoder.csv", true))
      writer.println(tweet)
      writer.close
      println(tweet)
    } 
    override def onDeletionNotice(statusDeletionNotice: StatusDeletionNotice) = {}
    override def onTrackLimitationNotice(numberOfLimitedStatuses: Int) = {}
    override def onException(ex: Exception) = {}
    override def onScrubGeo(userId: Long, upToStatusId: Long) = {}
  }
}

これでOK。起動する時に第一引数がtwitterのアカウントのID、第二引数がtwitterのアカウントのパスワード、第三引数以降は追跡したいキーワード(英単語か#ではじまるハッシュタグのみ) を半角スペース区切りで追加という感じになります。右クリックでDebag As > Scala Applicationを選択してデバッグ実行で引数が無いという例外が投げられて失敗したあと。
虫アイコンのドロップダウンメニューのDebug ConfigurationのArgumentsタグでそれぞれの引数を設定して実行します。


今回は、単語をすごい量のツイートが流れていそうなhttpに設定して実行します。コンソールに

2012/07/29 11:06:30,Vasilisa_Anime,http://t.co/nKcvZJw7
2012/07/29 11:06:30,Neylianavera,esteeeen; x_x @Georgejsm. http://t.co/Ap5vLa1R

のように表示されればOK。今回は日付、ユーザーID、ツイートをカンマ区切りで表示してます。


無論ブレークポイントを貼ってデバッグすることもできます。


最後にビルドして実際に動くか確かめて見ましょう。
今回は、2chまとめのタグ#MT2とニコ動の#nicovideoで検索してみます。

$ sbt
> assembly
[info] Including twitter4j-core-2.2.6.jar
[info] Including twitter4j-stream-2.2.6.jar
[success] Total time: 1 s, completed 2012/07/29 9:32:14
> exit
$ java -jar target/tweet_recorder-assembly-1.0.jar sifue password \#MT2 \#nicovideo
2012/07/29 11:35:08,nicorank_bot,毎時ランキング第1位:じょしらく 第三席「無情風呂」「浅草参り」「真田小ZOO」  http://t.co/0U9SZFnQ #nicovideo #so18447313 2012/07/29 11:35 現在
2012/07/29 11:35:09,2chtitle,「あっ、こいつインド人だな」って思う仕草 インド人とのつきあい方?インドの常識とビジネスの奥義 を Amazon でチェック!  http://t.co/Lf2sWYQ5 #MT2 #まとめ
2012/07/29 11:35:12,zyazya11,RT @Mell_Ta: ニコニコ有名歌い手がポケモン割れ発覚 http://t.co/qQh9eCvL #MT2 今ぐるたみんが熱い

以上のように表示され無事利用できるようです。これで好きなハッシュタグのツイートなどを保存して解析したりするのに使えますね。
ちゃんとtweet_recoder.csvというファイルを確かめても保存できているようです。


以上でScala2.9.2でsbt-assemblyを使ってEclipseでデバック可能な3rdパーティjarを含む実行jarを作るまでの手順を終わりとします。お疲れ様でした。