第3回 JXTAのDiscoveryの働き
丸山不二夫
稚内北星学園大学学長
(http://www.wakhok.ac.jp/)
2001/12/22
本連載では、成長過程にあるJXTAのテクニカルな部分に触れながら、JXTAによって実現可能となるPtoPサービスの可能性にアプローチしていきたい。また、大きなリリースのタイミングでは、その詳細について解説する。(編集局) |
今回は、shareコマンドとsearchコマンドのソースを読みます。JXTA-Shellのコマンド作成に必要な情報を紹介しながら、JXTAがどのように「告知」の公開・探索を行っているのかを見ていきたいと思います。
今回の内容
|
JXTA-Shellのコマンド・クラスをどこに作るか JXTA-Shellのイディオム Shell内部の変数の値を獲得するには Discoveryインスタンスの獲得 shareコマンドsearchコマンドの処理 pushメソッドとgetLocalAdvertisemententsメソッド 探索メッセージは、どのように送られるか? Discoveryの働きのまとめ |
JXTA-Shellのコマンド・クラスをどこに作るか |
JXTA-Shellのコマンドは、すべてがnet.jxta.impl.shell.ShellAppクラスを継承して作られます。JXTA-Shellコマンドを作成するためには、このクラスのほかに、net.jxta.impl.shell.*以下のクラスが必要になります。
shareコマンド・クラスのパッケージ名とクラス名を示します。パッケージ名ですが、net.jxta.impl.shell.bin.+shareという形をしています。このshare以前の名前は予約されています。この固定部分にコマンド名(この例の場合にはshare)を付けたのが、Shellコマンド・クラスのパッケージ名になります。さらに、コマンド名と同じ名前のクラスが、このパッケージの中にある必要があります。
package net.jxta.impl.shell.bin.share; |
もしもcmdXという名前のJXTA-Shellコマンドを作りたいのであれば、./net/jxta/impl/shell/bin/cmdX/cmdX.classというパス名を持つクラスファイルが必要だということです。なにか、cmdXが二重になっていると思われるかもしれませんが、そうではありません。もしも、コマンドcmdX用に補助的なヘルパー・クラスが必要であれば、これらのcmdXに関連するファイルたちを、cmdXディレクトリ以下に次のように配置すればいいのです。
./net/jxta/impl/shell/bin/cmdX/cmdX.class |
JXTA-Shellのイディオム ―― getEnvとgetDiscovery ―― |
JXTA-Shellでは、コマンド文字列の実行はShellAppインスタンスのstartAppメソッドの呼び出しとして実行されます。このとき、コマンドに与えられていた引数たちは文字列の配列としてこのstartAppメソッドに渡されます。次に示すのは、shareコマンドのstartAppメソッドのリストです。
public int startApp (String[] args) {<BR> |
このソースの次の処理は、ほとんどすべてのJXTA-Shellコマンドで行われている基本的なイディオムといっていいものです。
env = getEnv(); |
Shell内部の変数の値を獲得するには |
env = getEnv();のenvはShellEnvクラスのインスタンスで、JXTA-Shell内部の変数の値を獲得する際に用いられます。envはHashtableと同じようなものなのですが、直接変数を格納しているわけではなく、オブジェクトと変数名のペアから成るShellObjectというオブジェクトのインスタンスを格納しています。これは、変数名を表す文字列がnullのときに、自動的に変数名を生成して値を格納する際に利用される仕掛けなのですが、そのために、変数名を指定してその値を引き出すときには、次の例のように、いったんgetメソッドでShellObjectを獲得してから、getObjectメソッドを実行しなければなりません。
ShellObject obj =
env.get ("stdgroup"); |
この2段階のシーケンスはよく登場しますので、このパターンを頭に入れておいてください。
Discoveryインスタンスの獲得 |
Discoveryインスタンスのdiscoは、share/searchコマンドをはじめ、JXTA-Shellのいくつかのコマンドの中でdisco = group.getDiscovery();で獲得されます。このdiscoは重要な役割を果たします。つまり、discoはShellAppクラスを拡大したコマンド・クラスの中でprivate Discovery disco=null;と定義されているのですが、net.jxta.discovery.Discoveryはインターフェイスなので、正確にいえば、discoはDiscoveryインターフェイスを実装したインスタンスということになります。
次に、コマンドsearchのソースの一部を示します。ここでもgetEnvメソッドとgetDiscoveryメソッドが使われていることを確認してください。
public class search extends ShellApp { |
shareコマンドとsearchコマンドの処理 |
先のshareコマンドとsearchコマンド、2つのstartAppメソッドのソースを眺めれば、shareコマンドの処理の中心がpublishAdv(adv);というメソッド呼び出しであり、searchコマンドの処理の中心がdiscover(pid,attr,val)とgetLocal(attr,val)いう2つのメソッド呼び出しであることが分かります。これらのメソッドのソースを次に示します。
private void publishAdv(Advertisement adv) { |
private int discover(String address, String attr, String val ) { |
private int getLocal(String attr, String val) { |
こうして整理すると、shareコマンドは基本的にはDiscoveryインスタンスに対するpublishメソッドの呼び出しであり、searchコマンドは同じくDiscoveryインスタンスに対するgetRemoteAdvertisementsメソッド、あるいはgetLocalAdvertisementsメソッドの呼び出しであることが分かります。
publishメソッドとgetLocalAdvertisementsメソッド |
net.jxta.impl.discovery.DiscoveryServiceクラスのpublishメソッドのソースの一部を示します。publishは、告知のタイプがGROUPかPEERかそのほかの告知かによって、基本的にはそれらのIDを取り込んで一意なファイルの名前を作り、告知のXMLドキュメントを格納しローカルなディレクトリ上に書き出します。
public void publish(Advertisement adv, int type, |
publishされた告知、また、次に見るgetRemoteAdvertisementsでネットワーク上で発見された告知は、具体的にはJXTAが起動されたディレクトリ下のcmという名前のディレクトリの下に、次のように収められています。画面1を参照してください。ここでは、Advと名付けられたディレクトリ下に、cmで始まる名前を持ついくつかのファイルがあることが分かりますね。このファイルの中にいくつかの告知がXML文書として公開されているわけです。
画面1 |
今回、getLocalAdvertisementsメソッドのソースは省略しますが、こうしたcmディレクトリ下のXMLドキュメントから、attributeやvalueといった検索条件に合致した告知たちを選び出す働きをしています。publishメソッドが告知を書き出して、getLocalAdvertisementsメソッドがそれを読み出すわけですから、この2つのメソッドは、相互に補い合う働きをしていることになります。
getRemoteAdvertisementsメソッドのソースを読む |
先の2つのメソッドが、ともにローカルに働いているのに対して、searchコマンドが-rオプションを付けて呼ばれたときに、内部で呼び出されるgetRemoteAdvertisementsメソッドは、ネットワークを越えてほかのPeer上の告知を見つけに行きます。Discoveryインスタンス上で定義されたこれらのメソッドのこうした働きは、本連載のJavaでのJXTAプログラムの紹介ですでに見てきました。
JXTA-Shellのコマンドが、JXTA-JavaのAPIを利用して書かれていることをこれまで見てきたのですが、publishやgetRemoteAdvertisementsといった、JXTA-JavaのAPIに登場するメソッドのソースも簡単に手に入ります。今回は、これらのソースのメソッドの内部の処理をもう少し詳しく見てみることにします。次に、getRemoteAdvertisementsメソッドのソースを示します。
public int getRemoteAdvertisements(String peer, int type, |
初めにDiscoveryQueryというクラスのインスタンスdqueryが生成されています。このインスタンスに、setAttrやsetValueを使い、引数で与えられているattributeやvalueの値を設定しているようです。続いてResolverQueryクラスのインスタンスを生成しています。そのコンストラクタの引数をよく見ると、dquery.toString()という形で、先に作ったdqueryインスタンスを文字列にしてqueryを作るのに使っていますね。このqueryがsendQueryメソッドの引数に与えられます。Resolverインスタンスに対するsendQueryメソッドの呼び出しが、getRemoteAdvertisementsメソッドの中心部分を構成しています。このnet.jxta.impl.resolver.ResolverServiceのsendQueryメソッドは、JXTAのメッセージ交換メカニズムの特徴をよく表しています。
探索メッセージは、どのように送られるか? |
ここでQueryと呼ばれているものは、まず、「これこれの告知を検索せよ」という探索命令だと考えてください。細かなメカニズムは後で見ることとして、最初に確認したいのは、各ピアは、こうしたQueryを受け取るとその問い合わせに対する「回答」を作り出して、それを質問者に送り返そうとするということです。探索ですから、できるだけ多くのPeerに対してこうした問い合わせを行う必要があります。問題は、Peer to Peerの通信を特色とするJXTAが、どのようにして多数のピアに対する問い合わせを実行するかということです。
net.jxta.impl.resolver.ResolverServiceのsendQueryのソースを次に示します。このメソッドは、第1引数がnullのときには、まさに、たくさんのピアに探索メッセージを送りつける働きをします(逆に、第1引数が与えられれば、そのピアに対してだけ、メッセージを送ろうとします)。
public void sendQuery(String rdvPeer, |
問い合わせメッセージを、たくさんのピアに伝播(Propagate)する働きを担っているのは、次のpropagateInGroupです。後に、sendToNetwork、sendToEachRendezVous、sendToEachClientという3つのメソッドが並んでいますね。この3つのメソッドの並びが、JXTAに特徴的なPropagateの働きをよく表しています。
public void propagateInGroup(Message msg, String serviceName, |
sendToNetworkメソッドは、一番近い所に存在するピア同士がメッセージを交換するときに用いられます。同じネットワークに属するピア同士が、マルチキャストを通じて、メッセージを送るときに利用されるメソッドです。図1で、ピアAから、同一ネットワーク(正確にいえば、マルチキャスト・メッセージが到達可能なサブネット)内のピアB、C、Dに対するメッセージの送り出しです。
図1 |
sendToEachRendezVousは、あるピアから、そのピアが利用しているすべてのRendezVousに対してメッセージを送り出します。図2で、E、F、G、HはRendezVousを表しています。DからE、F、Gへの送り出し、CからHへの送り出しは、sendToEachRendezVousメソッドの働きを表しています。
図2 |
sendToEachClientが意味を持つのは、送り手側のピアがRendezVousである場合だけです。そうしたとき、このメソッドは、そのピアをRendezVousとしているすべてのクライアント・ピアに対してメッセージを送ります。図3で、RendezVousであるE、F、G、Hから発せられるメッセージが、このsendToEachClientメソッドの働きを示しています。
図3 |
このようにして、この3つのメソッドを内に含む、propagateInGroupメソッドの働きによって、Aから出発した探索メッセージは、ネットワークの中に大きく広がることができます。このメソッドの第4引数であるdefaultTTLは、こうした拡大を無限に続けるのではなく、ここで指定された整数値のホップでpropagateを終了せよという意味を持っています。defaultでは、このTTL(Time To Live)の値は7に設定されています。
Discoveryの働きのまとめ |
Discoveryの働きの探索は次回にも続きます。ここまでの過程を、ひとまずまとめておきましょう。
DiscoveryインスタンスでのgetRemoteAdvertisementsメソッドの呼び出しは、探索queryメッセージを引数に与えた、Resolverインスタンス上でのsendQueryメソッドの呼び出しを引き起こします。このことは、DiscoveryServiceがより基本的なサービスであるResolverServiceに「問題解決」の仕事を依頼していると考えることができます。ResolverServiceが解決すべき問題が、この場合は、「探索依頼」だったということです。
Resolverインスタンス上でのsendQueryメソッドの呼び出しは、今度は、Rendezvousインスタンス上でのpropagateInGroupメソッドの呼び出しを引き起こします。このメソッドによって、最初のgetRemoteAdvertisementsメソッドを発したピアからの探索queryは、ネットワーク上の数多くのピアに広がっていきます。
ところで、この最後のメッセージのPropagateの説明には少しおかしいところがあります。先に、propagateInGroup内の3つのメソッドの働きを3つの図で説明したのですが、それぞれのPropagateの中心には別のピアが存在しています。ピアAでpropagateInGroupメソッドが呼ばれたなら、ピアB、C、Dにはメッセージが届きますが、それ以上にはメッセージは広がらないはずです。
先の説明のようにメッセージが広がってゆくというのなら、メッセージを受け取ったピアが、バケツリレーのようにメッセージを、次のピアに送り出さなくてはなりません。JXTAには、実際そうしたメカニズムが組み込まれています。
次回も今回に引き続き、Discoveryの働きを、さらに見ていきたいと思います。
連載記事一覧 |
- 実運用の障害対応時間比較に見る、ログ管理基盤の効果 (2017/5/9)
ログ基盤の構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。今回は、実案件を事例とし、ログ管理基盤の有用性を、障害対応時間比較も交えて紹介 - Chatwork、LINE、Netflixが進めるリアクティブシステムとは何か (2017/4/27)
「リアクティブ」に関連する幾つかの用語について解説し、リアクティブシステムを実現するためのライブラリを紹介します - Fluentd+Elasticsearch+Kibanaで作るログ基盤の概要と構築方法 (2017/4/6)
ログ基盤を実現するFluentd+Elasticsearch+Kibanaについて、構築方法や利用方法、実際の案件で使ったときの事例などを紹介する連載。初回は、ログ基盤の構築、利用方法について - プログラミングとビルド、Androidアプリ開発、Javaの基礎知識 (2017/4/3)
初心者が、Java言語を使ったAndroidのスマホアプリ開発を通じてプログラミングとは何かを学ぶ連載。初回は、プログラミングとビルド、Androidアプリ開発、Javaに関する基礎知識を解説する。
|
|