現在、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()の定義に合わせて、BeanContextEventはトリガーされません。
BeanContextMembershipListenersの追加および削除は、addBeanContextMembershipListener()およびremoveBeanContextMembershipListener()の呼出しを通して実行されます。
toArray()メソッドは、現在のJavaBeanセットのコピーまたはターゲットのBeanContext内で入れ子になったBeanContextのインスタンスを返します。また、iterator()メソッドは、子の同じセットにjava.util.Iteratorオブジェクトを提供します。
指定されたオブジェクトが現在BeanContextの子である場合、contains()メソッドはtrueを返します。
size()メソッドは、入れ子になった現在の子の数を返します。
isEmpty()メソッドは、BeanContextに子がない場合、trueを返します。
すべてのCollectionメソッドは、マルチスレッド環境で正しく機能するために、指定された実装による適切な同期化を相互に必要とします。これにより、指定されたBeanContext内で入れ子になったJavaBeanセットのメンバーシップへのすべての変更が、不可分で確実に適用されます。すべての実装で、BeanContext.globalHierarchyLockを使用してこれらのメソッドの実装を同期化する必要があります。
ある状況では、add()およびremove() (またはその変化形)操作が入れ子状態で行われる場合があります。つまり、呼出し元Threadのスタック上で複数の操作が同時に発生することがあります。たとえば、BeanContextChildのAが追加(または削除)されるとき、そのsetBeanContext()メソッドも別のBeanContextChildのBを追加(または削除)します。特定のBeanContext実装は、BeanContextMembershipListener通知をBのadd()/remove()操作に対して1つ、その後Aの操作に対して1つ、合計2つ発行することを選択できます(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を現在登録されているBeanContextServicesListenerにトリガーし、その後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は、サービス・クラスをBeanContextServicesへ登録した後であれば、いつでもrevokeService()メソッドを呼び出してサービス・クラスを取り消すことができます。BeanContextServicesがBeanContextServiceRevokedEventをトリガーして、現在登録されているBeanContextServiceRevokedListenerとBeanContextServicesListenerにサービスが現在使用できないことを通知した後は、そのサービス・クラスが再度登録されるまで、取り消されたサービスに対する新たなサービス要求には対応できなくなります。サービスが取り消される前に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()を任意に実行できます。ただし、イベント通知を取得するためにリスナーを登録する場合、または他のコンポーネント/コンテナ固有のプロトコルがコンテナに対しそのコンポーネントの内容の状態を変更することを許可または要求する場合を除き、コンポーネントをすべての面で不変として扱うことを強くお薦めします。この種の許可された相互作用の例として、バックグラウンド・カラーやフォアグラウンド・カラーなどのプロパティがコンテナからコンポーネントに伝播される場合をあげることができます。
いったんBeanContextChildがBeanContextからの入れ子状態を解除されると、関連するコンポーネント (存在する場合)は、削除操作の副作用としてそのBeanContextのコンテナから削除されます。これは、BeanContextの役割です。一般に、setBeanContext()メソッドの呼出しを介してBeanContextChildを関連付けられたコンテナとともに別のBeanContextへ移動すると、そのコンポーネントには、その操作の副作用として、元のBeanContextが子からのPropertyChangeEventを介して変更通知を受ける前に、ふたたび親子関係が確立されます。ただし、検査を行なって、それが発生していない場合にはコンポーネントを削除する必要があります。
無限回帰を避けるため、コンテナおよびコンポーネントの入れ子関係とも関連付けられたBeanContextおよびBeanContextChildの両方で、関係の相手によりコンポーネントに適用された変更を取り消さないようにする必要があります。一般に、BeanContextは、BeanContextChildのコンポーネントの外観、可視設定、相対位置を担当します。BeanContextChildは、実装するアプリケーション機能に属するコンポーネントの状態および内容を担当します。
getContainer()メソッドから返される値は、実装するBeanContextの有効期間中は一定です。つまり、BeanContextとコンテナ間の関係は両者の有効期間中は静的です。
また、次のインタフェースも定義されます。
BeanContextまたはBeanContextChildは、このインタフェースを実装して、それを入れ子にしているBeanContextに、関連付けられたGUIコンポーネントを公開できます。 BeanContextは、このメソッドを使用して、コンポーネントのインスタンスへの参照と、それが認識するBeanContextChildへの参照間の関係を確立できます。この場合のBeanContextChildとコンポーネントは、同じオブジェクト・インスタンスによって実装されたものではありません(つまり、BeanContextChildはそのコンポーネント実装を、コンポーネントから継承するのではなく、別のオブジェクトに委譲します)。BeanContextは、入れ子になったBeanContextChildから取得したコンポーネント参照を調査して、その状態を判別できます。また、リスナーを特定のイベント用に登録することもできます。ただし、コンポーネントの状態を変更しないために、BeanContextが参照を一般に不変なものとして処理することを強くお薦めします。
getComponent()メソッドから返される値は、BeanContextChildが有効である間、一定です。
BeanContextが関連するコンテナを保持するが、BeanContextContainerProxyインタフェースを実装してコンテナを公開したくない場合、かつ任意のBeanContextChildの関連するコンポーネント (BeanContextChildComponentProxyインタフェースを実装するか、コンポーネントの直接のサブクラスとして、BeanContextChildにより公開された)の入れ子状態を処理する場合、BeanContextにはそのコンポーネントを関連するコンテナに対して追加または削除を行う権限が与えられます。この場合、BeanContextChildおよびその関連するコンポーネント実装は、このアクションに干渉しません。
クラスがBeanContextChildComponentProxyとBeanContextContainerProxyの両方を実装する場合、getComponent()とgetContainer()の双方が返すオブジェクトは、同一のオブジェクトになります。
環境のサポートまたは知識を必要としない単純なJavaBeanは、現在と同様将来も機能します。ただし、包含するBeanContextを利用するJavaBeanおよび入れ子になったBeanContextの両方とも、内包するBeanContextへの参照を、認識しているJavaBeanおよび入れ子になった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への入れ子状態から解除している間、この子またはこの子の「beanContext」プロパティでPropertyVetoEventをリスニングしているサード・パーティは、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のグローバルな属性でした。しかし、これでは不十分です。というのは、たとえばアプリケーション開発ツール環境では、「実行」モードで機能するJavaBeanがアプリケーション開発ツール環境の一部として存在します。同様に、「設計」モードで機能するJavaBeanは、開発者がアプリケーション作成にアプリケーション開発ツールを使用して構築します。
このため、この「モード」を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が提供されます。これらのクラスは、別のオブジェクトにより暗黙的にサブクラス化または委譲されるように設計されています。また、ここで統合するプロトコルの実装に完全に準拠(拡張可能)しています。
目次 |