2007年02月21日

Throwable#printStackTrace() の結果を文字列として取り出す(追記)

以前書いた

Throwable#printStackTrace() の結果を文字列として取り出す
について、PipedReader/Writer を用いて実現しようとしていましたが もっと簡便な方法がありました。。。

Throwable#printStackTrace(PrintWriter) の引数に渡す PrintWriter に StringWriter を使えばよいようです。 StringWriter なんて便利なクラスがあったんですね。。。
ちなみにマルチスレッドでの挙動は確認していません。

2006年12月07日

IFileEditorInput は IEditorInput と同じプラグイン内にない

ようやく EditorPart#init に記述すべきことが分かって、 あとはコメントにある雛形を書き写せば、と思ってそのままコピーしたらなぜかエラーが。 どうも IFileEditorInput(IEditorInput のサブインターフェイス)が見つからないらしい。 IEditorInput は見つけられている(ビルドパス内にいる)のに? 同じプラグイン内にないのか?

仕方がないのでまた Eclipse のソースの山から IFileEditor.java を検索。 どうやら org.eclipse.ui.ide にあるらしい。 このプラグインを、自分のプラグインプロジェクトの依存関係に追加して、ようやく IFileEditorInput が使えるように。 そして無事ファイルの右クリック-「開く」で簡単なコンポーネントが開くところまでこぎつけました。 長い道のりだった。。。

EditorPart の生成で「サイトが異なっています」

前述の editor プラグインがメニューに表示されない件を解決して、 実際に選択できるようになったものの、今度は Exception が発生してしまいました。 (日本語版なので)「サイトが異なっています」というメッセージなのですが、 何が原因なのか、まるで分からないです。

最低限の情報としてスタックトレースが表示されていて、 それによると org.eclipse.ui.internal.EditorManager#createSite 内で Exception が発生している模様。 Eclipse のソースの中からクラスを探し出し(internal なクラスのソースを見なくてはならないなんて!!)、 createSite 周辺のソースを読むと、どうもこのメソッド内で生成した EditorSite インスタンスと、 createSite の引数となっている IEditorPart (おそらくプラグイン拡張を実装した EditorPart)の getSite もしくは getEditorSite と比較して、 異なっていると Exception としているようです。
生成した直後に他のオブジェクト(IEditorPart)のメンバーと比較? と疑問に思ってよく見てみると、比較の直前に IEditorPart#init(IEditorSite, IEditorInput) を呼んでいる。
どうも IEditorSite を実装したクラスの init に IEditorSite を渡すので、 自分で保持しておけ、ということのようです。

そこまで判明したので、EditorPart を継承したプラグイン拡張の雛形クラスの init で、 親クラスの init(super.init)を呼べばよいかと思ったのですが。。。
EditorPart#init は抽象クラスとのこと。 雛形のクラスなんだから、最低限の実装はしていてくれればよいのに! しかもどう実装すればよいか明確な説明がない。。。 (少なくともヘルプやチュートリアルには)。
どうには EditorPart.java を Eclipse のソースの中から探し出し、 init メソッドを見てみると、確かに抽象クラス。 しかもそのコメントには、「このように実装するのがよい」などと書いてあります。

どうにかコメントに書かれていたとおり記述すると、 ようやくエディターの UI が表示されるように。
長い道のりだった。。。
ソースを読まないと起動すらできないとは。
世間の人は普通にソースを読んで、普通にこんな問題はクリアしているのか?

org.eclipse.ui.editors 拡張 : ポップアップメニューの「開く」に表示されない

(eclipse plugin 開発はまだ始めたばかりなので、このカテゴリは本当に覚え書です)

プラグイン・プロジェクトを作成し、 マニフェストエディターに従ってとりあえず org.eclipse.ui.editors 拡張ポイントに拡張を作成、 クラスが自動生成されるのでそのまま「実行」-「Eclipse アプリケーション」してみると、、、。
左側のエクスプローラ上のファイルを右クリックしても、開くアプリケーション候補の中に、 今作った editor プラグインがない。。。

実は、拡張エレメントの extensions に明示的に開くファイルの拡張子を指定しないと、 「開く」メニューの候補に現れないのです。。。
(てっきり、何も指定しなければすべてのファイルの「開く」メニューに表示されるかと思いました)。 ちなみに「*」と指定しても、すべてのファイルという意味にはならないようです。 ということは拡張子のないファイルの「開く」メニューに結び付けられない? (matchingStrategy を使えばできるのか? そこはまだ未調査)

