レベル: 中級 Taylor Cowan (taylor_cowan@yahoo.com), Senior Software Systems Engineer, Travelocity
2008年 4月 29日 RDF (Resource Description Framework) は、W3C (World Wide Web Consortium) が提唱する Web 上でのデータのリンクおよび表現に関する標準です。セマンティック Web アプリケーションを開発する Java™ 開発者は、RDF プロパティーを Java 型に、あるいは Java 型を RDF プロパティーに変換しなければなりません。Jenabean は Jena のセマンティック Web フレームワークの柔軟な RDF/OWL API を使用して JavaBeans を存続させることで、セマンティック Web アプリケーションの作成を Java 開発者にとって簡単かつ馴染みのある作業にします。
Java 開発者は幸運です。なぜなら、Jena の優れた RDF フレームワークを使用できるからです。Jena には RDF の書き込み/読み取り用の API があり、RDF をさまざまな方法で保存し、存続させることができます。Jena をまだよく知らないという方は、この記事を読む前に Philip McCarthy の記事「Jena 入門書」を読むことを是非お勧めします。
JDBC がリレーショナル・モデルを扱うのに適しているのと同じく、Jena は RDF データ・モデルを扱いやすいように設計されています。データベース・アプリケーションの大半を構成するのは、Java オブジェクトを保存するためのコード、そしてデータベースからのオブジェクトをマーシャリングするためのコードです。Java コードで作成されたセマンティック Web アプリケーションも、データベース・アプリケーションと同じような問題に直面します。つまり、Java オブジェクトを RDF に変換するか、あるいは RDF を Java オブジェクトに変換しなければなりません。その結果、開発者は Java 固有のモデル (通常は JavaBeans) と Jena の RDF を中心とした API との間のギャップを埋めるために、かなりの量のコードを作成せざるを得なくなります。
この記事では、Java と RDF とのバインディング・フレームワークである Jenabean (「参考文献」を参照) がこのプロセスを容易にし、作成しなければならないコードの量を減らす仕組みを説明します。Jena クライアント・コードの例を検討し、Jenabean の JavaBean ベースのプログラミング・モデルと比較していきますが、まずは単純な例を用いて以下の作業を行う方法を紹介します。
- Bean を RDF として保存する
- RDF として保存した Bean のプロパティーを特定の RDF プロパティーにバインドする
- RDF をその他のオブジェクトに関連付ける
- RDF を再び Bean として読み取る
Jenabean プログラミング・モデル
リスト 1 のサンプル RDF を見てください。この単純なサンプルは、読みやすいように N3 (Notation3) フォーマットになっています (「参考文献」を参照)。
リスト 1. サンプル RDF (N3 フォーマット)
<http://www.ibm.com/developerworks/xml/library/j-jena/>
a dc:Article ;
dc:creator "Philip McCarthy"^^xsd:string ;
dc:subject "jena, rdf, java, semantic web"^^xsd:string ;
dc:title "Introduction to Jena"^^xsd:string .
|
リスト 1 は、記事「Introduction to Jena」の著者が Philip McCarthy で、この記事では jena、rdf、java、semantic web について説明していることを宣言しています。またこのサンプルで使われている語彙は、Dublin Core メタデータによって分類される語彙の一部です (「参考文献」を参照)。上記の RDF ステートメントを Jena の Java API をそのまま使って複製するには、リスト 2 の方法が考えられます。
リスト 2. Jena の API をそのまま使うことによる サンプル RDF の表明
String NS = "http://purl.org/dc/elements/1.1/";
OntModel m = createModel();
OntClass articleCls = m.createClass(NS +"Article");
Individual i = articleCls.createIndividual(
"http://www.ibm.com/developerworks/xml/library/j-jena/");
Property title = m.getProperty(NS + "title");
Literal l = m.createTypedLiteral("Introduction to Jena");
i.setPropertyValue(title,l);
Property creator = m.getProperty(NS + "creator");
l = m.createTypedLiteral("Philip McCarthy");
i.setPropertyValue(creator,l);
Property subject = m.getProperty(NS + "subject");
l = m.createTypedLiteral("jena, rdf, java, semantic web");
i.setPropertyValue(subject,l);
m.write(System.out, "N3");
|
リスト 2 のそれぞれの値の表明には、以下の動作を行うための 3 行が必要です。
- プロパティーへのアクセス
- 型付きリテラルの作成
- プロパティーの関係の表明
このコードの利点は、わかりやすくて明確な方法で RDF の概念に直接対応していることです。これは JDBC クライアント・コードに見られる状況と似通っており、API がリレーショナル・モデルに直接適用されています。
Jena を長い間扱っていると、通常のオブジェクト指向コードとして見なされるコードと、Jena API を利用するクライアント・コードとの間にギャップがあるという実感を持ち始めることでしょう。プロパティーを設定しているのではなく、グラフの縁取りを表明しているというギャップです。
リスト 3 に、Jenabean で作成した場合の表明を記載します。
リスト 3. Jenabean で作成した場合の表明
Model m = _
Bean2RDF writer = new Bean2RDF(m);
Article article = new Article("http://www.ibm.com/developerworks/xml/library/j-jena/");
article.setCreator("Philip McCarthy");
article.setTitle("Introduction to Jena");
article.setSubject("jena, rdf, java, semantic web");
writer.save(article);
m.write(System.out, "N3");
|
m.write(...,"N3") は、読者の励みになるように組み込んだ行です。この記事に記載したコードを自分自身で試してみてください (「ダウンロード」を参照)。
リスト 2 とリスト 3 は大きく異なりますが、結果的に Jena モデルに対して行われる変更はほとんど同じです。この 2 つのリストはどちらも、リスト 1 のトリプルのスーパーセットを作成します。リスト 2 では m.createClass(...) を呼び出して新しい記事を宣言している一方、リスト 3 では同じ結果を実現する手段として Article クラスの新規インスタンス、new Article(...) を作成します。Jenabean を使用しない場合、プロパティーを表明するたびに、モデルからプロパティーにアクセスし、リテラルを作成し、そして setPropertyValue(...) を呼び出して新しいステートメントをモデルに表明しなければなりません。しかし Jenabean の Bean2RDF コンバーターを使えば、JavaBean のセッター・メソッドを呼び出すだけで済みます。その結果、リスト 3 はほとんどの Java 開発者にとって見慣れたコードとなっています。
OOP と RDF とのバインディング
Jenabean の最大のメリットは、今見てきたように Java オブジェクトと RDF との間のギャップを埋めるところにあります。そのため、ドメイン・モデルで使用する Bean を使ったお馴染みのオブジェクト指向プログラミング (OOP) のスタイルでセマンティック Web アプリケーションをコーディングすることが可能になるというわけです。ただし、Java オブジェクトと RDF がまったく同じだと言っているわけではありません。オブジェクトと RDF はそれぞれに異なる方法でデータを表現します。そこで、優れたバインディング・ツールが解決しなければならない問題には以下の 3 つがあります。
オブジェクトと RDF のインピーダンス・ミスマッチ
開発者が Java オブジェクトのセットとしてのオントロジーを複製することだけに熱心になることは珍しくありませんが、Java オブジェクトと OWL クラスの 1 対 1 の相関関係は実際には存在しません。OWL は多重継承を許可し、多数のクラスに同じプロパティーを共有させ、さらにプロパティー同士が互いを継承し合えるようにします。繰り返しますが、リレーショナル・データベース管理システム (RDBMS) の場合と同様、オブジェクトと RDF はある点では似ていますが、その他の点では異なります。したがって、Java コード内から RDF を作成して使用しなければならないことには変わりありません。多くのツールで取っているコード生成の手法には、RDF スキーマまたは OWL オントロジーを読み取って、クラスとプロパティーを Java 言語で長々と複製する必要が伴います。このようなツールでは、既存の JavaBean クラスから共通の RDF プロパティーを表明することはできません。RDF バインディング・ツールは、(型が互換するという前提で) JavaBean プロパティーを RDF プロパティーの URI に任意に関連付けられるものでなければなりません。
シャロー・コピーとディープ・コピー
あらゆるバインディング・ツールが解決しなければならないもう 1 つの問題は、オブジェクト・グラフのどれくらいの部分を存続させるかです。オブジェクトを凝縮して保存すると、密に関係付けられたオブジェクト・グラフによってモデル全体が存続することになりかねません。ロードに関しても同様の問題があります。バインディング・ツールは、RDF モデル全体が JavaBeans としてメモリーにロードされないようにすると同時に、これを機能として必要に応じて実行できるようにする必要があります。
循環
循環関係は、RDF でもオブジェクト・モデルでもよくある事態です。そのため、バインディング・ツールは永続化およびロードを行う際に循環を検出して対処可能でなければなりません。当然、無限ループを防ぐこと、そして前にロードされたオブジェクトを検出して再使用することによって時間を短縮することも必要です。
Jenabean は、プロセスをできるだけ単純に維持しながら以上の 3 つの問題を解決します。Jenabean によるプロセスには、コード生成フェーズやバイトコードの生成ステップ、あるいはランタイムのバイトコード操作ライブラリーも必要ありません。唯一依存するのは、Jena とそのライブラリーだけです。Jenabean は保守的なポリシーにデフォルト設定されており、オブジェクトをその直接的で特異なプロパティーと一緒に保存します。オブジェクトとそれに関連するすべてのものを保存するには、意図的に「ディープ」コピーを必要としていることを開発者が示されなければなりません。あるいは、インスタンスの特定のコレクション・プロパティーを保存またはロードするように指定することもできます。
RDF に対応した JavaBeans を作成する方法
 |
