現在、JavaBeans 仕様 (バージョン 1.0) には、JavaBeans の階層や論理構造を説明した規則も、JavaBeans が、内部で JavaBean のインスタンスを生成したランタイムを認識したり、そのランタイムから任意のサービスや機能を取得したりする際の規則も含まれていません。
望ましい方法は、論理的でトラバーサルな JavaBeans の階層を提供すると同時に、任意の JavaBean のインスタンスを生成するオブジェクトが従来の方法で JavaBean に多様なサービスを提供したり、基本的なシステムサービスと JavaBean 間への割り込みを行うための一般的なメカニズムを提供することです。
ほかのコンポーネントモデルでは、コンポーネントとその環境、またはコンテナ間の関係という概念が存在します。そこでは、新たにインスタンスを生成されたコンポーネントが、そのコンテナまたは組み込みコンテキストへの参照とともに提供されます。
コンテナまたは組み込みコンテキストは、階層または論理構造を確立するだけではなく、コンテキストが提供するサービスを判別して利用するためにコンポーネントが調査するサービスプロバイダとしても機能します。
ここでは、次の拡張可能なメカニズムをサポートするプロトコルを定義します。
BeanContext の階層構造およびその一般的な機能は、次のように提供されます。
public interface java.beans.beancontext.BeanContext extends java.beans.beancontext.BeanContextChild, java.util.Collection, java.beans.DesignMode, java.beans.Visibility { Object instantiateChild(String beanName) throws IOException, ClassNotFoundException; public InputStream getResourceAsStream(String name, BeanContextChild requestor ); public java.net.URL getResource(String name, BeanContextChild requestor ); void addBeanContextMembershipListener( BeanContextMembershipListener bcml ); void removeBeanContextMembershipListener{ BeanContextMembershipListener bcml ); public static final Object globalHierarchyLock; }
BeanContext のメンバーシップ内の変更通知は、次のようにモデル化されます。
public interface BeanContextMembershipListener extends java.util.Listener { void childrenAdded(BeanContextMembershipEvent bcme); void childrenRemoved(BeanContextMembershipEvent bcme); }
イベント関連のすべての BeanContext の基底クラスは、次のように定義されます。
public abstract class BeanContextEvent extends java.util.EventObject { public BeanContext getBeanContext(); public synchronized void setPropagatedFrom(BeanContext bc); public synchronized BeanContext getPropagatedFrom(); public synchronized boolean isPropagated() }
BeanContextMembershipEvent は、次のように定義されます。
public class BeanContextMembershipEvent extends BeanContextEvent { public BeanContextMembershipEvent(BeanContext bc, Object[] deltas); public BeanContextMembershipEvent(BeanContext bc, Collection deltas); public int size(); public boolean contains(Object child); public Object[] toArray(); public Iterator iterator(); }
BeanContext の役割の 1 つに、BeanContext および JavaBean インスタンスの階層的入れ子構造という概念の導入があります。この構造をモデル化するために、BeanContext は、構造または階層内の関係を定義する API を公開する必要があります。
BeanContext は、後述するように java.beans.beancontext.BeanContextChild インタフェースを実装することにより、そのスーパーストラクチャーを公開します。このインタフェースを利用すると、BeanContext の入れ子にされた BeanContext の検出および操作が可能になります。その結果、BeanContext の階層作成機能が導入されます。
BeanContext は、java.util.Collection インタフェースセマンティクスによりモデル化された多数のインタフェースメソッドを使って、そのサブストラクチャーを公開します
BeanContext は、すべての必須 Collection API を実装するために必要で、add() および remove() 用に次の特定のセマンティクスが使用されます。
add() メソッドは、新規 Object である BeanContextChild または BeanContext をターゲットの BeanContext 内で入れ子にするために呼び出されます。次のセマンティクスに従うためには、仕様に準拠して実装された add() が必要です。
このようにして、BeanContext はその子を監視し、子がサードパーティー (通常は別の BeanContext) によって削除された場合に setBeanContext() を呼び出して検出できるようになります。BeanContext は、その時点で子がメンバーではないことを判別すると、サードパーティーによる変更を拒否することができます。
remove() メソッドは、既存の子 JavaBean または BeanContext をターゲットの BeanContext から削除する場合に呼び出されます。次のセマンティクスに従うためには、仕様に準拠して実装された remove() が必要です。
結果として、targetChild が java.beans.beancontext.BeanContextChild インタフェース (または BeanContextProxy,、詳細は後述) を実装する場合、BeanContext は setBeanContext() を null1 BeanContext 値で呼び出して、BeanContext 内の入れ子ではなくなった子を通知します。
特定の BeanContextChild は、入れ子になった BeanContext から入れ子状態を解除できない場合、PropertyVetoException をスローします。受け取った BeanContext は、このインスタンスの削除操作を呼び出して、IllegalStateException をスローします。無限回帰を避けるため、子にはその後の削除通知を繰り返し取り消すことは許可されません。実際、子は、入れ子になった現在の BeanContext からその削除を無効にする条件 (一時的な場合) の解決を試みる必要があります。
入れ子になった BeanContext のすべての子の寿命は、少なくとも、指定された BeanContext 内での子の包含関係の継続期間になります。BeanContext 内の包含関係を意識しない単純な JavaBeans の場合、これは、入れ子になった BeanContext の有効期間中は少なくとも JavaBean が存在することを意味します。
BeanContext では、java.util.Collection API によって定義された、オプションの addAll()、removeAll()、retainAll()、または clear() メソッドのいずれかを実装する必要性はありませんが、実装する場合には、前述の add() と remove() 用に定義されたセマンティクスをオブジェクトごとに実装する必要があります。失敗時には、これらのメソッドは部分的に適用されたすべての変更を無効にして、複合操作が呼び出されて失敗する前の状態に BeanContext を戻します。失敗時には、上記の add() および remove() の定義に合わせて、BeanContextEvents はトリガーされません。
BeanContextMembershipListeners の追加および削除は、addBeanContextMembershipListener() および removeBeanContextMembershipListener() の呼び出しを通して実行されます。
toArray() メソッドは、現在の JavaBean セットのコピーまたはターゲットの BeanContext 内で入れ子になった BeanContext のインスタンスを返します。また、iterator() メソッドは、子の同じセットに java.util.Iterator オブジェクトを提供します。
指定されたオブジェクトが現在 BeanContext の子である場合、contains() メソッドは true を返します。
size() メソッドは、入れ子になった現在の子の数を返します。
isEmpty() メソッドは、BeanContext に子がない場合、true を返します。
すべての Collection メソッドは、マルチスレッド環境で正しく機能するために、指定された実装による適切な同期化を相互に必要とします。これにより、指定された BeanContext 内で入れ子になった JavaBeans セットのメンバーシップへのすべての変更が、不可分で確実に適用されます。すべての実装で、BeanContext.globalHierarchyLock を使ってこれらのメソッドの実装を同期化する必要があります。
ある状況では、add() および remove() (またはその変化形) 操作が入れ子状態で行われる場合があります。その場合、Thread を同時に呼び出すスタック上で複数の操作が同時に発生します。次に例を示します。BeanContextChild の A が追加 (または削除) されると、setBeanContext() メソッドは別の BeanContextChild の B も追加 (または削除) します。特定の BeanContext 実装は、2 つの BeanContextMembershipListener 通知を発行することがあります。その場合、B の add()/remove() 操作に対して 1 つ発行し、その後 A の操作に対して 1 つ発行する (B が A の前に追加されたためにこの順序になる) か、または A と B の両方を含む 1 つの通知にまとめます。どのような理由でも A を追加または削除できない場合には、この条件を示す PropertyVetoException をスローする前に、副作用として発生する B に対するすべての追加または削除操作の実行または取り消しは行われません。つまり、独自のメンバーシップのステータスに対する変更を拒否する前に、副作用として発生するメンバーシップの変更は避けるかまたは取り消す必要があります。
instantiateChild() メソッドは、新たな JavaBean インスタンスを目的の BeanContext の子としてインスタンス生成する際に呼び出すことのできる簡易メソッドです。JavaBean の実装は、beanName の実パラメータの値から導かれ、java.beans.Beans.instantiate() メソッドによって定義されます。
一般に、これは目的の BeanContext の ClassLoader を使用して、適切な java.beans.Beans.instantiate() メソッドを呼び出すことにより実装されます。ただし、特定の BeanContext 実装は、このメソッドの実装内のインスタンス生成操作に対する副作用を引き起こす場合があります。
BeanContextEvent は、BeanContext の定義済みセマンティクスの状態変化に属するすべてのイベントに対する抽象ルート EventObject クラスです。この抽象ルートクラスは、通知元である BeanContext を定義し、BeanContexts の階層を通じて BeanContextEvent サブクラスの伝播を可能にするメカニズムを導入します。setPropagatedFrom() および getPropagatedFrom() メソッドを使用すると、BeanContext は伝播されるイベントのソースとして自らを識別するため、イベントは BeanContext に伝播されます。これは一般的な伝播メカニズムであり、大規模な階層を経由して伝播が行われる場合、パフォーマンス面で重要な影響があるため、注意して使用する必要があります。
BeanContextMembershipEvent は、特定の BeanContext インスタンスのメンバーシップ内で発生する変更を記述します。このイベントは、特定の BeanContext インスタンスのメンバーシップ (すなわち、メンバーシップセット内のデルタ) に対して、追加または削除された子のリストをカプセル化します。
特定の BeanContext インスタンスに対して呼び出された add()、remove()、addAll()、retainAll()、removeAll()、または clear() が成功する場合はいつでも、BeanContextMembershipEvent がトリガーされてこの操作による影響を受ける子を記述します。
BeanContext は、getResourceAsStream() および getResource() という 2 つのメソッドを定義します。これらのメソッドは、java.lang.ClassLoader 上のメソッドに類似しています。BeanContext 内で入れ子になった BeanContextChild インスタンスは、入れ子構造の BeanContext に対するメソッドを、ClassLoader のメソッドに優先して呼び出します。このため、子と基本的な ClassLoader セマンティクス間に動作を割り込ませることにより、BeanContext 実装からセマンティクスを拡張することができます。
BeanContext のサービス機能は、次のようにして提供されます。
public interface BeanContextServices extends BeanContext,BeanContextServicesListener { boolean addService(Class serviceClass, BeanContextServiceProvider service); boolean revokeService(Class serviceClass, BeanContextServiceProvider bcsp, boolean revokeNow ); boolean hasService(Class serviceClass); Object getService(BeanContextChild bcc, Object requestor. Class serviceClass, Object serviceSelector, BeanContextServicesRevokedListener sl ) throws TooManyListenersException; void releaseService(BeanContextChild bcc, Object requestor, Object service); Iterator getCurrentServiceClasses(); public Iterator getCurrentServiceSelectors(Class sc); addBeanContextServicesListener( BeanContextServicesListener bcsl ); removeBeanContextServicesListener( BeanContextServicesListener bcsl ); }
BeanContextServiceProvider インタフェースは、次のように定義されます。
public interface BeanContextServiceProvider { Object getService(BeanContext bc, Object requestor, Class serviceCls, Object serviceSelector); void releaseService(BeanContext bc, Object requestor, Object service); Iterator getCurrentServiceSelectors(BeanContext bc, Class serviceCls); } The BeanContextServiceRevokedListener is defined as follows: public interface BeanContextServiceRevokedListener extends java.util.EventListener { void serviceRevoked( BeanContextServiceRevokedEvent bcsre ); }
BeanContextServicesListener は、次のように定義されます。
public interface BeanContextServicesListener extends BeanContextServiceRevokedListener { void serviceAvailable( BeanContextServiceAvailableEvent bcsae ); }
BeanContextServiceAvailableEvent は、次のように定義されます。
public class BeanContextServiceAvailableEvent extends BeanContextEvent { public BeanContextServiceAvailableEvent( BeanContextServices bcs, Class sc ); BeanContextServices getSourceAsBeanContextServices(); public Class getServiceClass(); public boolean isServiceClass(Class serviceClass); public Iterator getCurrentServiceSelectors(); }
BeanContextServiceRevokedEvent は、次のように定義されます。
public class BeanContextServiceRevokedEvent extends BeanContextEvent { public BeanContextServiceRevokedEvent( BeanContextServices bcs, Class sc, boolean invalidNow ); public BeanContextServices getSourceAsBeanContextServices(); public Class getServiceClass(); public boolean isServiceClass(Class service); public boolean isCurrentServiceInvalidNow(); }
BeanContextServiceProviderBeanInfo は、次のように定義されます。
public interface BeanContextServicesProviderBeanInfo extends java.beans.BeanInfo { java.beans.BeanInfo[] getServicesBeanInfo(); }
構造化された階層を提供することを除く、BeanContext の主要な役割は、JavaBean コンポーネントがコンテキスト固有の機能またはサービスを環境から取得するための標準的なメカニズムを提供することです。
Class オブジェクトにより提供されるサービスは、通常、インタフェース、または public としてのインスタンス生成が不可能な実装への参照です。この Class は、BeanContextServiceProvider、サービスのファクトリ、およびサービスが登録されている BeanContext 内で現在入れ子になっている BeanContextChild と関連付けられた任意のオブジェクト間のインタフェースプロトコルまたは規約を定義します。一般に、この種のプロトコルは、BeanContextChild の実装を依存関係から孤立させる、コンテキスト固有またはコンテキスト依存の動作をカプセル化します。このため、実装の単純化、高い相互運用性および移植性が実現します。
BeanContextServiceProvider は、1 つ以上のサービスの「ファクトリ」です。これは、サービスが BeanContextServices に登録されていない場合、adService() メソッドを介して自らを特定の BeanContextServices に登録します。BeanContextServices は、指定されたサービスを BeanContextServiceProvider に関連付け、serviceAvailable() メソッド経由で BeanContextServiceAvailableEvent を現在登録されている BeanContextServicesListeners にトリガーし、その後 true を返します。それ以外の場合は false を返し、サービスが BeanContextServices に登録されていることを示します。
いったん登録されたサービスは、取り消されるまで、BeanContextServices getService() メソッドを介して利用可能になります。
hasService() メソッドは、特定のサービスが存在するかどうかをテストするために使用可能です。また、getCurrentServices() メソッドは、BeanContextServices で現在利用可能なサービスに対してイテレータを返します。
BeanContextChild または BeanContextChild と関連付けられた任意のオブジェクトは、getService() メソッドの呼び出しを介して、入れ子になった BeanContextServices から現在登録されているサービスへの参照を取得できます。getService() メソッドが指定するのは、BeanContextChild、関連付けられた要求者、要求されたサービスの Class、サービスに依存したパラメータ (サービスセレクタとして知られる)、およびその後、BeanContextServiceProvider により取り消されたサービスクラスを要求者に通知するために使用される BeanContextServicesRevokedListener です。リスナーは、要求者およびサービスクラスごとに、ユニキャストイベントソースに自動的に登録されます。また、要求者が指定されたサービスクラスの参照すべてを放棄するか、BeanContextServiceProvider を提供することによりサービスが強制的に取り消された場合にその副作用として、自動的に登録解除されます。
BeanContextServices は、この getService() 呼び出しを、関連する BeanContextServiceProvider (存在する場合) に渡し、その getService() メソッドの呼び出しを介して条件が満たされるようにします。BeanContextServiceProvider は、BeanContext、提供されるサービスのクラス、サービスに依存したサービスパラメータ (サービスセレクタ)、およびサービスを要求するオブジェクトへの参照に渡されます。
BeanContext への参照は、BeanContextServiceProvider に対し、複数のソースからのサービス要求を識別可能にすることを目的としています。BeanContextServiceProvider には、このようにして取得されたすべての BeanContext への弱参照を保持することだけが許可されます。
サービスセレクタのパラメータは、サービスの要求者により特定のサービスで使用される、サービスに依存した値です。これには、BeanContextServiceProvider により提供されるサービスをパラメータ化する目的があります。その使用例としては、サービス実装クラスのコンストラクタへのパラメータ、特定のサービスプロパティーの値、既存の実装マップ内へのキーとしての使用などを挙げることができます。
要求者への参照には、BeanContextServiceProvider に対して要求者の状態調査を許可し、サービスのカスタマイズまたはパラメータ化を行う目的があります。このため、この参照は、BeanContextServicesProvider により「不変である」と見なされます。また、getService() 呼び出しからの復帰後に、BeanContextServiceProvider は、要求者と BeanContextChild の両方への弱く不変の参照だけを保持する許可が与えられます。
BeanContextServiceProvider は要求にこたえて、要求されたサービスの Class インスタンスへの参照を返す (返される参照は、<serviceRefence> instanceof <serviceClass> が true になる) か、null を返すか、または非チェック例外をスローします。
入れ子になった BeanContextServices が、BeanContextServiceProvider を持たない特定のサービスに対して要求される場合、BeanContextServices は、要求されたサービスを、自らの入れ子にされた BeanContextServices に委譲して要求を満たします。このようにして、委譲要求は葉である BeanContextServices からルートである BeanContextServices へ伝搬可能になります。
特定のサービスクラスが、入れ子にされた BeanContextServices.getCurrentServiceSelectors() メソッドを介してサービスクラスの演算値の有限リストを実装し、次に BeanContextServiceProvider.getCurrentServiceSelectors() を介して現在利用可能なサービスセレクタ (存在する場合) を取得する場合、BeanContextChild は、特定の BeanContextServices に対して、現在利用可能なサービスクラスおよび割り当てられたすべてのサービスセレクタのリストを (getCurrentServiceClasses() メソッドを介して) 問い合わせます。
問題のサービスは、有効なサービスセレクタセットの有限な演算値セットを実装しない場合、null を返します。
BeanContextChild により getService() を介して取得された参照は、参照が BeanContextChild により、入れ子にされた BeanContextServices の releaseService() メソッドの呼び出しを介して解放されるまで有効です。ただし、BeanContextServices が BeanContextServiceRevokedEventをトリガーし、そのイベントの isCurrentServiceInvalidNow() メソッドが true を返す場合を除きます。この場合、BeanContextServices またはサービスを提供した BeanContextServiceProvider は、現在のサービス参照がただちに無効にされた、つまり「強制的に取り消された」(一般的には次の状況で発生) と判断します。
BeanContextChild インスタンスが特定の BeanContextServices インスタンスから削除されると、BeanContextChild インスタンスは、適切な releaseService() を呼び出すことにより、BeanContextServices から取得したサービスへの参照をすべて破棄します。入れ子状態を解除する BeanContextChild も BeanContextServices インスタンスである場合、およびこれらのサービス参照のすべてが、すでに定義されたように、委譲された getService() 要求の結果として、入れ子状態を解除する BeanContextServices 自身のメンバーに公開されている場合には、BeanContextServiecs は、BeanContextServiceRevokedEvent をトリガーして、入れ子にされた子に対して「サービスが強制的に取り消された」ことを通知します。入れ子状態の解除時に委譲されたサービスへの現在の参照をただちに無効化することにより、階層構造に依存するサービスの位置が変化した場合に、要求者によってそのサービスが使用されることを防ぐことができます。
サービスクラスの「強制的な取り消し」を受ける BeanContextChild インスタンスは、保持する可能性のあるその種の参照用の releaseService() を呼び出しません。この場合、BeanContextServiceProvider または BeanContextChild へのサービス参照を提供した BeanContextServices は、そのサービスへの参照をすべて無効にしてしまっているからです。
BeanContextServiceProvider は、revokeService() メソッドを呼び出して BeanContextServices へ登録したあとであれば、いつでもサービスクラスを呼び出すことができます。BeanContextServices が BeanContextServiceRevokedEvent をトリガーして、現在登録された BeanContextServiceRevokedListeners および現在サービスを利用できない BeanContextServicesListeners を通知すると、そのサービスクラスが再度登録されるまで、取り消されたサービスに対する新たなサービス要求には対応できなくなります。取り消される前に BeanContextChild の要求者により取得されたサービスへの参照は、有効のままになります。このため、サービスは、そのサービスへのすべての参照が解放されるまで有効のままになり、現存する参照を満たします。ただし、例外的な状況で、サービスの取り消し時に BeanContextServiceProvider または BeanContextServices が現在の参照すべてへのサービスをただちに終了させる場合を除きます。即時取り消しは、BeanContextServices .revokeService() メソッドを、実パラメータ値 revokeNow == true で呼び出すことにより実行されます。現在のサービス参照の即時無効化の実行後に、サービス実装が、即時取り消し通知を無視してサービスへの参照を誤って保持しているサービスの要求者が呼び出すサービスを引き続き使用しようとする試みに応答して、サービス固有の非チェック例外をスローする場合があります。
(サービス要求の委譲時に) マルチスレッド環境で正しく動作するためには、BeanContextServices の実装は、addService()、hasService()、getCurrentServiceClasses()、getCurrentServiceSelectors()、getService()、releaseService()、および revokeService() の実装を、BeanContext.globalHierarhyLock と同期させる必要があります。
BeanContextServicesProvider は、BeanContextServicesProviderBeanInfo を実装する BeanInfo クラスを提供することにより、実装を提供するサービスクラスに対して BeanInfo を公開します。このようにして、BeanInfo の配列を公開することにより、各サービスクラス用の BeanInfo がサポートされます。たとえば、開発用ツールは、この情報を利用してアプリケーション開発者に対し、アプリケーションに含まれるサービスクラスのパレットを提供できます。
BeanContext の主な役割の 1 つは、JavaBean コンポーネントの論理的な入れ子構造および BeanContext のインスタンス階層の表現です。このため、階層が持続的であるさまざまなシナリオを期待するのは当然のことです。階層が持続的であるとは、つまり、BeanContext が持続性メカニズム、特に java.io.Serializable または java.io.Externalizable のいずれかに参加する場合のことを指します。後者の場合、BeanContext は、子のサブグラフ用の持続性コンテナ、クラス情報のエンコードおよびデコード、および直列化解除後のサブグラフ相当機能の維持など、基本的に ObjectOutputStream および ObjectInputStream による直列化に対応した機能を提供します。
特に、BeanContext は、自身が持続性を付与されるか復元された場合、持続性を持ち、かつ適切な持続性インタフェースを実装する現在の子を復元します。
上記の要件の結果として、入れ子にされた BeanContext へのすべての参照、または入れ子にされた BeanContextServices 経由で取得された委譲者すべてへの参照を持続性のないものにするために、持続性を持つ BeanContextChild の実装が求められます。
BeanContexts は、持続状態からの BeanContextChild インスタンスを復元する際に、新たにインスタンス生成された BeanContextChild への add() の呼び出しと等価な動作を実行する必要があります。その結果、入れ子になった BeanContext の新たに復元されたインスタンスが通知されるため、BeanContextChild はその環境への依存関係を十分に再確立できるようになります。
また、BeanContext は java.beans.beancontext.BeanContextChild を実装するため、このインタフェースの実装を扱う際には、次に定義された持続性要件に準拠する必要があることにも留意してください。
必須ではありませんが、多くの BeanContext は java.awt.Container および java.awt.Component の表示階層内で関連付けることができます。Container は BeanContext を直接実装することはできません2が、そこに記述された BeanContextProxy インタフェースを実装することにより BeanContext に関連付けることができます。
BeanContext インタフェースを直接実装しない (そのコンポーネントやサブクラスの場合は、実装できない) クラスのインスタンスが、その実装のインスタンスに関連付けられている場合、(デリゲーションを介して) そのインスタンスは BeanContextProxy インタフェースを実装することにより、この関連付けを公開できます。このようにして、構築ツールなどの任意のサードパーティーが、そのオブジェクトと関連付けられた BeanContext の問い合わせおよび検出を行い、関連付けられた BeanContext 内で入れ子になったオブジェクトからメンバーシップ変更を検出するか、またはそのサービスを取得することが可能になります。
これにより、複数の個別のオブジェクト (コンテナなど) が 1 つの BeanContext を共有することが可能になります。この場合、共有された BeanContext は BeanContextContainerProxy を実装しないことに留意してください。これは、単一の BeanContext とそのインタフェースを実装するコンテナ間のピアツーピアの関係であるためです
getBeanContext() から返される値は、実装するインスタンスの有効期間中変化しません。つまり、BeanContextProxy と関連付けられた BeanContext の関係は静的であるため、どちらかが有効である間は変化しません。
BeanContext (または BeanContextChild) と BeanContextProxy インタフェースの両方を実装するクラスは存在しません。これらを一緒に使用することはできません。
BeanContextProxy の実装元によっては、Collection に基づく BeanContext を維持することに加え、そしておそらくそれとは別個に、java.util.Collection またはほかの何らかのコレクションに似た API (javajava.awt.Container など) を実装する場合があります。
この場合、Collection API 経由で BeanContext から要素を追加または削除するか、あるいはコレクションに似た API (public boolean java.awt.Container.add(Component) など) を使って BeanContextProxy の実装元から要素を追加または削除することが可能です。BeanContext のコレクションまたは BeanContextProxy の実装元のコレクションに対して追加または削除されたオブジェクトが、対応するオブジェクトのコレクションに対しても追加または削除されるかどうか (つまり、Container.add() は BeanContext.add() も推測するかどうか、およびその逆) は、実装に依存しています。そのような状況では、両者 (BeanContextProxy の実装元および BeanContext 自体) が 1) ほかと同じ追加/削除セマンティクスを実装する (つまり、x.add(o) が x.getBeanContext().add(o) の副作用を受ける場合、x.getBeanContext().add(o) も x.add(o) の副作用を受ける)、および 2) ほかの該当コレクションに対して追加/削除操作を実行する前に、そのオブジェクトがほかの該当コレクションのメンバーであるかどうか (同期化を) テストする (両者へのコレクション操作で無限再帰が発生するのを避けるため) 必要があります (つまり、x.getBeanContext().contains(o) が true の場合に x.add(o) は x.getBeanContext().add(o) を呼び出さない、およびその逆)。
BeanContextProxy を実装するオブジェクトが BeanContext に対して追加または削除された場合、そのオブジェクトに操作を実行することに加え、同じ操作を BeanContextProxy.getBeanContext() から返された BeanContext に対しても実行する必要があることに留意してください。つまり、BeanContextProxy の実装元は、任意の入れ子になった BeanContext により、直接実装された BeanContext と同じ扱いを受ける必要があります。操作が BeanContext に適用される場合は、対応する BeanContextProxy にも適用される必要があります
次のインタフェースは、BeanContext が、関連付けられたコンテナへの参照を公開して、その BeanContextChild メンバーが関連付けられたコンポーネントオブジェクトの追加または削除をコンテナに対して行うこと、またはコンテナの状態を検査することを可能にします。
関連付けられたコンポーネントを持つ BeanContextChild が、関連付けられたコンテナを持つ BeanContext に追加される場合、結果として、コンテナ内のコンポーネントの入れ子に関連して相互作用のモデルが 3 つ存在します。
または
このようにして、最大の相互運用性を得るために、BeanContextChild は常に、そのコンポーネントの親が BeanContext コンテナであるかどうかをチェックし、親でない場合には、適切であれば自らを追加します。このため、BeanContextChild はどんなシナリオのもとでも正しく機能します。
BeanContextChild は、最初に show() の呼び出しを介して自らを表示可能にする役割を担当します。BeanContextChild は、自らに対して hide() および show() を繰り返し実行可能です。
入れ子にされた BeanContext、またはその関連するコンテナは、BeanContextChild のコンポーネントに対して hide() または show() を任意に実行できます。ただし、イベント通知を取得するためにリスナーを登録する場合、またはほかのコンポーネント/コンテナ固有のプロトコルがコンテナに対しそのコンポーネントの内容の状態を変更することを許可またはリクエストする場合を除き、コンポーネントをすべての面で不変として扱うことを強くお勧めします。この種の許可された相互作用の例として、background または foreground カラーなどのプロパティーがコンテナからコンポーネントに伝達される場合を挙げることができます。
いったん BeanContextChild が BeanContext からの入れ子状態を解除されると、関連するコンポーネント (存在する場合) は、削除操作の副作用としてその BeanContext のコンテナから削除されます。これは、BeanContext の役割です。一般に、setBeanContext() メソッドの呼び出しを介して BeanContextChild を関連付けられたコンテナとともに別の BeanContext へ移動すると、そのコンポーネントには、その操作の副作用として、元の BeanContext が子からの PropertyChangeEvent を介して変更通知を受ける前に、ふたたび親子関係が確立されます。ただし、検査を行なって、それが発生していない場合にはコンポーネントを削除する必要があります。
無限回帰を避けるため、コンテナおよびコンポーネントの入れ子関係とも関連付けられた BeanContext および BeanContextChild の両方で、関係の相手によりコンポーネントに適用されたすべての変更を取り消さないようにする必要があります。一般に、BeanContext は、BeanContextChild のコンポーネントの外観、可視設定、相対位置を担当します。BeanContextChild は、実装するアプリケーション機能に属するコンポーネントの状態および内容を担当します。
getContainer() メソッドから返される値は、実装する BeanContext の有効期間中は一定です。つまり、BeanContext とコンテナ間の関係は両者の有効期間中は静的です。
また、次のインタフェースも定義されます。
BeanContext または BeanContextChild は、このインタフェースを実装して、関連付けられた GUI コンポーネントを入れ子になった BeanContext に公開します。BeanContext は、このメソッドを使って、コンポーネントのインスタンスへの参照と、それが認識する BeanContextChild への参照間の関係を確立できます。その場合、BeanContextChild およびコンポーネントは、同じオブジェクトインスタンスによって実装されることはありません。つまり、BeanContextChild はそのコンポーネント実装を、コンポーネントから継承するのではなく、個別のオブジェクトに委譲します。BeanContext は、入れ子になった BeanContextChild から取得したコンポーネント参照を調査して、その状態を判別します。その後、リスナーを特定のイベント用に登録します。ただし、コンポーネントの状態を変更しないため、BeanContext が参照を一般に不変なものとして処理することを強くお勧めします。
getComponent() メソッドから返される値は、BeanContextChild が有効である間、一定です。
BeanContext が関連するコンテナを保持するが、BeanContextContainerProxy インタフェースを実装してコンテナを公開したくない場合、かつ任意の BeanContextChild の関連するコンポーネント (BeanContextChildComponentProxy インタフェースを実装するか、コンポーネントの直接のサブクラスとして、BeanContextChild により公開された) の入れ子状態を処理する場合、BeanContext にはそのコンポーネントを関連するコンテナに対して追加または削除を行う権限が与えられます。この場合、BeanContextChild およびその関連するコンポーネント実装は、このアクションに干渉しません。
クラスが BeanContextChildComponentProxy と BeanContextContainerProxy の両方を実装する場合、getComponent() と getContainer() の双方が返すオブジェクトは、同一のオブジェクトになります。
環境のサポートまたは知識を必要としない単純な JavaBeans は、現在と同様将来も機能します。ただし、包含する BeanContext を利用する JavaBeans および入れ子になった BeanContext の両方とも、認識している JavaBeans および入れ子になった BeanContext を使って、内包する BeanContext への参照の伝搬を可能にするメカニズムを実装する必要があります。提案されているインタフェースは次のとおりです。
public interface java.beans.beancontext.BeanContextChild { void setBeanContext(BeanContext bc) throws PropertyVetoException; BeanContext getBeanContext(); void addPropertyChangeListener (String name, PropertyChangeListener pcl); void removePropertyChangeListener (String name, PropertyChangeListener pcl); void addVetoableChangeListener (String name, VetoableChangeListener pcl); void removeVetoableChangeListener (String name, VetoableChangeListener pcl); }
予想される使用法としては、サードパーティーが BeanContext 上で定義された適切なメソッドの 1 つを呼び出して (コレクションからの継承により)、ターゲットの BeanContext のメンバーシップに BeanContextChild を追加する場合が挙げられます。その結果、BeanContext は、設定用メソッド setBeanContext() を呼び出して、BeanContextChild の「beanContext」プロパティーを設定しようとします。BeanContextChild の setBeanContext() メソッドを呼び出すことができるのは、BeanContext だけです。このメカニズムは、新たな BeanContext 値を保持する子の通知に BeanContext が使用するメカニズムであるためです。このプロパティーは、アプリケーション構築用ツールを使ってユーザーが直接設定したり、カスタマイズすることはできません。このため、BeanContextChild 用の BeanInfo は、構築ツールがユーザーにプロパティーのカスタマイズを提示することのないように、このプロパティーを隠された状態に設定する必要があります。
BeanContextChild オブジェクトは、PropertyVetoException をスローして、入れ子にされた BeanContext が、特定の BeanContext 内で機能しないこと、または入れ子にされていないことを通知することができます。そのような拒否通知は、BeanContext によって、BeanContextChild がその特定の BeanContext 内で機能しない、つまりファイナルであると解釈されます。
BeanContextChild を BeanContext への入れ子状態から解除している間、その子または子の PropertyVetoEvents の「beanContext」プロパティーに従うサードパーティーが、PropertyVetoException をスローして、呼び出し側に入れ子解除状態ではないことを通知できます。このやり取りを抑制するために、BeanContextChild またはサードパーティーは、初期入れ子解除通知を拒否できます。ただし、その後の通知を拒否することはできません。そして、通知を受け取ったあと、自らの状態を通知に合わせて修正して、その後に実行される入れ子解除に備える必要があります。
このインタフェースを実装するクラスは、java.beans.PropertyChangeListener の (サブ) インタフェースのイベントソースとしても動作します。このため、その状態を適切に更新してから、適切な java.beans.PropertyChangeEvent をトリガーする必要があります。その際、propertyName には「beanContext」を、oldValue には以前に入れ子であった BeanContext への参照を、newValue には新たに入れ子にされた BeanContext への参照を指定して、入れ子にされた BeanContext の値が変化したことをすべてのリスナーに通知します。
終了処理中の BeanContextChild インスタンス、または入れ子になった BeanContext は、終了前に階層から自らを引き出すために、入れ子になった BeanContext に対して remove() メソッドを呼び出します。
BeanContext 内で入れ子にされた BeanContextChild のインスタンスは、一般に、入れ子にされた BeanContext インスタンスへの参照を含むフィールドまたはインスタンス変数を、さらに可能であれば getService() メソッドを使って BeanContextServices インスタンスから取得したサービスを、定義します。
その種のインスタンスに持続性を持たせる作業の際、インスタンスが入れ子にされた環境で間違ってオブジェクトに持続性を持たせてしまうことがないよう、インスタンスがフィールドまたはインスタンス変数を一時的なものとして定義するか、そのような状態が持続することがないようカスタムの持続性メソッドを実装する必要があります。
ターゲットオブジェクトの直列化が、内部で入れ子にされたソース環境の大半も直列化する場合に、オブジェクトの直列化を介し、クリップボードを使用して行うオブジェクトインスタンスのカットおよびペーストなどの操作は正しく動作しないため、この要件は非常に重要です。
java.beans.instantiate() は JavaBeans の (再) インスタンス生成を実行する現行のメカニズムであるため、BeanContext の抽象化導入に対応するためには、このメソッドの構文およびセマンティクスを継承またはオーバーロードする必要があります。提案されている拡張は、次のとおりです。
このメソッドの機能は、現在 JavaBeans 仕様で定義されていますが、これら既存のセマンティクスに加え、null 以外の BeanContext が指定された場合、メソッドは beanContext の実パラメータとして targetChild の実パラメータの値 (新たにインスタンス生成された JavaBean コンポーネントへの参照) を指定して add() メソッドを呼び出します。4
java.beans.instantiate() の現行の実装には、アプレットでもある JavaBeans のインスタンスを生成するための最小限のサポートが含まれています。特に、このメソッドは、新たにインスタンス生成された JavaBean 用の AppletContext および AppletStub を構築し、まだ呼び出されていない場合には、新たにインスタンス生成されたアプレットにスタブを設定してから、アプレットに対して init() を実行します。
残念なことに、java.beans.instantiate() により作成された AppletContext および AppletStub を操作することはできないため、現行の実装では大半のアプレットが十分に機能するほどのサポートが提供されることはありません。これは、既存の Applet API に AppletContext および AppletStub 実装の構築方法を規定する仕様が不足しているために生じた結果です。また、そのような仕様が存在したとしても、あとで適切に初期化されたオブジェクトのインスタンスを生成するには、Codebase、Parameters、AppletContext、および Documentbase などの多数の Applet 属性を java.beans.instantiate() に伝搬する API が必要です。
完全に機能するアプレットをサポートする鍵は、アプレットに完全に機能する AppletContext および AppletStub のインスタンスを提供することであるため、設計上の目標は instantiate() を実行してこの状態を実現するメカニズムを提供し、適切な初期化およびバインディング5を実行できるようにすることです。このため、提案されるインタフェースは次のようになります。
public static Object instantiate(ClassLoader cl, String beanName, BeanContext bCtxt, AppletInitializer ai ); public interface AppletInitializer { void initialize(Applet newApplet, BeanContext bCtxt); void activate(Applet newApplet); }
新たにインスタンス生成された JavaBean コンポーネントが java.applet.Applet のインスタンスである場合、新たに構築されたアプレット (Bean) が initialize() への呼び出しを介して AppletInitializer に渡されます。
仕様に準拠した AppletInitializer.initialize() の実装は、次のようになります。
仕様に準拠した AppletInitializer.activate() の実装は、アプレットにアクティブであるとのマーク付けを行います。またオプションで、アプレットの start() メソッドを呼び出すこともできます。
新たにインスタンス生成された JavaBean が、アプレットのインスタンスではない場合は、AppletInitializer インタフェースは無視されることに留意してください。
InfoBus 技術は、発行と署名の抽象化に基づき、単一の Java Virtual Machine 内の JavaBean コンポーネント間で、動的な自己記述型データの認識および交換を容易にする標準拡張パッケージです。
InfoBus を入れ子にされた BeanContextChild に公開する BeanContext は、javax.infobus.InfoBus 型の hasService() および getService() メソッドを介してサービスを公開することにより、その機能を実行します。
このため、BeanContextChild の実装は、このメカニズムを使って InfoBus のインスタンスを認識することにより、現行の BeanContext に対応した一般的な InfoBus の実装を検出できます。
Infobus 1.2 の仕様では、InfoBus クラスが提供する便利なメカニズムが定義されています。このメカニズムを利用すると、特定の BeanContextServices のインスタンス内で入れ子にされた BeanContextChild インスタンスの検出メカニズムが単純化されます。
出力機能をその下位構造に公開する BeanContext は、java.awt.PrintJob (サブ) タイプの参照を委譲できます。
Java Network Printing Interface が発展するにつれて、補足的な仕様が提供され、サービスメカニズムを通じてその機能が公開されます。
JavaBeans は、JavaBeans がアプリケーション開発ツールつまり IDE で開発者により操作および変換される際のモードである、「設計」モードをサポートします。また、その結果 JavaBeans がアプレット、アプリケーションまたは抽象化されたほかの実行可能プログラムの一部として実行時にインスタンス生成される際のモードである、「実行」モードもサポートします。
仕様の最初のバージョンでは、「モード」または状態 (つまり「設計」時または「実行」時) は、JVM のグローバルな属性でした。しかし、これでは不十分です。というのは、たとえばアプリケーション開発ツール環境では、「実行」モードで機能する JavaBeans がアプリケーション開発ツール環境の一部として存在します。同様に、「設計」モードで機能する JavaBeans は、開発者がアプリケーション作成にアプリケーション開発ツールを使って構築します。
このため、この「モード」を JVM グローバルよりさらに細分化してスコープする能力が必要になります。
抽象化された BeanContext は、1 つ以上の JavaBeans の「コンテナ」または「コンテキスト」として、この「モード」をより細かくスコープするための適切なメカニズムを提供します。
このため、この「モード」を下位に公開および伝達する BeanContext は、次のようにして java.beans.DesignMode 型の参照を委譲できます。
public interface java.beans.DesignMode { void setDesignTime(boolean isDesignTime); boolean isDesignTime(); }
また、参照を委譲する BeanContext は、propertyName = "designTime" と指定し、モードにより値が変更される際には oldValue および newValue に適切な値を設定して、適切な java.beans.propertyChangeEvent をトリガーする必要があります。
BeanContextChild のインスタンスが、内部で入れ子になった BeanContext のインスタンスに対して setDesignTime() を呼び出すのは、不正な動作であることに留意してください。
関連付けられた表示機能、つまり GUI を保持する JavaBeans は、GUI が物理的に不可能である (ハードウェアが存在しない)、または現在の状況では適切ではない (クライアントではなくサーバーコンテキストで実行中) ことを示す環境でインスタンス生成が可能です。
JavaBeans 仕様の最初のバージョンでは、JavaBeans に「可視」状態、つまり GUI のレンダリング用のメカニズムを提供するために、java.beans.Visibility インタフェースが導入されました。
子が持つ GUI の表示機能に関して、特定のポリシーを施行する BeanContext は、java.beans.Visibility インタフェースを使って、子を制御します。
BeanContext は、入れ子構造になった JavaBeans 間で重要な属性の関連付けおよび伝達を行うために、関連付けられたロケールを保持する場合があります。
このため、BeanContext は、propertyName = "locale" とし、oldValue にロケール委譲の前の値への参照を設定し、newValue にはロケール委譲の新たな値への参照を指定して、適切な java.beans.PropertyChangeEvent をトリガーする必要があります。これにより、ロケールの変更がリスナーに通知されます。
BeanContext に対するロケール値の設定および取得は、実装に依存しています。
この比較的複雑なプロトコルの実装を容易にするために、「ヘルパー」クラスである、java.beans.beancontext.BeanContextChildSupport、java.beans.beancontext.BeanContextSupport、および java.beans.beancontext.BeanContextServicesSupport が提供されます。これらのクラスは、別のオブジェクトにより暗黙的にサブクラス化または委譲されるように設計されています。また、ここで統合するプロトコルの実装に完全に準拠 (拡張可能) しています。
目次 |