覚えたら書く

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

MBassador - メッセージをフィルタリングする

先日のエントリで、MBassadorでメッセージの型によって反応するハンドラが変わるサンプルを示しました。


MBassadorでは、同じ型のメッセージでもメッセージ内容をフィルタリングして受け取るハンドラを切り替えることができます。

概要

フィルタリングは大まかに以下のやり方で実施します。

  1. IMessageFilterを実装してフィルタークラスを定義する
  2. IMessageFilter#acceptsをオーバーライドして、引数のメッセージをハンドラで受信するかどうかを判定します
  3. @Handlerのfiltersパラメータで定義したフィルタークラスを指定する
  4. @Handler(filters = @Filter(SomethingFilter.class)) の形式でフィルターを利用することを指定します


以下具体的なサンプルとなります

文字列の内容によるフィルタリング

EventBusに発行された文字列の先頭が"http:"か"ftp:"かをフィルタリングして、メッセージを受け取るハンドラを切り替えます

■ハンドラの定義

フィルターのクラスを定義して、そのフィルターをハンドラのパラメータに指定しています。

  • PrefixHttpFilter ・・・ 文字列の先頭が"http:"であるかどうかを判定するためのフィルターです
  • PrefixFtpFilter ・・・ 文字列の先頭が"ftp:"であるかどうかを判定するためのフィルターです
import net.engio.mbassy.listener.Filter;
import net.engio.mbassy.listener.Handler;
import net.engio.mbassy.listener.IMessageFilter;
import net.engio.mbassy.subscription.SubscriptionContext;

class MsgListener1 {

    @Handler(filters = @Filter(PrefixHttpFilter.class))
    public void httpHandle(String msg) {
        System.out.println("http Handle::" + msg);
    }

    @Handler(filters = @Filter(PrefixFtpFilter.class))
    public void ftpHandle(String msg) {
        System.out.println("ftp Handle::" + msg);
    }

    public static class PrefixHttpFilter implements IMessageFilter<String> {

        @Override
        public boolean accepts(String message, SubscriptionContext context) {
            return message.startsWith("http:");
        }
    }

    public static class PrefixFtpFilter implements IMessageFilter<String> {

        @Override
        public boolean accepts(String message, SubscriptionContext context) {
            return message.startsWith("ftp:");
        }
    }
}

■EventBus生成・Handlerのsubscribe・メッセージ発行

"ftp:"で始まるメッセージと"http:"で始まるメッセージとを発行しています

import net.engio.mbassy.bus.MBassador;

public class SampleLauncher1 {

    public static void main(String[] args) {

        System.out.println(">>> App start");

        MBassador eventBus = new MBassador();
        MsgListener1 listener = new MsgListener1();
        eventBus.subscribe(listener);    // ハンドラの登録

        // FTP messeage
        eventBus.publishAsync("ftp://user1:pass000@example-dummy.org/");
        eventBus.publishAsync("ftp://user2:pass000@example-dummy.org/");
        eventBus.publishAsync("ftp://user3:pass000@example-dummy.org/");


        // HTTP messeage
        eventBus.publishAsync("http://example-dummy.org/books/1001");
        eventBus.publishAsync("http://example-dummy.org/books/1002");
        eventBus.publishAsync("http://sample-dummy.org:8080/books/");

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

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

■実行結果

>>> App start
ftp Handle::ftp://user1:pass000@example-dummy.org/
ftp Handle::ftp://user2:pass000@example-dummy.org/
ftp Handle::ftp://user3:pass000@example-dummy.org/
http Handle::http://example-dummy.org/books/1001
http Handle::http://example-dummy.org/books/1002
http Handle::http://sample-dummy.org:8080/books/

>>> App end

"ftp:"で始まるメッセージにはftpHandleメソッドが反応して、"http:"で始まるメッセージにはhttpHandleメソッドが反応していることが分かります。


独自定義したクラスのメンバの内容によるフィルタリング

自分で独自に定義したクラスをメッセージとしてEventBusに対して発行し、そのクラスのメンバの値によって反応するハンドラを切り分けるサンプルです。

■独自クラス(メッセージ)

以下のPetクラスをEventBusに対して発行します

import lombok.Value;

@Value
public class Pet {

    private String name;

    private Type type;

    enum Type {
        CAT,
        DOG,
        OTHER;
    }
}

■ハンドラの定義

Petクラスのtypeのenumの値を判定するフィルタークラスを定義して、各ハンドラに指定しています。

  • CatFilter ・・・ Petクラスのtypeの値がCATかどうかを判定するためのフィルターです
  • DogFilter ・・・ Petクラスのtypeの値がDOGかどうかを判定するためのフィルターです
  • OtherFilter ・・・ Petクラスのtypeの値がOTHERかどうかを判定するためのフィルターです
import net.engio.mbassy.listener.Filter;
import net.engio.mbassy.listener.Handler;
import net.engio.mbassy.listener.IMessageFilter;
import net.engio.mbassy.subscription.SubscriptionContext;

class PetListener {

    @Handler(filters = @Filter(CatFilter.class))
    public void catHandle(Pet pet) {
        System.out.println("Cat Handle::" + pet);
    }

    @Handler(filters = @Filter(DogFilter.class))
    public void dogHandle(Pet pet) {
        System.out.println("Dog Handle::" + pet);
    }

    @Handler(filters = @Filter(OtherFilter.class))
    public void otherHandle(Pet pet) {
        System.out.println("Other Handle::" + pet);
    }

    public static class CatFilter implements IMessageFilter<Pet> {

        @Override
        public boolean accepts(Pet pet, SubscriptionContext context) {
            return pet.getType() == Pet.Type.CAT;
        }
    }

    public static class DogFilter implements IMessageFilter<Pet> {

        @Override
        public boolean accepts(Pet pet, SubscriptionContext context) {
            return pet.getType() == Pet.Type.DOG;
        }
    }

    public static class OtherFilter implements IMessageFilter<Pet> {

        @Override
        public boolean accepts(Pet pet, SubscriptionContext context) {
            return pet.getType() == Pet.Type.OTHER;
        }
    }
}

■EventBus生成・Handlerのsubscribe・メッセージ発行

Cat, Dog, Otherの3種のPetをメッセージとしてEventBusに発行します

import net.engio.mbassy.bus.MBassador;

public class SampleLauncher2 {

    public static void main(String[] args) {

        System.out.println(">>> App start");

        MBassador eventBus = new MBassador();
        PetListener listener = new PetListener();
        eventBus.subscribe(listener);    // ハンドラの登録

        // cat
        eventBus.publishAsync(new Pet("miko", Pet.Type.CAT));

        // dog
        eventBus.publishAsync(new Pet("panchi", Pet.Type.DOG));

        // other
        eventBus.publishAsync(new Pet("hebio", Pet.Type.OTHER));

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

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

■実行結果

>>> App start
Cat Handle::Pet(name=miko, type=CAT)
Dog Handle::Pet(name=panchi, type=DOG)
Other Handle::Pet(name=hebio, type=OTHER)

>>> App end

各typeに対応するフィルターが設定されたハンドラが反応していることが分かります。



関連エントリ