miauのブログ

はてなダイアリー「miauの避難所」をはてなブログに移行しました

Java のインタラクティブシェルを探して

一ヶ月ほど前から Java の案件に関わっていて、現在設計フェーズなんですが。私は Java に不慣れなので「こういう処理って Java で簡単に書けるんだっけ?」という場面が結構あります。

例えば今日だと「String#split でデリミタが先頭や末尾にある場合どうなるんだっけ?」といった疑問が沸いてきまして。正攻法はマニュアル嫁ってことになるんだと思いますけど、LL に慣れてると irb みたいなインタラクティブシェルで実際の動作を確認したくなってきます。

そんなわけで Java の動作を確認できそうなインタラクティブシェルについて、簡単に調査してみました。

Rhino

検索すると、

ということで Java 製の JavaScript 実装である Rhino の例が出てきたので試してみます。

からダウンロード&解凍して、

>cd rhino1_7R2
>java -jar js.jar
Rhino 1.7 release 2 2009 03 22
js>

みたいな感じで起動。

js> "2001:db8::9abc"
2001:db8::9abc
js> "2001:db8::9abc".split(":")
2001,db8,,9abc

戻り値がちゃんと表示されるのはいいけど、toString() された値が表示されるからイマイチわかりにくい・・・。["2001", "db8", "", "9abc"] みたいに表示してくれたほうが嬉しいんだけど・・・。

そもそも、これってちゃんと Java の String 使ってる?

js> typeof("hoge")
string

なんか JavaScript の文字列のような・・・。

js> typeof(new String("hoge"))
object
js> (new String("hoge")).getClass()
js: "<stdin>", line 39: uncaught JavaScript runtime exception: TypeError: Cannot find function getClass in object hoge.
        at <stdin>:39

new String() も怪しい・・・。

js> typeof(new java.lang.String("hoge"))
object
js> (new java.lang.String("hoge")).getClass()
class java.lang.String

あ、これはちゃんと Java の String っぽいですね。じゃあ動作確認を・・・。