Jenabean で特に重要なアノテーション
@Id
は、Jenabean が JavaBean インスタンスごとに固有の URI を作成する際に使われます。このアノテーションは、固有の int または String を返すゲッター・メソッドに配置してください。
@Namespace
はクラス・レベルで適用されます。このアノテーションを使用するとデフォルトの振る舞いが無効にされ、使用したい名前空間を定義できるようになります。値は、/ または # で終わる有効な URI でなければなりません。
@RdfProperty
は、JavaBean プロパティーを任意の RDF プロパティーにバインドします。それには、ゲッター・メソッドにアノテーションを付け、そのアノテーションの引数として RDF プロパティーの URI を指定するだけです。
|
|
Hibernate やその他のバインディング・ツールを使い慣れている方は、どこで魔法が起こるのか疑問に思っていることでしょう。Jenabean は Java アノテーションを使用して Bean がどのように RDF にマッピングされるかを宣言します。アノテーションをベースとした他のバインディング層と同じく、Jenabean が特に役立つのはモデルが Java オブジェクトを基に制御されている場合です。もちろん常にそうだとは限りませんが、これに該当する場合は Jenabean が活躍します。
極めて単純な動作サンプル
Jenabean には Bean を RDF としてシリアライズする方法をカスタマイズするための機能が数多くありますが、デフォルト設定に満足なら、すぐに Bean の作成、読み取りを開始できます。まずは、必要なすべての要件を満たす単純な JavaBean サンプルを作成してみましょう。JPA (Java Persistence API) や Hibernate の場合と同じように、オブジェクトは確実に固有の ID を持っていなければなりません。Jenabean では単一のアノテーション、@Id を少なくとも 1 つの Bean フィールドに追加する必要があります。これが、固有 ID の役割を果たします。リスト 4 に、この単純な Bean を記載します。
リスト 4. 保存できる状態の単純な Bean
package example;
import thewebsemantic.Id;
public class Person {
private String email;
@Id
public String getEmail() { return email;}
public void setEmail(String email) { this.email = email;}
}
|
リスト 4 は、Jenabean がPerson クラスのインスタンスを確実に保存し、ロードするのに十分な情報を提供します。ここで何かを拡張したり、XML 記述子ファイルを作成したりする必要はありません。E メール・アドレスは固有であるため、有効な ID となります。リスト 5 に、Person インスタンスを Jena モデルに保存する方法を示します。
リスト 5. Person クラスのインスタンスを保存する方法と、その結果の RDF
Model m = ModelFactory.createOntologyModel();
Bean2RDF writer = new Bean2RDF(m);
Person p = new Person();
p.setEmail("person@example.com");
writer.save(p);
m.write(System.out, "N3");
...
<http://example/Person>
a owl:Class ;
<http://thewebsemantic.com/javaclass>
"example.Person" .
<http://example/Person/taylor_cowan@yahoo.com>
a <http://example/Person> ;
<http://example/email>
"taylor_cowan@yahoo.com"^^xsd:string .
|
Bean2RDF はオブジェクトを RDF として作成する Jenabean クラスです。このクラスはデフォルトでシャロー・モードに設定されます。つまり、インスタンスとその特異なプロパティーが保存されるということです。Person クラスがモデルにまだ追加されていない場合は、この新しいクラスは owl:Class のインスタンスとして表明されます。リスト 5 で、Jenabean が example パッケージを新しいオントロジー・クラスの名前空間として使用していることに注目してください。2 番目の表明は、それぞれのインスタンスを作成するために使用された Java クラスを示すアノテーションです。Person インスタンスも、その E メール・アドレスと併せて表明されています。Jenabean は保存されたインスタンスの URI を最初に作成して表明を支援するだけでなく、E メール・プロパティーにも留意して、この E メール・プロパティーを string リテラル値として表明します。
Jena モデルから JavaBeans を取得する
 |
