覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

MBassador - EventBus内で発生した例外をハンドリングする

MBassadorを用いてイベントのハンドリングを行っている場合、デフォルトではハンドラ内部で例外が発生しても何事も無かったようになってしまい、エラーが発生したことすら分からないような状態になってしまいます。

さすがにこれはまずいので、基本的にはエラーをハンドリングをするためのエラーハンドラをEventBusに対してセットする必要があります。


エラーハンドラをセットしない場合

まず、エラーハンドラをセットしなかった場合の動きを確認しておきます

■Handlerの定義

ハンドラ内であえてIllegalArgumentExceptionをスローするようにしています

import net.engio.mbassy.listener.Handler;

public class MsgHandler {

    @Handler
    public void handlMessage(String msg){
        System.out.println("handle start. msg=" + msg);

        System.out.println("---------------");

        throw new IllegalArgumentException("null parameter");
    }
}

■EventBusを作成してメッセージ送信

EventBusに上で定義したハンドラをセットしてメッセージを送信しています。

import net.engio.mbassy.bus.MBassador;

public static void main(String[] args) {

    MBassador<Object> bus = new MBassador<>();
    bus.subscribe(new MsgHandler());

    bus.publishAsync("msg-1");

    try {
        Thread.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("\n>>> App end");
}

■実行結果

handle start. msg=msg-1
---------------

>>> App end

エラー(IllegalArgumentException)が発生したことが全く分からない状態になっています


エラーハンドラをセットする場合

次にEventBusに対してエラーハンドラをセットするサンプルです。
(メッセージのHandlerには先の例と同じMsgHandlerクラスを使います)

■エラーハンドラの定義

エラーハンドラはIPublicationErrorHandlerインターフェースを実装したクラスとして定義する必要があります
(EventBus内での例外発生時に、handleErrorメソッドにエラーの詳細情報が渡されて呼び出されます。)

import net.engio.mbassy.bus.error.IPublicationErrorHandler;
import net.engio.mbassy.bus.error.PublicationError;

final class EventBusErrorHandler implements IPublicationErrorHandler {


    @Override
    public void handleError(PublicationError publicationError) {
        System.out.println("publicationError = " + publicationError);

        publicationError.getCause().printStackTrace();
    }
}

■EventBusを作成してメッセージ送信

EventBusに上で定義したハンドラをセットしてメッセージを送信しています。
先の例とは違いIBusConfigurationを生成してIBusConfiguration#addPublicationErrorHandlerでエラーハンドラをセットします。
そして生成したIBusConfigurationをEventBus(MBassadorのコンストラクタ)に渡します。

import net.engio.mbassy.bus.MBassador;
import net.engio.mbassy.bus.config.BusConfiguration;
import net.engio.mbassy.bus.config.Feature;
import net.engio.mbassy.bus.config.IBusConfiguration;

public static void main(String[] args) {

    IBusConfiguration config = new BusConfiguration()
            .addFeature(Feature.SyncPubSub.Default())
            .addFeature(Feature.AsynchronousHandlerInvocation.Default())
            .addFeature(Feature.AsynchronousMessageDispatch.Default())
            .addPublicationErrorHandler(new EventBusErrorHandler());

    MBassador<Object> bus = new MBassador<>(config);
    bus.subscribe(new MsgHandler());

    bus.publishAsync("msg-1");

    try {
        Thread.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("\n>>> App end");
}

■実行結果

handle start. msg=msg-1
---------------
publicationError = PublicationError{
    cause=java.lang.reflect.InvocationTargetException
    message='Error during invocation of message handler. There might be an access rights problem. Do you use non public inner classes?'
    handler=public void trial.app.mbassador.ErrorMsgHandler.handlMessage(java.lang.String)
    listener=trial.app.mbassador.ErrorMsgHandler@68de145
    publishedMessage=msg-1}
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at net.engio.mbassy.dispatch.ReflectiveHandlerInvocation.invoke(ReflectiveHandlerInvocation.java:29)
    at net.engio.mbassy.dispatch.MessageDispatcher.dispatch(MessageDispatcher.java:30)
    at net.engio.mbassy.dispatch.FilteredMessageDispatcher.dispatch(FilteredMessageDispatcher.java:42)
    at net.engio.mbassy.subscription.Subscription.publish(Subscription.java:72)
    at net.engio.mbassy.bus.MessagePublication.execute(MessagePublication.java:49)
    at net.engio.mbassy.bus.AbstractSyncAsyncMessageBus$1.run(AbstractSyncAsyncMessageBus.java:67)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalArgumentException: null parameter
    at trial.app.mbassador.ErrorMsgHandler.handlMessage(ErrorMsgHandler.java:13)
    ... 11 more

>>> App end

メッセージのハンドラ内で例外がスローされた際に、エラーハンドラが動作していることが分かります。
エラー発生時の原因例外はPublicationError#getCauseから辿ることができます。



関連エントリ