System#exitを呼び出してもVMを終了させない方法

こんな感じででけた。もしこのコードを試すのであれば、ラーニングテストなので、適宜org.junit.Testをインポートしてね。

@Test
public void exitしないことを確認しよう() throws Exception {
	System.setSecurityManager(new SecurityManager() {
		@Override
		public void checkExit(int status) {
			throw new SecurityException();
		}
		@Override
		public void checkPermission(final Permission perm) {
		}
		@Override
		public void checkPermission(final Permission perm, final Object context) {
		}
	});
	try {
		//  はーい、ここ注目ですよー。
		System.exit(0);
	} catch (Exception e) {
	}
	System.out.println("System#exitを実行したのに終了しなかったYO!");
}

実行してみると、こんな感じでコンソールに表示されるはず。

System#exitを実行したのに終了しなかったYO!
Exception in thread "main" java.lang.SecurityException
	at learning.Learning$1.checkExit(Learning.java:17)
	at java.lang.Runtime.exit(Runtime.java:88)
	at java.lang.System.exit(System.java:921)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:202)

SecurityExceptionが後に表示されるのは、なぜなんでしょう?そこは分かってないです。なんでこんな事を試したか、というと、アプリケーションの中にスクリプトコードでカスタマイズできる部分を用意し、ユーザに自由にカスタマイズしてもらう事を想定すると、System#exitを実行されてVMが終了されるのは非常に困るからです。実際、JAMCircleのスクリプトコンソールにこんな感じで組み込んでみると、JRubyのコードからSystem.exitを実行しても終了されないようになりました。

SecurityManager securityManager = System.getSecurityManager();
System.setSecurityManager(new SecurityManager() {
	@Override
	public void checkExit(int status) {
		throw new SecurityException();
	}
	@Override
	public void checkPermission(final Permission perm) {
	}
	@Override
	public void checkPermission(final Permission perm, final Object context) {
	}
});
// run ruby code
runtimeAdapter.eval(runtime, script);
// retrieve security manager
System.setSecurityManager(securityManager);

そういうのはpolicyに書くといいよ、というお話もありますが、JAMCircleみたいな単体アプリケーションを配布する事を考えると、ユーザにpolicyを修正して頂く事になるので現実的ではありません。本当はJRuby等のスクリプトコンテナの方で設定できるのがスクリプトコンテナの利用者からするといいと思うんです。なんとなくアプリケーションの責務ってよりも、スクリプトエンジンの責務っぽいですから。ただ、これをこのまま組み込んでしまうと、スクリプトから任意のファイルを触れると思うので、それもセキュリティ上よろしくないですよね。実際に組み込むのはもう少し考える事がありそうです。

追記

コメントでも頂きましたが、上記のコンソールに表示されるスタックとレースは、RemoteTestRunner内でSystem#exitが呼ばれているからでした。

2010/08/13追記

スクリプト実行中に他の操作で終了処理するとプロセスが落ちませんね。ちゃんと呼び出し元を判別する必要があるみたい。

Quick JUnitが@ITで取り上げられました!

@ITにQuick JUnitがとりあげられました!岡本さんありがとうございます!
http://www.atmarkit.co.jp/fjava/rensai4/devtool16/devtool16_1.html
最初、タイトルを見ただけうれしくなり、本文をしっかり目を通していませんでした。ところが大事なお名前の部分を見落としていたようです。心より恥じます。すいません。
さて、文中ではEclipse MarketplaceからQuick JUnitをインストールされているのですが、アイコンが表示されていません。そうなんです。Quick JUnitにはまだアイコンがないのです。この機会に募集したいと思います。アイコンの大きさは32x32、フォーマットはpngかgifです。オープンソースへのコントリビュートはソースコードだけじゃありません。興味をもたれた方はブクマコメントやtwitterなどでお声をおかけください。

Javaフレームワーク開発入門はプログラムを書き始めた新人にとって、最初の魔導書になるに違いない

著者のきむきむさん(id:skimura)こと木村聡さんから献本いただきました!

Javaフレームワーク開発入門

Javaフレームワーク開発入門


