S2Axisあそび続きの続き

S2Axisのサンプルのex03をいじります。BookShelfをLogicクラスとして、Axisに特化しない形に直しましょう。getBooks()の戻り値を配列から books プロパティの型であるところのMapをそのまま返す形にしてみましょう。

public interface Bookshelf {
    void addBook(Book book);

    Book getBook(String isbn);

    Map getBooks();
}

実装はこっち。例外のテストもするので、getBook()の引数が空文字列だったら実行時例外を投げるようにしました。適当な名前の付け方だよなあ、まあ勘弁。

public class BookshelfImpl implements Bookshelf {
    private Map books = new HashMap();

    public void addBook(Book book) {
        books.put(book.getIsbn(), book);
    }

    public Book getBook(String isbn) {
        if(isbn.equals("")){
            throw new BookShelfRuntimeException("例外メッセージです。");
        }
        return (Book)books.get(isbn);
    }

    public Map getBooks() {    
        return books;        
    }
}

例外クラスの中身はなんてこたないので省略。自前で作った例外が上手く渡るかチェックしようと思った名残です。その時はAxisFaultを継承したのを作ったんだけど、AOPにしたのでRuntimeExceptionの子クラスにしました。

これでS2のサンプルにありそうな、Logicクラスの体裁になりましたね。な・り・ま・し・た・ね!

次、Axis用のActionクラスを作りましょう。名前、〜AxisServiceとか色々考えたんだけど、手の動くままてきとーに〜AxisActionなんてのにしちゃった。まあこれも勘弁。

public interface BookshelfAxisAction {
    void addBook(Book book);

    Book getBook(String isbn) throws AxisFault;

    Book[] getBooks();    

}

ここではもうAxis特化なので、throws宣言にAxisFaultを書いてます。宣言しないと上手い事クライアントに渡らないみたいです。

で、実装。

public class BookshelfAxisActionImpl implements BookshelfAxisAction {
    private Bookshelf bookshelf;

    public void addBook(Book book) {
        bookshelf.addBook(book);        
    }

    public Book getBook(String isbn) throws AxisFault {
        return bookshelf.getBook(isbn);
    }

    public Book[] getBooks() {      
        Collection values = bookshelf.getBooks().values();
        return (Book[]) values.toArray(new Book[values.size()]);    
    }
    
    public void setBookshelf(Bookshelf bookshelf) {
        this.bookshelf = bookshelf;
    }
    
    public Bookshelf getBookShelf(){
        return bookshelf;
    }

}

殆どLogic側に委譲しまくりなんですけど、配列への変換がこちらのgetBooks()に移ってます。

で、BookShelfRuntimeExceptionをAxisFaultへ変換するinterceptorを作りましょう。これもパカパカ気を遣わずに名前付けちゃった。遺憾ですな、もうしません、名前重要。

public class ExceptionToAxisFaultInterceptor extends ThrowsInterceptor {

    public void handleThrowable(RuntimeException t, MethodInvocation invocation)
    throws Throwable {
        throw new AxisFault(t.getMessage());
    }
}

これがですね、

        throw new AxisFault(t.getMessage(),t);

なんつって発生元の例外をネストすると、第一引数で突っ込んだメッセージが取れないんですよ。

; nested exception is: 
    org.seasar.remoting.axis.examples.ex03.BookShelfRuntimeException

なんてのが取れちゃう。なのでネストはしないでおきます。

で、diconはこう。

<components>
    <component name="traceInterceptor" class="org.seasar.framework.aop.interceptors.TraceInterceptor"/>
    <component name="throwsInterceptor" class="org.seasar.remoting.axis.examples.ex03.ExceptionToAxisFaultInterceptor"/>

    <component name="Bookshelf" class="org.seasar.remoting.axis.examples.ex03.BookshelfImpl" instance="session" />

    <component name="BookshelfAxisAction" class="org.seasar.remoting.axis.examples.ex03.BookshelfAxisActionImpl" instance="request" >
        <meta name="axis-service"/>
        <aspect>throwsInterceptor</aspect>
    </component>

</components>

Axisのサービスとして公開する時は、name属性は必須みたいです。S2JSFと同じですねい。Axisだからって特別な事、あまりないですね。instance属性がrequestなのは、S2JSFのActionクラスに倣いました。sessionでもいい気がするけど。

BookshelfImplのinstance属性がsessionなのは、Bookを保持するMapを持ってたりしてステートフルだからです。一応。

で、クライアント側でありますテストケースはこうです。

public class BookshelfTest extends S2TestCase {

(省略)

    public void test() throws Exception {
        BookshelfAxisAction bookshelf = (BookshelfAxisAction) getComponent(BookshelfAxisAction.class);

(省略)
    }
    
    public void testException() throws Exception {
        BookshelfAxisAction bookshelf = (BookshelfAxisAction) getComponent(BookshelfAxisAction.class);
        
        try{
            Book book = bookshelf.getBook("");
            fail("例外が呼ばれないぞ。");
        }
        catch(AxisFault ex){ 
            
            System.out.println( ex.getMessage()); 
            System.out.println(ex.getClass().getName());
            assertTrue(true);
        }
        
    }
}

BookshelfからBookshelfAxisActionへ替えて、例外用テストを追加しました。そうそう、昨日、メッセージとるのにgetFaultString()使うとか書いてますが、getMessage()で普通に取れました。おかしーなー昨日はダメだった気がするんだけど、色々と原因がからまってたんだな。

クライアントのdiconも〜AxisActionへ変更します。

(省略)
    <component class="org.apache.axis.client.Service" autoBinding="none">
        <property name="maintainSession">true</property>
    </component>
(省略)
    <component class="org.seasar.remoting.axis.examples.ex03.BookshelfAxisAction">
        <aspect>remoting</aspect>
    </component>

セッション管理するので、ex04を参考にmaintainSessionをtrueにします。

ではレッツゴー。

グリーングリーン。イエーイ。コンソールには

例外メッセージです。
org.apache.axis.AxisFault

とちゃんと表示されました。

所感

まだ未経験のフレームワークやらのチュートリアルや本や雑誌記事を見て、一番ひっかかるのは例外処理などの異常系がどうなるかなんですよね。これがちゃんと載ってる事ってあまりないなーと感じています。

なんか読んでても、どうしてもモヤモヤが残って、おちんちんのあたりがムズムズするんですよ。不安と云うか。

まあ濃いドキュメントや突っ込んだ本を読めって事なんでしょうけど、導入するかしないか検討したいなあって時には、雑誌などの入門記事やオフィシャルのチュートリアルで済ませたい訳ですよ、人情として。

わがままかなあ。まあ自分でいじれ!って事なんでしょうねぇ今回みたいに。やってみりゃ大した事でもなかったし。