序文
アプリケーションが情報のさまざまな部分を保存することは至って普通のことで、リレーショナルデータベースでは、ほとんどの場合がそうです。いつものデータタイプで作業をしているときはいい仕事をしますが、バイナリデータ、たとえば、画像やドキュメントを扱うときは非常に効率が悪くなります。代わりにファイルシステムを利用することができ、性能も優れているのですが、情報検索のためのクエリー言語や、リレーションシップまたはトランザクションという概念は存在しません。
.第三者が格納データにアクセスできるようにすることは(アプリケーションが成長すると生じる典型的な要件)、多くの場合、一晩で終わってしまうことのない長くて複雑な処理です。記憶機構の内部構造は、情報を横断し検索するAPIアーキテクチャやその方法に影響を及ぼしやすくなります。
JSR-170とは?
非常に都合のいいことに、JSR-170(サイト・英語)は、Javaコンテントリポジトリ(JCR)としても知られ、処理系非依存の方法でこれらの問題(とその他)への対処を図っています。すなわち、APIは、基本リソース(たとえば、データベース、ローカルあるいは仮想ファイルシステム)とは無関係な同一のものであると思われます。データ記憶のトップに位置するJCRは、細かなアクセス制御やバージョニング、コンテンツイベント、全文検索、とりわけフィルタリングなどのコンテントサービスを提供しています。Day Softwareで導入されたJSR-170の陰にいる素晴らしい専門家グループによる仕様が、Vignetteのようなコンテント管理システム(CMS)販売業者や、Hummingbird Ltd.、Stellent、BEA SystemやIBM、Oracleのような通常のJava方式ソリューションプロバイダを含めた、コンテント管理やドキュメント格納に対する事実上の業界標準になる可能性があります。
2006.ほぼ2年半の作業の後、2005年の6月に完成したAPIには、2006年の初期にリリースされたリファレンス実装(JackRabbit)(サイト・英語)の初の1.0バージョンによるjavax.jcrパッケージを基本にした約50個のクラス(主にインターフェースと例外)があります。
JSR-170概要
Javaコンテントリポジトリは、(ものが保管される場所であるという普通の意味(source)に加えて)データを扱う作業のためにいくつかの機能を提供する保存場所という考え方に基づいています。保存場所には、右図に示されているように、ノードとプロパティで作り上げられた"ツリー構造"で情報が保存されます。円はノードを示し、一方、正方形はプロパティを示します。ノードは、唯一の親と、子(下位ノード)とプロパティをいくらでも持つことができます。プロパティは、唯一の親(これはノード)だけで子はなく、論理、データ、ダブル、ロング、ストリング、ストリーム型の内の1つ以上の値と1つの名前から成っています。プロパティだけが情報ストレージに利用され、一方、ノードは、ツリー内部の’パス’作成のためのものです。ある程度、このツリーは、ノードがディレクトリでプロパティが実際のファイルであるファイルシステム構造に似ています。
リポジトリ機能は、いくつかの’順守レベル’にわけられ、それぞれ一連の特別な特徴があります。
-
レベル 1
レベル1は、すべてのインプリメンテーションには必須であり、保存場所への読み出しアクセスを提供します。レベル1での要点を下記に示します。- ノードとプロパティへの読み出しアクセス
- プロパティ値への読み出しアクセス
- XML/SAXへのエクスポート
- XPATH構造による問い合わせサービス
-
レベル 2
レベル2は、以下に示す書き込み機能を提供しています。- ノードとプロパティの追加と削除
- プロパティ値への書き込みアクセス
- XML/SAXからのインポート
JCRインプリメンテーションは、レベル2に適合する必要はなく、仕様の域を越えてしまうことに留意ください。従って、読み出し専用の保存場所で作業するのが極めて効果的です。
-
オプション
‘オプション’レベルには、リードライト保存場所からは必要とされませんが、JSR-170に実際に値を追加するなど、いくつかの高度な機能があります。このレベルには(とりわけ)以下に示すものが含まれています。- トランザクション - これは、JMSあるいはJDBCリソースに沿ったリソースとしての保存場所を確保できるようにします。
- バージョニング - この保存場所に、あとで検索されるノードのさまざまな状態の記録ができるようにすることです。その仕様は、ここの話題ぐらいの長さになります。JSR-170バックエンドでCVCクローンを構築するのは可能です。
- イベント - 保存場所の内部で何かが起きたことをクライアントに知らせることができるオブザベーションとしても知られています。
- ロッキング - ツリーの一部をフリーズさせることが可能な機能で、効率よくサブツリーを読み出し専用として提供します。
APIの検討
JSR-170を扱う場合、なにもコード変更する必要もなしに、JCRインプリメンテーションに簡単に切り替えができるようにするため、javax.jcrパッケージのインターフェイスで作業することが推奨されます。
このAPIの主要クラスは、クライアントと保存場所の間の接続を表し、実行中のワークスペース名とcredentialssuppliedで定義されているセッションです。読み込み(レベル1)と書き込み(レベル2)の両方を目的としたSessioncontainsメソッドで、基本保存場所でサポートされていない機能を利用すると、例外処理されます。
パッケージには、保存場所を構成しているユニットを定義しているインターフェイスもあります。ワークスペースやクレデンシャル、ノード、プロパティ、アイテム(ノードやプロパティのためのスーパークラス)、値などです。クエリーは、javax.jcr.queryパッケージを通して扱われ、ノードタイプの定義は、javax.jcr.nodetypeを通して扱われますが、パッケージの残りの部分は、オプションのレベル機能を扱います。すなわち、javax.jcr.version、javax.jcr.observation、javax.jcr.lockです。ある興味深いパッケージにjavax.jcr.utilがあります。これには、『GoF(4人組)』による有名なデサインパターン(ご参考・英語)からのビジターパターンインターフェイスである、ItemVisitorのインプリメンテーションが含まれています。
JSR-170の実装
Google(サイト・英語)やSourceForge(サイト・英語)は、JSR-170実装プロジェクトページを記載していますが、それらのほとんどが、何のリリースもされていないアルファ版のものです。下記は、自由にダウンロードできるプロジェクトのリストで、プログラムの作者が利用していました。
- Jackrabbit(サイト・英語)
これは、JSR-170用のリファレンス実装であり、Apacheの基礎の一部で、レベル1と2、オプション機能を提供しています。書き込みのとき、このプロジェクトはインキュベーション過程を経て、製品利用するのに十分安定していると見なされ、正式に一般公開されます。さらに、Jackrabbitは、JSR-170の先駆者であるDay Softwareの商業用製品用の基本として利用されています。また、JSR-170で定義された特徴をすべて実行すると、JackRabbitは余分な機能(SessionListeners、あるいは、CustomNode登録のような)を追加して、JCA接続やtaglib、WebDAVインターフェイス、仮想ファイルシステムやJDBCバックエンドを含む、寄贈されたプロジェクトの興味深いパッケージソフトとなります。JackRabbitはApache2.0に基づいて許可されます。 - eXo JCR(サイト・英語)
これは、eXoプラットフォーム(source)の一部であり、仕様やいくつかのオプション仕様に要求される強制機能のすべてを含んでいます。最新リリース(1.0RC7)は2006年の6月22日に公開され、仕様の最終原案2に基づいています。eXo JCRは、MySQLやDB2またはHSQL(これはデフォルトである)などのJDBC対応データベースをバックエンドストレージとしてサポートし、二重の許可がされています(GPLと商業用)。このプロジェクトの最終リリースの日付は不明です。 - Jeceira(サイト・英語)
これは、JackrabbitやeXo JCRと比較して比較的新しいプロジェクトで、書き込みのとき、オプションレベルでの観測だけと、レベル1とレベル2の両方でのいくつかの要件を実行します。残念ながら、このプロジェクトは未完成の状態で、ここ9ヶ月、新しくリリースされたものはありません。ただし、JCR-170インプリメンテーションとしてのJackrabbitにほとんど近い、一般的なJavaベースのCMSであるMagnolia(サイト・英語)に利用されていました。全レベルを含めるように計画された最終1.0版のリリースの日付は、現在のところ不明です。Jeceiraは、Apache2.0に基づいて許可され、そのストレージエンジンとしてHSQLデータベースを利用しています。
JCRモジュール
Springモジュール(サイト・英語)の一部である、JCRモジュールの主な目的は、主要なSpringディストリビューションによるORMパッケージ開発と同じように、JSR-170APIでの開発を簡単にすることです。特長には以下に示すものが含まれます。
- JcrTemplate、これは、JcrCallbackhand例外処理の実行を許可します(検査済みのJCR例外を未検査のSpring DAO例外に変換します)。このテンプレートは、JCRセッションでのほとんどのメソッドを実行し、代替品として簡単に利用することができます。さらに、このテンプレートは、トランザクション用保存場所を利用する場合に、非常に実用的な機能である、いくつかのメソッドにわたって利用されるスレッド範囲セッションのことを認識しています。
- RepositoryFactoryBean、これは、保存場所のインスタンスを設定したり、始動したり、止めたりします。保存場所を設定すべき方法にJR-170が対処しない場合、この関連でインプレメンテーションが変化します。サポートには、おそらく他の保存場所をサポートできる抽象基本クラスとJeceiraやJackrabbit用の事前に定義されたFactoryBeansが含まれています。
- SessionFactory、これは、リポジトリや資格書、ワークスペースインターフェイスを一体化し、リスナや顧客ネームスペースを自動記録できるようにします。
- Springの宣言型トランザクションは、(オプションの)トランザクション機能を実行する保存場所に対応します。
- OpenSessionInViewインターセプタとフィルタ、これは、さまざまな要素にわたるスレッド対して、同じセッションが利用できるようにします。JcrTemplateに加えて、JCRセッションの検索や終了、管理が具体化され、全体的に呼び出し元に対してトランスペアレントになります。
この記事では、レファレンス実装(Jackrabbit)がいかに利用されようとも、JCRモジュールがjavax.jcrインターフェイスを利用しているため、インプリメンテーションを変更するのは、ただの設定の問題になります。Jackrabbitを管理しているJavaコンテントリポジトリの使用の仕方とSpringモジュールがどのように役立つかを、順を追って見てみましょう。
リポジトリとSessionFactoryの設定
<bean id="repository" class="org.springmodules.jcr.jackrabbit.RepositoryFactoryBean">
<!-- normal factory beans params -->
<property name="configuration" value="classpath:jackrabbit-repo.xml"/>
<property name="homeDir" ref="./tmp/repo"/>
</bean>
JCRサポートは、JackRabbit設定ファイルとホームディレクトリが必要なJackrabbitを設定するためのRepositoryFactoryBeanクラスを用意します。RepositoryFactoryBeanは、ローカルファイルシステムで作業している場合に有効であることに留意してください。保存場所がJNDI内部に登録されている可能性のあるサーバ環境の場合は、
<bean id="repository" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jcr/myRepository"/>
</bean>
あるいは、以下に示すようにSpring2.0のスキーマネームスペースを利用します。
<jndi:lookup id="entityManagerFactory" jndi-name="jcr/myRepository"/>
JCRでの作業を簡単にするために、このモジュールでは、以下に示すようにSessionFactroyインターフェイスを追加します。
public interface SessionFactory {
public Session getSession() throws RepositoryException;
public SessionHolder getSessionHolder(Session session);
}
SessionFactoryは、一度設定されると、同じ資格書を持つセッションが簡単に検索できるようにインプリメンテーション内部の認証の詳細を非表示にします。インプリメンテーション機能(仕様では対象とされていない)を活用するために、インターフェイスは、SessionHolderで検索が可能です。SessionHolderは、初期設定でトランザクションやセッション管理に利用されるJCRモジュールの特定クラスで、すべてのJCRインプリメンテーションに役立つ一般的なインプリメンテーションですが、オプション機能やカスタマイズされた機能(Jackrabbitのトランザクション[GLOVA1]インフラストラクチャをサポートするJackrabbitSessionHolderなど)をサポートしていません。JCRモジュールには、他のJSR-170対応ライブラリに対するサポートの組み込みを簡単にするSessionHolderインプリメンテーション(後でさらに詳しく検討します)を見つけ出す、容易でトランスペアレントな方法が備えられています。
SessionFactoryの既定インプリメンテーションは、不利に作用する保存場所と資格書を必要とする JcrSessionFactoryです。
<!-— SessionFactory -->
<bean id="jcrSessionFactory" class="org.springmodules.jcr.JcrSessionFactory">
<property name="repository" ref="repository"/>
<property name="credentials">
<bean class="javax.jcr.SimpleCredentials">
<constructor-arg index="0" value="bogus"/>
<!-- create the credentials using a bean factory -->
<constructor-arg index="1">
<bean factory-bean="password" factory-method="toCharArray"/>
</constructor-arg>
</bean>
</property>
</bean>
<!-- create the password to return it as a char[] -->
<bean id="password" class="java.lang.String">
<constructor-arg index="0" value="pass"/>
</bean>
Bean宣言は非常に単純です。SimpleCredentialコンストラクタに提供されるパスワードである単なる’catch’のことであり、文字列を受け取るだけで、対処するときは、Springファクトリ宣言が利用されていました。
JcrTemplate
JcrTemplateは、セッションの開始・終了や、トランザクションロールバック(基本保存場所で提供されている場合)、数ある機能の中でも例外処理を扱う必要のある呼び出し元を解放するJCRセッションで作業するために便利な方法を提供するときの、以下に示すJCRモジュールコアクラスの1つです。
<bean id="jcrTemplate" class="org.springmodules.jcr.JcrTemplate">
<property name="sessionFactory" ref="jcrSessionFactory"/>
<property name="allowCreate" value="true"/>
</bean>
この場合も前記と同様に、テンプレート定義は単純で、HibenateTemplateのようなSpringフレームワークのその他のテンプレートクラスに似ています。
例
保存場所を設定したので、Jackrabbitのウィキページの例の1つ(source)で以下のように"軽く試して"みましょう。
public Node importFile(final Node folderNode, final File file, final String mimeType,
final String encoding) {
return (Node) execute(new JcrCallback() {
/**
* @see org.springmodules.jcr.JcrCallback#doInJcr(javax.jcr.Session)
*/
public Object doInJcr(Session session) throws
RepositoryException, IOException {
JcrConstants jcrConstants = new JcrConstants(session);
//create the file node - see section 6.7.22.6 of the spec
Node fileNode = folderNode.addNode(file.getName(),
jcrConstants.getNT_FILE());
//create the mandatory child node - jcr:content
Node resNode = fileNode.addNode(jcrConstants.getJCR_CONTENT(),
jcrConstants.getNT_RESOURCE());
resNode.setProperty(jcrConstants.getJCR_MIMETYPE(), mimeType);
resNode.setProperty(jcrConstants.getJCR_ENCODING(), encoding);
resNode.setProperty(jcrConstants.getJCR_DATA(), new FileInputStream(file));
Calendar lastModified = Calendar.getInstance();
lastModified.setTimeInMillis (file.lastModified ());
resNode.setProperty(jcrConstants.getJCR_LASTMODIFIED(), lastModified);
session.save();
return resNode;
}
});
}
主な違いは、JCRテンプレートの内部にコードが重ねられていることで、このテンプレートは、try/catchブロック(例外処理が調べられたIOやリポジトリのため)を利用する必要があることや、セッション(トランザクションもあればこれも)整理の処置をすることから解放してくれます。JCR定数を処理する間違いのない方法を用意し、ネームスペースの接頭辞の変化を認識するJcrConstantsのユーティリティクラスによって、"jcr:データ"のようなハードコードストリングが解読されることは、言及される価値があります。ご覧のように、その例をより堅牢にして、実際のビジネスコードへの影響をわずかなものにして見ました。
トランザクションサポート
JCRモジュールの長所の1つは、JavaコンテントリポジトリでSpringトランザクションインフラストラクチャ(どちらも宣言しプログラムで)を利用する能力があることです。JSR170は、オプション機能としてトランザクションサポートを取り扱い、トランザクションフックを公開する一般的な方法を強要しないため、各インプリメンテーションは、異なるメソッドを選択することができます。書き込みするときは、Jackrabbitだけがトランザクション(作業のほとんどで)をサポートすることが知られていて、各JcrSessionに対してjavax.transaction.XAResourceを公開することでそれを実行します。JCRモジュールには、下記に示すローカルトランザクションに利用できるLocalTransactionManagerが提供されています。
<bean id="jcrTransactionManager" class="org.springmodules.jcr.jackrabbit.LocalTransactionManager">
<property name="sessionFactory" ref="jcrSessionFactory"/>
</bean>
宣言型のトランザクション区分に対して、前記に宣言されたトランザクション管理Beanに加え、以下に示す標準のSpringクラスが利用されています。
<!-- transaction proxy for Jcr services/facades -->
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="transactionManager" ref="jcrTransactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED, readOnly</prop>
</props>
</property>
</bean>
<bean id="jcrService" parent="txProxyTemplate">
<property name="target">
<bean class="org.springmodules.examples.jcr.JcrService">
<property name="template" ref="jcrTemplate"/>
</bean>
</property>
</bean>
JTAマネージャが必要ならば、単純ですが素晴らしいソリューションは、Jackrabbitの寄贈パッケージでのjcaコネクタを利用することです。Jencks(サイト・英語)のような組み込みが可能なJCAコンテナを利用する場合、必ずしもそのためのアプリケーションサーバは必要ありません。JCAコネクタ設定は、この記事の範囲ではありませんが、JCRモジュール例の中にJencksを使った例を見ることができます。
TransactionAwareRepository
単純なJCRコードが必要とされるアプリケーションでは、JCRモジュールによって直接JCR APIを利用しているコードで、トランスペアレントなトランザクション方式セッションの利用が可能になります。1つ見つかると、JcrSessionFactoryで定義されたパラメータを受け取ったSession.login()で作成されたあらゆる新しいセッションが、スレッド範囲セッションを返すために、パラメータとしてJcrSessionFactoryを利用しているTransactionAwareRepositoryを使用することができます。トランザクションを利用すれば、JCRセッションがトランザクション的になり、allowNonTxRepositoryプロパティを手入力で真に設定する必要があるとまでは行かないまでも、下記の設定などの場合は、そうしないと例外が発生します。
<bean id="transactionRepository" class="org.springmodules.jcr.TransactionAwareRepository">
<property name="allowNonTxRepository" value="true"/>
<property name="targetFactory" ref="jcrSessionFactory"/>
</bean>
transactionRepository Beanは、あらゆる基本メカニズム、あるいは、スレッド範囲セッションを認識することなしに、トランザクション的であろうがなかろうが、単純なJCR リポジトリとして利用されます(1つしかなければ、セッションを閉じてトランザクションに任せることができます)。
オプション機能であるサポート検出
まだ、さまざまなJCRインプレメンテーションのためのトランザクションサポートのような組み込みが可能なオプション機能は許されていますが、最大限にコードを再利用するため、JCRモジュールは、SessionHolderProviderとSessionHolderProviderManagerインターフェイスに加えて(すでに述べた)SessionHolderインターフェイスを利用しています。通常、ユーザは、インターフェイスがフレームワークに内存していればこれらと情報のやり取りをする必要はありません。ただし、これらには、JCRモジュールに対する主要な拡張のポイントが説明されています。
SessionHolderクラスは、内部でさまざまな要素、主にセッションを扱うトランザクション管理に利用され、一方、 SessionHolderProviderとSessionHolderProviderManagerは、sessionHoldersを作成する方法と、プロバイダそれぞれの利用のされ方を取り扱っています。既定値では、ServiceSessionHolderProviderManagerが利用され、これは、JDK1.3サービスプロバイダ(サイト・英語)メカニズムによる機能を自動的に見出します。このマネージャは、SessionHolderProviderインプリメンテーションの完全な限定名があるMETA-INF/services/org.springmodules.jcr.SessionHolderProvider入力用のクラスパスを検索します。Jackrabbitサポートは、このように設定され、JCRモジュールディストリビューションのjarには、以下のような、たった1行のMETA-INF/サービスファイルが含まれます。
org.springmodules.jcr.jackrabbit.support.JackRabbitSessionHolderProvider
既定値のSessionHolderProviderManagerは、JcrSessionFactoryによって内部で利用されるため、ファクトリが起動したとき、あらゆる特注のインプレメンテーションが取り出され、適切な保存場所で利用されます。ただし、JcrSessionFactoryのSessionHolderProviderManagerを設定することで、異なる検出方法に簡単に切り替えられます。既定のサービス検出の代替としては、 ListSessionHolderProviderManagerがあり、これは、顧客を考慮したプロバイダリスト簡易利用の実現(たとえば試験用など)を認めています。
<bean id="listProviderManager" class="org.springmodules.jcr.support.ListSessionHolderProviderManager">
<property name="providers">
<list>
<bean class="org.mycompany.jcr.CustomHolderProvider"/>
<bean class="org.springmodules.jcr.jackrabbit.support.JackRabbitSessionHolderProvider"/>
<bean class="org.springmodules.jcr.support.GenericHolderProvider"/>
</list>
</property
</bean>
<bean id="jcrSessionFactory" class="org.springmodules.jcr.JcrSessionFactory">
...
<property name="sessionHolderProviderManager" ref="listProviderManager"/>
</bean>
保存場所ごとにプロバイダは1つであるのが望ましいことに留意してください。リストに同じ保存場所で作業する複数のプロバイダがあれば、適合する最初のものが利用されるときに、その順番が重要になります。
Javaコンテントリポジトリの未来
JSR-170が2005年の5月に終わっていても、Javaコンテントリポジトリでの作業は終わりません。公式の後継である、JSR-283(サイト・英語)では、いま少し例を上げると、コンテントモデル化性能への拡張や、連携やリモーティング、クライアントサーバプロトコルマッピングなどの強化に焦点があてられます。また、JSR以外の考え方や構想があります。すなわち、JavaクラスをJCRツリーやその逆(ORMは、データベースの代わりにJavaコンテントリポジトリによって元に戻される)変換できる結合、マッピングのフレームワークや、JCR上に組み込まれるWebDAVサーバ(Jackrabbit寄贈パーケージを参照)などです。JSR-170コネクタは、2~3例を上げると、AlfrescoやBEAポータルサーバ、IBM Dominoなどのさまざまな製品に現れていました。
JCRモジュールに関しては、Spring2.0のネームスペーススキーマ用サポート(これは、構造XMLを減らす)である、それぞれのインプリメンテーションのためのAcegiとの安全な統合と、他のJCRインプレメンテーションとの統合がこの計画に含まれています。明らかに、JCRの未来が輝いて見えます。
原文はこちらです:http://www.infoq.com/articles/spring-modules-jcr
(このArticleは2007年3月12日にリリースされました)