js> java.lang.String("2001:db8::9abc")
2001:db8::9abc
js> java.lang.String("2001:db8::9abc").split(":")
[Ljava.lang.String;@1891d8f

配列の中身が表示されないorz

これだとちょっと動作確認には向きませんね・・・。

SecureJSH

もう一つ検索して出てきたのがこれ。

Java での SSH サーバ実装のようですが、Java のコードを対話的に実行できるようです。

使い方は・・・

からダウンロードして・・・

>cd SecureJSH\bin
>set JAVA_HOME="C:\Program Files\Java\jdk1.6.0_15"
>AAA-run-sjshd.bat
Using ClassPath:
D:\SecureJSH\bin\;D:\SecureJSH\lib\sjsh-uim.jar;D:\SecureJSH\lib\sjsh.jar;D:\SecureJSH\lib\ext\javac.jar
SSH-2.0-SJSHD-1.1 Starting...
SSH-2.0-SJSHD-1.1 Listening /127.0.0.1:22 ...
SSH-2.0-SJSHD-1.1 Started.
Now you can login using an SSH client.

Type in anything to stop:

のように、JAVA_HOME の設定をして AAA-run-sjshd.bat というのを叩くと、SSH サーバが起動します。

それから sshlocalhost に接続すると・・・ユーザとパスワードが聞かれるけどどうするんだろう・・・?としばらく試行錯誤してたんですが、このへんは

に書いてました。

  • ユーザは sjsh を指定
  • 秘密鍵として SecureJSH\SJSH-Stage\home\sjsh\.ssh\id_rsa を指定する

とと接続できるそうで。SecureJSH\SJSH-Stage が Linux サーバのディレクトリ構成みたいになってるってことですね。
TeraTerm なんかでも接続できますが、ssh コマンドが入っている場合は、

>ssh -l sjsh -i D:\SecureJSH\SJSH-Stage\home\sjsh\.ssh\id_rsa localhost

こんな感じで接続できると。

実際に接続すると、

This is an interactive Java(TM) shell, type in Java(TM)
statements to get them executed.

Press Ctrl^D to logout.

Type in 'man' or 'help' to see the help manual.

Press <Tab> to complete contents.

Use UP/DOWN arrow keys for command history.
(中略)
[jsh ]$

みたいなメッセージが表示されます。

じゃあ早速・・・

[jsh ]$ "hoge"
012: ;
ERROR: 文ではありません。

Java のコードをそのまま実行する形なので、セミコロンが必要&式じゃなくてステートメントを書かないとダメと。ちょっと使いにくいですね。

文字を画面に出したい場合は、print というコマンドを使うか、Java の式として println() メソッドを呼ぶことになります。(System.out.println だと SSH サーバ側に出力されてしまいます)

[jsh ]$ print "hoge"
hoge

[jsh ]$ println("hoge");
hoge

また、変数の値はステートメント間で保持されないので、def コマンドであらかじめ定義してから使う必要があります。

[jsh ]$ String hoge = "abc";

[jsh ]$ print hoge
ERROR: シンボルを見つけられません。
シンボル: 変数 hoge
場所    : $JSH$ の クラス

[jsh ]$ def String hoge // temporary string

[jsh ]$ hoge = "abc";

[jsh ]$ print hoge
abc

基本的な使い方はさておき、今回動作確認したかったあたりを。

[jsh ]$ print "2001:db8::9abc"
2001:db8::9abc

[jsh ]$ print "2001:db8::9abc".split(":")
[Ljava.lang.String;@1a19458

Rhino のときと同じで、型の情報がわかりにくいですね・・・。PHP の var_dump() みたいなのが無いか探してみると、

"Apache Commons Lang"(http://commons.apache.org/lang/)の
"ToStringBuilder#reflectionToString(Object object)"で可能です。

とのことで。

から commons-lang-2.5.jar をダウンロードして、SecureJSH\lib\ext あたりに置いて、SSH サーバを再起動します。

[jsh ]$ import org.apache.commons.lang.builder.ToStringBuilder

[jsh ]$ print ToStringBuilder.reflectionToString("2001:db8::9abc")
java.lang.String@746ad0[value={2,0,0,1,:,d,b,8,:,:,9,a,b,c},offset=0,count=14,hash=-953369656]

[jsh ]$ print ToStringBuilder.reflectionToString("2001:db8::9abc".split(":"))
[Ljava.lang.String;@1485542[{2001,db8,,9abc}]

うーん・・・Java に慣れてるひとはこれでわかるのかもしれませんが、私にはちょっと厳しいですね。

あとは、文法が Java と違うので煩わしいのと、SSH サーバを立ち上げてそこに接続、というのがちょっと面倒です。(チームで共用サーバ立てておけばそれなりに便利かもしれませんけど。)

でも補完が効くのと Java のコードがそのまま使えるので、状況によっては便利かもしれません。

Scala

もうなんかやたら評判のいい Scala

このあたりを読むとかなり使いたくなってくるわけですが、こいつも JVM 上で動く&引数なしだと対話モードで動作するので、Java の動作を確認するのに使えそうです。

インストールは、

あたりを参照。.zip で入れると PATH 通す必要があるので .jar のインストーラで入れたほうが楽みたいです。

さっそく起動して、適当な文字列を入れてみます。

>scala
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) Client VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> "hoge"
res0: java.lang.String = hoge

ちゃんと型が表示されるのでわかりやすいですし、文字列リテラルjava.lang.String になってくれています。

scala> "2001:db8::9abc".split(":")
res1: Array[java.lang.String] = Array(2001, db8, , 9abc)

配列の場合も、配列である旨&要素の型が表示されるので曖昧じゃないです。

今度こそ、やりたかった動作確認をば。

scala> "::1".split(":")
res2: Array[java.lang.String] = Array(, , 1)

scala> "1::".split(":")
res3: Array[java.lang.String] = Array(1)

scala> "::".split(":")
res4: Array[java.lang.String] = Array()

scala> "::".split(":", -1)
res5: Array[java.lang.String] = Array(, , )

String#split はデフォルトだと末尾の空要素を削って出力する、と。今回の動作確認ではきっちり実用レベルでした。

メソッドのタブ補完もできるし、比較した中では一番使いやすかったです。でも Scala をあんまり使いすぎると Java がますます面倒になりそうなので、動作確認にだけ使うようにしよう・・・。

JRuby(2010-08-10 追記)

そういえば忘れてた JRubyJRubyJVM 上で動いているので、Java のクラスが使えるんでした。
NetBeansRuby バンドルを入れると JRuby が動くようになるので、ruby2/jruby-1.4.0/bin/jirb.bat で簡単に確認してみます。

irb(main):001:0> "hoge"
=> "hoge"

irb(main):002:0> java.lang.String.new("2001:db8::9abc")
=> #<Java::JavaLang::String:0xfdfc58>

普通に文字列を渡すと Ruby 上の文字列になっているので、java.lang.String で明示的に指定する必要があると。参考にした

によると、Java ライブラリのアクセスには include Java が必要とのことでしたが、実行しなくても問題ないみたいですね。

irb(main):003:0> java.lang.String.new("2001:db8::9abc").split(":")
=> #<#<Class:01x82674b>:0x14b84c7>

irb(main):004:0> java.lang.String.new("2001:db8::9abc").split(":").each{|e| p e}
"2001"
"db8"
""
"9abc"
=> #<#<Class:01x82674b>:0x178dc08>

配列が返ってきてるはずなんですが、よくわからない表記になってしまっています。each 等でループしてやれば出力できますが・・・Java の動作確認という意味ではイマイチですね。

      • -

ということで、しばらくは scala で動作確認をやろうと思ってるんですけど、他にもオススメの環境等があれば教えてくださいませ。