献本いただいた理由は、書籍の中でOSGiのチュートリアルを簡単に紹介したから、ということでした。作業が滞っていて申し訳ない気持ちでいっぱいです。開いてみてびっくり。ほんとにちょろっと登場してるだけ・・・。なのに献本いただけるなんて感謝感激です。
最初、この本の話を聞いた時、「フレームワークって世に溢れているので、読みたい人いるのかな?」と正直思っていました。でも実際に手に取ってみて、この本の使い方は違うところにあるなと感じたのです。この本はフレームワークを読み解く時に、フレームワークがなぜそうなっているのかという疑問に対する答えが詰まった本なのです。言い換えると、実際にいくつものフレームワークを構築してきた著者の叡智が凝縮した本と言っても過言ではありません。
タイトルに「魔導書」という表現を用いましたが、コンピュータを使いこなす人を「ウィザード」と呼んでたりしますよね。ウィザードになるために、「良い」と思った本を、手あかで縁が真っ黒になるくらい、読み込む本の事を「魔導書」と表現してみました。この本は、ウィザードになるための最初の一冊になりうるなと思ったのです。
僕が新人のころにこの本と出会ってたら、もっとかっこいいコードが書けるエンジニアになれただろうな。とても良い本ですので、書店で見かけた際はぜひ手をとってみてください。

Growl Java api

cocoa-javaがdeprecatedなので、どこかに行ってしまったGrowl Java APIですが、snow leopardで利用するための記憶メモ。欠けている所はそのときそのとき修正しよう。

必要なもの

  1. このサイトからgrowl-java-0.1.tar.gzをダウンロード
  2. makeするためにXcode toolsからgcc4.2.pkg、DeveloperTools.pkg等をインストール(その辺りの流儀を分かっていない)
  3. gcc4.2のバイナリへのシンボリックリンクが貼れていないので張り直し(/usr/bin/g++ -> /usr/bin/g++-4.2)

やったこと

  1. Makefile内のヘッダーのインクルードパスを下記に変更
/Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/

その他メモ

出来上がるlibgrowl.jnilibはライブラリのパスに追加し下記のように指定すればロードできる

System.loadLibrary("growl")

ProcessBuilderの使い方

JavaDoc読んでもよく分かんなくてはまった><。4,5時間これに嵌り続けたので晒しておこう。間違ってる点は指摘してくださいませ。

ŠO•”ƒvƒƒOƒ‰ƒ€‚ðŽÀs‚µ‚ÄŒ‹‰Ê‚ðŽæ“¾‚·‚é(Javaƒ}ƒXƒ^[)のページを参考にしました。(ありがとうございます!)ポイントは以下の通り。

  • 今回のはWindows XP上の話で他のOSの事はとりあえず横において置いてます。
  • 1コマンドの実行は1Processで。
  • ProcessBuilder#command(String...)は "コマンド名","引数1","引数2"...と言うように指定する。(JavaDocから類推すると複数コマンド実行できるように見えるじゃんか><)
  • 環境変数はSystem.getenv()からコピーしてくれるような期待をさせてくれるJavaDocだけど、そんなことしてあげないから!っていう感じ。Windows版JREは意外にツンなのか。
  • という事で、環境変数PATHも自前で設定しなければならないようだ。
  • でもC:\Windows\System32にはパスが通っているらしい。
  • 環境変数の設定はProcessBuilder#environment().put("PATH",環境変数で指定したいパス);
  • 環境変数で指定したいパスはもちろん「;」で区切って複数指定できるらしい。
  • Process#getInputStream()やProcess#getErrorStream()はもちろん同一スレッドで受け取ってたら「まとめて出力」しか出来ないので、ストリームで扱う意味がなくなってまう。参考リンク先のようにThreadを作るのが正解。
  • でもStringWriter#printlnを使っちゃうのは速度的に良いのかもわかんないけど、System.outやSystem.errに直接流し込んじゃう方が素のコンソールに近い動きをするはずだからいいんじゃなかろうか。

サンプルコードのCatcherをいじってみる。

class Catcher extends Thread {
  Reader in;
  PrintWriter out;
  public Catcher(Reader in,PrintWriter out) {
    this.in = in;
    this.out = out;
  }

  public void run() {
    int c;
    try {
      while ((c = in.read()) != -1) {
        out.write((char)c);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }

  }

}

こうしておくとこんな感じでストリームを監視するスレッドを作成できる。

new Catcher(p.getInputStream(),System.out);
new Catcher(p.getErrorStream(),System.err);

繰り返しますが、変なところはじゃんじゃんつっこんで下さい!よく分からんの。ProcessBuilderは。

Antから起動するJavaプロセスのリモートデバッグ

普通やらないであろうAntから起動するJavaプロセスのリモートデバッグについて。いや、Eclipse Test Frameworkのパッチがうまく動かなくてリモートデバッグやってみようと思い立ったわけで。Antスクリプトに追加したコードがこんな感じ

<java>
   ...
   <jvmarg line="-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address=localhost:44000"/>
</java>

えぇ。にせにゃならんのをのまま与えててうまく動かないーってはまってただけですよ。