Java の型マッピング
Jenabean はすべてのプリミティブ型とそのラッパーに対し、Jena の Java と RDF とでの型マッピングのデフォルトを継承します。さらに Jenabean には java.util.Date に対する追加サポートがあり、これを xsd:dateTime にマッピングします。配列のプロパティーは rdf:Seq にマッピングされますが、多重度を表現するのにもっとも自然な形は java.util.Collection<?> インターフェースを使うことです。この場合、例えば Person クラスに多数の Appointment があるとすると java.util.Collection<Appointment> 型のプロパティーを指定することになります。Jenabean は List や ArrayList などといった java.util.Collection に固有の実装をサポートしません。その理由は、RDF が順序を保証しないためです。もちろん、他の Bean のプロパティーを使用することも可能で、その場合には単一のインスタンス、配列、あるいはコレクションのいずれかとなります。
|
|
RDF で表現される個々の要素には URI が必要ですが、その一方、Java 開発者は固有の ID という点で考える傾向があります。Jenabean は、名前空間 (この場合、パッケージおよびクラス名からデフォルト設定されたもの) に宣言済み ID フィールドを付加することで Java 開発者を支援します。この名前空間が作成されれば、以下のように RDF2Bean を使用してモデルから情報をロードすることができます。
RDF2Bean reader = new RDF2Bean(m);
Person p2 = reader.load(
Person.class,"person@example.com"); |
Jenabean は Person のすべてのインスタンスもロードします。
Collection<Person> people = reader.load(Person.class); |
上記は、モデル内の Bean にアクセスする最も簡単な方法です。Jenabean はまた、SPARQL (SPARQL Query Language for RDF) の結果とのバインディングもサポートします。要するに Jenabean の場合、Bean 作成者に最低限必要とされるのは、該当する型の全インスタンスに固有の値を保持するフィールドを示すことです。Bean のクラスとプロパティーには、保存時にそのクラスのパッケージおよび名前に基づくデフォルト URI が指定されます。そのため、厄介な作業もなく Java 層からすぐに RDF を作成し始められるというわけです。
名前空間とプロパティーを指定する
これまで Jenabean が Bean のクラスパスと名前に基づいてデフォルト URI を作成する方法を説明してきましたが、Jenabean には使用する名前空間を自分で選んで宣言するためのサポートも備わっています。特定の名前空間に Bean をマッピングするには @Namespace アノテーションを使用します。ここでは説明のために、まだ使用されていないことがわかっている名前空間として Jenabean 固有のプロジェクト URL を使用します。
@Namespace("http://jenabean.googlecode.com/")
public class Person { _
<http://jenabean.googlecode.com/Person/person@example.com>
a <http://jenabean.googlecode.com/Person> ;
|
デフォルトのパッケージの代わりに、Person クラスとそのプロパティーには新しい名前空間が指定されていることに注目してください。この名前空間は、@Namespace アノテーションの引数として指定した名前空間と一致するものです。この新しい名前空間が、クラスとそのプロパティーにデフォルトで使用されることになります。
RDF の世界では、一般的なプロパティーを使用することが推奨されています。一般的なプロパティーを使わなければ、セマンティック Web は決して発展しません。よく知られたプロパティーを使うことにより、データがよりセマンティックになり、他の人々にも理解しやすくなります。スパイダー (Web 巡回の変形) が、上記で Jenabean プロジェクトの URL 名前空間から生成した RDF スニペットに遭遇したとしても、それを利用することはできないはずです。その反面、より一般的でよく知られた表明によって多少、Bean を変更することはできます。人々のつながりを作る一般的な語彙である FOAF (Friend of a Friend) 言語 (「参考文献」を参照) には、E メール・アドレスの特殊なプロパティー、foaf:mbox があります。ここで必要となるのは、Person Bean で @RdfProperty アノテーションを使うことだけです。
@Id
@RdfProperty("http://xmlns.com/foaf/0.1/mbox")
public String getEmail() { return email;}
<http://xmlns.com/foaf/0.1/mbox>
"person@example.com"^^xsd:string .
|
これで email プロパティーは foaf:mbox として自己表明したため、このデータに興味を持つ他の RDF エージェントがこのプロパティーを E メール・アドレスとして認識するようになります。
オブジェクトの関係
OWL と RDF の世界では、さまざまなカーディナリティーの関係を表現する際に、同じプロパティーを繰り返し表明するという方法が取られますが、Jenabean は java.util.Collection インターフェースを使うことによって、このような関係の表現を非常に簡単なものにします。リスト 6 は、friends を (foaf:knows を緩くしたような方法で) サポートするように拡張した Person クラスです。
リスト 6. 友人関係をサポートするように拡張した Person
public Collection<Person> friends = new
LinkedList<Person>();
@RdfProperty("http://xmlns.com/foaf/0.1/knows")
public Collection<Person> getFriends() { return friends;}
public void setFriends(Collection<Person> friends) { this.friends = friends;}
|
上記は何も手の込んだことをしていません。Collection<Person> 型の friends という新規フィールドに get メソッドと set メソッドを関連付けているだけです。これで、Person と複数の friends を作成して、各友人を friends コレクションに追加すれば、この Person と複数の friends を関連付けることができます。@RdfProperty アノテーションはオプションですが、既存のサードパーティーの語彙にバインドする場合には重要になります。このアノテーションが、「友人」プロパティーを Jena モデルに含まれる foaf:knows RDF プロパティーにバインドするように指定するからです。リスト 7 では、従来の JavaBean 手法で友人関係を作成しています。
リスト 7. 友人関係を表現する方法と、その結果の RDF
Model m = ModelFactory.createOntologyModel();
Bean2RDF writer = new Bean2RDF(m);
Person p = new Person();
p.setEmail("person@example.com");
Person f1 = new Person();
f1.setEmail("friend1@example.com");
Person f2 = new Person();
f2.setEmail("friend2@example.com");
p.getFriends().add(f1);
p.getFriends().add(f2);
writer.save(p); // modifies the Jena model
m.write(System.out, "N3");
...
foaf:knows
jb:friend2@example.com, jb@friend1@example.com .
|
この単純な Person Bean が Bean2RDF.write(...) で保存されると、モデルには FOAF 仕様に準拠した新規データが含まれることになります。
順序付きリストを取得する
RDF では子ノードの順序付けは定義されていないので、Jena が何らかの特定の順序で友人のリストを取得すると見なすことはできません。順序付きリストが必要な場合、Jenabean は Java 配列を RDF シーケンスにマッピングします。この方法を説明するため、Person クラスに String の配列として表現した一連の電話番号を設定します (リスト 8 を参照)。
リスト 8. 配列プロパティーの例と、その結果の RDF
private String[] phoneNumbers;
public String[] getPhoneNumbers() { return phoneNumbers;}
public void setPhoneNumbers(String[] phoneNumbers) {
this.phoneNumbers = phoneNumbers;}
jb:phoneNumbers
[ a rdf:Seq ;
rdf:_1 "321-321-3210"^^xsd:string ;
rdf:_2 "321-321-3211"^^xsd:string
] ;
|
まとめ
Jenabean は、RDF を使い慣れた JavaBean モデルで読み取ったり、作成したりできるようにします。この記事ではその基本的な方法として、単純な Bean を作成して保存し、再び読み取る手順を説明しました。この記事で説明した内容は Java 言語でセマンティック Web アプリケーションを作成するという全体像のほんの一部にすぎません。さらに学ぶには、Jenabean プロジェクトのホーム・ページにアクセスしてください (「参考文献」を参照)。このホーム・ページではフィードバックを報告したり、ヘルプを求めたりすることもできます。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Code samples for this article | j-jenbean.zip | 4KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | |  | Taylor Cowan は Travelocity のアーキテクトです。その経歴全体をとおして、変換ワークフロー、UI 技術、そして最近ではセマンティック Web (RDF/OWL) というコンテキストで XML と Java コードに取り組んでいます。彼はオープンソースの プロジェクト、Jenabean のコミッターです。 |
記事の評価
|