OpenDaylight Potassiumでプラグイン開発(Notification処理実装編)
はじめに
前回では、DataTreeChangeListenerを使ってDatastoreの更新イベントを契機に様々な処理ができることを確認しました。
今回は、OpenDaylightのコア機能であるMD-SALのNotification処理の実装をしていきたいと思います。
今回は、OpenDaylightのコア機能であるMD-SALのNotification処理の実装をしていきたいと思います。
概要
ODLのMD-SALにはNotificationを扱う機構が存在します。これにより、イベントの送受信機能が実装可能となり、例えばモジュール間でのpub/subパターン等を簡単に実装可能になります。
Notificationの定義はYANG で定義可能です。第二章で記載しましたが、 YANG Toolsを利用することで必要なinterfaceやclassが自動生成されます。
今回は、イベント発生契機として前回作成したDataTreeChangeListenerを使います。また、イベント受信(リスナー)では、イベント受信を契機にログ出力する機能を実装していきたいと思います。
本記事のソースコードは以下から参照可能です。
https://github.com/t-matz/techblog-odlsample/tree/blog03
実装
以下の手順でNotificationの動作を確認します。
- YANGモデルの追加
- ソースコードの実装
- ビルド/ODLで読み込み
- CRUD操作をしてNotificationを確認
YANGモデルの追加
Notification をYANGに定義することで、必要なinterfaceなどが自動生成され、使用できるようになります。今回は
これらのファイルを利用して実装を進めていきます。
sample-notify
という名前で、中身はmessage
という文字列フィールドが一つ存在するNotificationを定義しました。
module odlsample {
// 省略
notification sample-notify {
leaf message {
type string;
}
}
}
YANG定義後ビルドすると以下の配下に、ファイルが自動生成されます。
api/target/generated-sources/BindingJavaFileGenerator/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yangodlsample/rev/240304
自動生成されるファイル- SampleNotify.java :Notificationの中身のクラス
- SampleNotifyBuilder.java:SampleNotifyオブジェクトのビルダ
これらのファイルを利用して実装を進めていきます。
ソースコードの実装
受信処理の実装
受信処理用のファイルを、OdlsampleProvider.javaと同じ階層に作成します。今回はSampleNotificationListener.javaという名前で作成しました。
Listenerインターフェースの型パラメータとして
SampleNotify
をセットすることで、今回定義したSampleNotify
を受信するリスナーとなります。ListenerインターフェースのonNotification
で、Event受信時の処理内容を定義しています。今回はSampleNotify
のmessageというフィールドの文字列をログに出力する単純な実装になっています。
public final class SampleNotificationListener implements Listener<SampleNotify>, AutoCloseable {
// 省略
@Override
public void onNotification(@NonNull SampleNotify notification) {
LOG.info("MESSAGE RECIEVED: {}.", notification.getMessage());
}
リスナーの登録
第二章で説明したDataTreeChangeListener
と同様に、Listenerを登録する必要があります。このListenerはNotificationService
を使って登録することが出来ます。今回も、Listener自身のコンストラクタで登録処理を実装していきたいと思います。
public final class SampleNotificationListener implements Listener<SampleNotify>, AutoCloseable {
private final NotificationService notifyService;
private final Registration registration;
private static final Logger LOG = LoggerFactory.getLogger(SampleNotificationListener.class);
public SampleNotificationListener(final NotificationService notifyService) { // --- 1
this.notifyService = notifyService;
this.registration = this.notifyService.registerListener(SampleNotify.class, this); // --- 2
}
- ODLがあらかじめ用意している
NotificationService
はコンストラクタの引数として渡しており、こちらはインスタンス化する際にblueprint経由で渡されます。 NotificationService ::registerListener
でリスナーの登録ができます。引数として受信するNotificationのクラスとListenerクラス(今回は自分自身となるのでthis
)を指定します。
リスナーのインスタンス化
今回、リスナーのクラスとして新たにSampleNotificationListener
を定義しています。現状では定義しただけであり、実際に動作させるためには、定義したクラスをインスタンス化する必要があります。ODLでは、このような場合にblueprintの機能を用いてインスタンス化する仕組みがよく使われます。blueprintの定義は
impl/src/main/resources/OSGI-INF/blueprint/impl-blueprint.xml
にあります。今回新たに追加する部分は以下の部分となります。新たに定義した
SampleNotificationListener
のインスタンス化処理をxmlで記述することで、モジュール起動時にインスタンス化してくれます。また、先述の通りコンストラクタの引数としてNotificationService
を渡しています。
<reference id="notificationService"
interface="org.opendaylight.mdsal.binding.api.NotificationService" />
<bean id="listener"
class="jp.apresia.techblog.impl.SampleNotificationListener"
init-method="init" destroy-method="close">
<argument ref="notificationService" />
</bean>
送信処理の実装
送信のタイミングは自由に決められますが、今回はDataTreeChangeListener
をイベントの契機とするため、該当箇所にコードを追記します。自動生成された
SampleNotifyBuilder
クラスを使用してNotificationの中身を設定します。SampleNotifyBuilder::setMessage
で定義したmessage
フィールドに文字列をセットすることが出来ます。今回はCRUD操作のmodifiedType
を文字列のメッセージとして設定しています。
@Override
public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Sample>> changes) {
WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
for (DataTreeModification<Sample> change : changes) {
LOG.trace("change, {}", change);
final DataObjectModification<Sample> root = change.getRootNode();
updateData(wtx, root.getModifiedChildren());
// Notification
SampleNotify notify = new SampleNotifyBuilder()
.setMessage(root.getModificationType().name())
.build();
this.pubService.offerNotification(notify);
}
}
またthis.pubService.offerNotification(notify);
でイベントを送信していますが、このpubService(NotificationPublishService)もblueprintでコンストラクタ引数として渡していることに注意してください。
<!-- 省略 -->
<reference id="pubService"
interface="org.opendaylight.mdsal.binding.api.NotificationPublishService" />
Notificationの確認
実装完了したら、前回同様mvnコマンドでビルドして、ODLで読み込みます。以下のアドレスにアクセスして、CRUD操作がLOGに出力されるか確認していきます。
http://localhost:8181/openapi/explorer/index.html
GETはデータの取得だけで、データツリーの変更はないため、ログには表示されません。
POST/PUT/DELETEでデータを操作すると以下がログとして表示されます。
http://localhost:8181/openapi/explorer/index.html
GETはデータの取得だけで、データツリーの変更はないため、ログには表示されません。
POST/PUT/DELETEでデータを操作すると以下がログとして表示されます。
opendaylight-user@root>log:tail
INFO [DOMNotificationRouter-listeners-0] MESSAGE RECIEVED: SUBTREE_MODIFIED.
INFO [DOMNotificationRouter-listeners-0] MESSAGE RECIEVED: WRITE.
INFO [DOMNotificationRouter-listeners-0] MESSAGE RECIEVED: DELETE.
おわりに
Notificationを利用して、イベント送受信処理の実装が出来ました。また、今回は同一モジュール内で確認しましたが、こちらの機能は、異なるモジュール間でも実現可能です。
今回で4回にわたって連載してきた「OpenDaylight Potassiumでプラグイン開発」シリーズは終了となります。OpenDaylightに関する公開資料は少なく、古いものが多いため、調査が困難でした。 本記事もすぐに古くなってしまうと思いますが、何かの役に立てば幸いです。
今回で4回にわたって連載してきた「OpenDaylight Potassiumでプラグイン開発」シリーズは終了となります。OpenDaylightに関する公開資料は少なく、古いものが多いため、調査が困難でした。 本記事もすぐに古くなってしまうと思いますが、何かの役に立てば幸いです。