2006年08月24日

Throwable#printStackTrace() の結果を文字列として取り出す(2/2)

printStackTrace(PrintWriter) ではうまくいかない場合があるため、 実際には Throwable#getStackTrace() を用いる方が簡便かもしれません。 (JRE 1.4 以降)

このメソッドはスタックトレースの内容を StackTraceElement 配列として取得できます。 配列の一つ一つの内容を文字列に直せば、 printStackTrace() と同様の結果を得ることができます。 (ただしタブや改行などの整形は行う必要があります)

例)

public class StackTracePrintTest {
    ...
    private String getStackTraceString(Throwable ex) throws IOException {
        StringBuffer buf = new StringBuffer();
        buf.append(ex.toString());
        buf.append("\n");

        // スタックトレースの内容を取得
        StackTraceElement[] sts = ex.getStackTrace();
        for (int i = 0; i < sts.length; i++) {
            buf.append("\tat ");
            buf.append(sts[i].toString());
            buf.append("\n");
        }

        // Cause Throwable が存在する場合
        Throwable cause = ex.getCause();
        if (cause != null) {
            buf.append("Caused by: ");
            buf.append(this.getStackTraceString(cause));
        }

        return buf.toString();
    }
}

上記のようにすれば、スタックトレースの内容を文字列に出力することができます。 ただし自前でこの処理を作るよりは、 やはり printStackTrace() の内容をそのまま String で返すようなメソッドが用意されていてもよいように思いますが。。。

Throwable#printStackTrace() の結果を文字列として取り出す(1/2)

デバックを行う場合など、例外が何処で発生したかを追跡するため printStackTrace() メソッドを使用しますが、 通常このメソッドは標準エラー出力にしか出力されません。 ログファイルに残したり、(コンソールではなく)ウェブ画面やGUI上に表示したい場合など 文字列に出力できるとよいのですが、標準では用意されていません。

方法の一つとして、Throwable#printStackTrace(PrintStream) や Throwable#printStackTrace(PrintWriter) を使用する方法があります。 引数に指定したストリームに結果を出力させることができるため、 Pipe ストリームを利用して結果をストリームから取り出すことができます。

例)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.PrintWriter;
import java.io.Reader;

public class StackTracePrintTest {
    ...
    private String getStackTraceString(Throwable e) throws IOException {
        // Pipe ストリームで出力ストリームと入力ストリームを接続する。
        PipedWriter pipeOut = new PipedWriter();
        PipedReader pipeIn = new PipedReader(pipeOut);
        PrintWriter out = new PrintWriter(pipeOut);

        // Throwable の printStackTrace メソッドで出力ストリームに書き出す。
        e.printStackTrace(out);
        out.flush();
        out.close();

        // 入力ストリームから結果を取得する。
        BufferedReader is = new BufferedReader(pipeIn);
        StringBuffer buf = new StringBuffer();
        String line;
        while ((line = is.readLine()) != null) {
            buf.append(line);
        }

        return buf.toString();
    }
}

上記の例の getStackTraceString メソッドで、 Throwable からスタックトレース情報を取得することが出来ます。

ただし、実際にこの処理をアプリケーション内で使おうとしたとき (おそらくマルチスレッドの状態で使うと) e.printStackTrace() の箇所以降に進まなくなってしまいました。 マルチスレッドの際のストリームの動作を調べれば解決するのでしょうが。。。

2006年06月01日

cygwin で java -jar コマンドを使用する際の注意点

cygwin では、cygwin のルートフォルダが C:\cygwin の場合、 たとえば C:\cygwin\usr\local は cygwin 上では /usr/local のようにあらわせます。 (ちょうど cygwin のルートフォルダが cygwin 上でのルートディレクトリになります)。

この事が頭にあると、java コマンドで jar ファイルを実行する際、 つい
% java -jar /usr/local/java/xxx.jar
のように実行してしまいますが、 そうすると
Unable to access jarfile
jar ファイルが見つからないとエラーが発生してしまいます。

実はここで実行されている java コマンドは、 cygwin の 内部コマンドではなく、あくまで Windows 用の java.exe に過ぎないので jarファイル名は Windows のパス形式「C:\cygwin\usr\local\java\xxx.jar」のように 指定しないといけません。

分れば何ということはないのですが、 気づくまで小一時間かかりました。。。