BeanContext API
Overview
The purpose of the Extensible Runtime Containment and Services Protocol is to introduce the concept of a relationship between a Component and its environment, or Container, wherein a newly instantiated Component is provided with a reference to its Container or Embedding Context. The Container, or Embedding Context not only establishes the hierarchy or logical structure, but it also acts as a service provider that Components may interrogate in order to determine, and subsequently employ, the services provided by their Context."This protocol supports extensible mechanisms that:
- Introduce an abstraction for the environment, or context, in which a JavaBean logically functions during its life-cycle, that is a hierarchy of JavaBeans
- Enable the dynamic addition of arbitrary services to a JavaBean's environment
- Provide a single service discovery mechanism through which JavaBeans may interrogate their environment in order both to ascertain the availability of particular services and to subsequently employ those services.
- Provide better support for JavaBeans that are also Applets.
In English, this means that there now exists a standard mechanism through which Java developers can logically group a set of related JavaBeans into a "context" that the beans can become aware of and/or interact with. This context, or "containing environment", is known as the BeanContext.
There are two distinct types of BeanContext included in this protocol: one which supports membership only (interface java.beans.beancontext.BeanContext and one which supports membership and offers services (interface java.beans.beancontext.BeanContextServices to its JavaBeans nested within.
To orient yourself with the classes and interfaces of java.beans.beancontext, take a minute to look over the following hierarchy diagram. You will notice that the majority of the package is defined as interfaces, which allow for multiple inheritance.
Inheritance Diagram of the BeanContext API
The classes and interfaces relevant to the BeanContext API are listed in the following diagrams. As you study the diagrams, take note of the BeanContext and BeanContextServices interfaces, and that each has its own concrete implementation that you can subclass or instantiate directly (classes java.beans.beancontext.BeanContextSupport and java.beans.beancontext.BeanContextServicesSupport respectively). Also take note of the location of the java.beans.beancontext.BeanContextChild interface. This is the interface that allows nested JavaBeans to become aware of their enclosing BeanContext.
![]()
![]()
![]()
![]()
![]()
![]()
Bean Context #1: Containment Only
The "containment" portion of the Extensible Runtime Containment and Services Protocol is defined by the BeanContext interface. In its most basic form, a BeanContext is used to logically group a set of related java beans, bean contexts, or arbitrary objects. JavaBeans nested into a BeanContext are known as "child" beans. Once nested, a child bean can query its BeanContext for various membership information, as illustrated in the following examples.
Here are some possible BeanContext containment scenarios:
The sample code presented in this chapter uses instances of the BeanContextSupport helper class to provide the basic BeanContext functionality. A BeanContextSupport object is simply a concrete implementation of the BeanContext interface.
With a BeanContextSupport instance, it is possible to:
- Add an object, bean, or BeanContext:
boolean add(Object o)- Remove an object, bean, or BeanContext:
boolean remove(Object o)- Add a BeanContextMembershipListener:
void addBeanContextMembershipListener(BeanContextMembershipListener bcml)- Remove a BeanContextMembershipListener:
void removeBeanContextMembershipListener(BeanContextMembershipListener bcml)- Get all JavaBean or BeanContext instances currently nested in this BeanContext as an array or as an Iterator:
Object[] toArray() Object[] toArray(Object[] a) Iterator iterator()- Determine whether or not a specified object is currently a child of the BeanContext:
boolean contains(Object o)- Get the number of children currently nested in this BeanContext:
int size()- Determine whether or not the BeanContext currently has zero children:
boolean isEmpty()- Instantiate a new JavaBean instance as a child of the target BeanContext:
java.lang.String)">Object instantiateChild(String beanName)The following test programs, which are run from the command line, illustrate the use of these methods.
The comments in the source code explain the purpose of each.
File:
Example1.javaimport java.beans.beancontext.*; /** * A test program that adds a bean to a beancontext, and * reports on various aspects of the context's membership state. * This program also shows that a bean's getBeanContext() method * can be called to get a reference to its enclosing context. */ public class Example1 { private static BeanContextSupport context = new BeanContextSupport(); // The BeanContext private static BeanContextChildSupport bean = new BeanContextChildSupport(); // The JavaBean public static void main(String[] args) { report(); // Add the bean to the context System.out.println("Adding bean to context..."); context.add(bean); report(); } private static void report() { // Print out a report of the context's membership state. System.out.println("============================================="); // Is the context empty? System.out.println("Is the context empty? " + context.isEmpty()); // Has the context been set for the child bean? boolean result = (bean.getBeanContext()!=null); System.out.println("Does the bean have a context yet? " + result); // Number of children in the context System.out.println("Number of children in the context: " + context.size()); // Is the specific bean a member of the context? System.out.println("Is the bean a member of the context? " + context.contains(bean)); // Equality test if (bean.getBeanContext() != null) { boolean isEqual = (bean.getBeanContext()==context); // true means both references point to the same object System.out.println("Contexts are the same? " + isEqual); } System.out.println("============================================="); } }Output
:============================================= Is the context empty? true Does the bean have a context yet? false Number of children in the context: 0 Is the bean a member of the context? false ============================================= Adding bean to context... ============================================= Is the context empty? false Does the bean have a context yet? true Number of children in the context: 1 Is the bean a member of the context? true Contexts are the same? true =============================================
File:
Example2.javaimport java.beans.beancontext.*; /** * Test program that adds 100 beans to a context, * and calls size() to report the number of beans * currently nested. Finally, * this test calls toArray() to get references to * all child beans. */ public class Example2 { public static void main(String[] args) { // A BeanContext BeanContextSupport context = new BeanContextSupport(); // Many JavaBeans BeanContextChildSupport[] beans = new BeanContextChildSupport[100]; System.out.println("Number of children in the context: " + context.size()); // Create the beans and add them to the context for (int i = 0; i < beans.length; i++) { beans[i] = new BeanContextSupport(); context.add(beans[i]); } System.out.println("Number of children in the context: " + context.size()); // Context now has 100 beans in it, get references to them all Object[] children = context.toArray(); System.out.println("Number of objects retrieved from the context: " + children.length); } }
Output:
Number of children in the context: 0 Number of children in the context: 100 Number of objects retrieved from the context: 100
File:
Example3.javaimport java.beans.beancontext.*; import java.io.*; /** * An example of how to use the instantiateChild() convenience method * to create a bean automatically nested into a bean context. */ public class Example3 { public static void main(String[] args) { BeanContextSupport context = new BeanContextSupport(); System.out.println("Number of children nested into the context: " + context.size()); BeanContextChildSupport child = null; try { child = (BeanContextChildSupport)context.instantiateChild("java.beans.beancontext.BeanContextChildSupport"); } catch IOException(e){ System.out.println("IOException occurred: " + e.getMessage()); } catch(ClassNotFoundException e){ System.out.println("Class not found: " + e.getMessage()); } System.out.println("Number of children nested into the context: " + context.size()); } }Output
:Number of children nested into the context: 0 Number of children nested into the context: 1
BeanContextMembershipEvent Notification
The BeanContext API uses the standard Java event model to register listeners and deliver events.
In a basic BeanContext, the event classes and interfaces involved are:
- java.beans.beancontext.BeanContextMembershipEvent Encapsulates the list of children added to, or removed from, the membership of a particular BeanContext. An instance of this event is fired whenever a successful add(), remove(), retainAll(), removeAll(), or clear() is invoked on a given BeanContext instance.
- java.beans.BeanContextMembershipListener: Objects wishing to receive BeanContextMembershipEvents implement this interface. It defines methods void childrenAdded(BeanContextMembershipEvent bcme) and void childrenRemoved(BeanContextMembershipEvent bcme), which are called when a child is added to or removed from a given BeanContext instance.
BeanContextMembershipEvent Notification: Sample Code
File:
MembershipTest.javaimport java.beans.beancontext.*; /** * A simple test program to illustrate delivery * of the BeanContextMembershipEvent. */ public class MembershipTest { public static void main(String[] args) { BeanContextSupport context = new BeanContextSupport(); // the context MyMembershipListener listener = new MyMembershipListener(); BeanContextChildSupport bean = new BeanContextChildSupport(); // a JavaBean context.addBeanContextMembershipListener(listener); // now listening! context.add(bean); context.remove(bean); } } /** * A custom implementation of the BeanContextMembershipListener interface. */ class MyMembershipListener implements BeanContextMembershipListener { public void childrenAdded(BeanContextMembershipEvent bcme) { System.out.println("Another bean has been added to the context."); } public void childrenRemoved(BeanContextMembershipEvent bcme) { System.out.println("A bean has been removed from the context."); } }
Output:
Another bean has been added to the context. A bean has been removed from the context.
The same example, implemented using an anonymous inner class
import java.beans.beancontext.*; public class MembershipTest { public static void main(String[] args) { BeanContextSupport context = new BeanContextSupport(); context.addBeanContextMembershipListener(new BeanContextMembershipListener() { public void childrenAdded(BeanContextMembershipEvent bcme) { System.out.println("Another bean has been added to the context."); } public void childrenRemoved(BeanContextMembershipEvent bcme) { System.out.println("A bean has been removed from the context."); } }); BeanContextChildSupport bean = new BeanContextChildSupport(); context.add(bean); context.remove(bean); } }
Output:
Another bean has been added to the context. A bean has been removed from the context.
Bean Context #2: Containment and Services
As mentioned in the introduction, the BeanContext API also provides a standard mechanism through which JavaBeans can discover and utilize the services offered by their enclosing BeanContext. Service capability is defined by the BeanContextServices interface. Because this interface is a BeanContext extension, it inherits all BeanContext membership capabilities. The discovery and requesting of services can be summarized in the following steps:
- A JavaBean that implements the java.beans.beancontext.BeanContextServicesListener joins the bean context (the context itself is a BeanContextServices implementation), and registers its intent to be notified of new services via the context's addBeanContextServicesListener(BeanContextServicesListener bcsl) method.
- A java.beans.beancontext.BeanContextServiceProvider
- After being notified of the newly available service, the listening JavaBean requests an instance of the service from the context.
- The context tells the service provider to deliver the service to the requesting JavaBean.
BeanContextServices: Service Related Methods
Using a java.beans.beancontext.BeanContextServicesSupport object as the bean context, it is possible to:
- Add a service to this BeanContext:
boolean addService(java.lang.Class serviceClass, BeanContextServiceProvider serviceProvider)- Add a service to this BeanContext:
- Revoke a service:
void revokeService(java.lang.Class serviceClass, BeanContextServiceProvider serviceProvider, boolean revokeCurrentServicesNow)- Release a BeanContextChild's (or any arbitrary object associated with a BeanContextChild) reference to the specified service:
void releaseService(BeanContextChild child, java.lang.Object requestor, java.lang.Object service)- Add a BeanContextServicesListener
void addBeanContextServicesListener(BeanContextServicesListener bcsl)- Remove a BeanContextServicesListener:
void removeBeanContextServicesListener(BeanContextServicesListener bcsl)- Get the currently available services for this context:
Iterator getCurrentServiceClasses()- Determine whether or not a given service is currently available from this context:
boolean hasService(java.lang.Class serviceClass)- Get a service from the context:
Object getService(BeanContextChild child, java.lang.Object requestor, java.lang.Class serviceClass, java.lang.Object serviceSelector, BeanContextServiceRevokedListener bcsrl)- Get the list of service dependent service parameters (Service Selectors) for the specified service:
Iterator getCurrentServiceSelectors(java.lang.Class serviceClass)
Service Event Notification
JavaBeans nested into a BeanContextServices implement BeanContextServicesListener to listen for new services being added, and/or BeanContextServiceRevokedListener to listen for services being revoked.
There are two event types that may be intercepted by such listeners:
- BeanContextServiceAvailableEvent received by the BeanContextServicesListener in order to identify the service being registered.
- BeanContextServiceRevokedEvent : received by the BeanContextServiceRevokedListener in order to identify the service being revoked.
The Service Provider
JavaBeans can query their enclosing bean context for a list of available services, or ask for a specific service by name. The service itself, however, is actually delivered by a BeanContextServiceProvider. The provider can be any object that implements the java.beans.beancontext.BeanContextServiceProvider . Services become available in a context via the bean context's addService() registration method.
BeanContextServiceProvider offers the following three methods, which will be automatically called when a bean requests (or releases) a service from its context:
- Object getService(BeanContextServices bcs, java.lang.Object requestor, java.lang.Class serviceClass, java.lang.Object serviceSelector)
- Iterator getCurrentServiceSelectors(BeanContextServices bcs, java.lang.Class serviceClass)
- public void releaseService(BeanContextServices bcs, java.lang.Object requestor, java.lang.Object service) Release a service from any object that currently has a reference to it
The Service
The service itself is best described by this paragraph from the specification:
A service, represented by a Class object, is typically a reference to either an interface, or to an implementation that is not publicly instantiable. This Class defines an interface protocol or contract between a BeanContextServiceProvider, the factory of the service, and an arbitrary object associated with a BeanContextChild that is currently nested within the BeanContext the service is registered with.
The following section presents a sample application that uses a word counting service to count the number of words in a given text file.
A Word Counting Service Example
The classes defined in this sample application are:
- DocumentBean.java: A JavaBean that encapsulates a File object. Create an instance of this bean by passing it a String indicating the name of the text file to represent. This bean extends BeanContextChildSupport, which allows it to listen for addition/revocation of services in its context. When the bean detects that a WordCount service has been added to the context, it requests the service to count the number of words it contains.
- WordCountServiceProvider.java: A class that acts as the factory for delivering the WordCount service. This class implements the BeanContextServiceProvider interface.
- WordCount.java defines the service itself.
- DocumentTester.java: The main test program.
File:
DocumentBean.javaimport java.beans.beancontext.*; import java.io.*; import java.util.*; /** * A JavaBean that encapsulates a text file. When added to a bean context, * this bean listens for a WordCount service to become available. When * the service does become available, the DocumentBean requests an * instance of the service. The service then counts the number of words in the file, * and prints a report to standard output. */ private File document; private BeanContextServices context; /** * Creates a new DocumentBean given the name of the file to read from. * @param fileName the name of the file to read from */ public DocumentBean(String fileName) { document = new File(fileName); } /** * Called when this bean detects that a new service * has been registered with its context. * * @param bcsae the BeanContextServiceAvailableEvent */ public void serviceAvailable(BeanContextServiceAvailableEvent bcsae) { System.out.println("[Detected a service being added to the context]"); // Get a reference to the context BeanContextServices context = bcsae.getSourceAsBeanContextServices(); System.out.println("Is the context offering a WordCount service? " + context.hasService(WordCount.class)); // Use the service, if it's available if (context.hasService(WordCount.class)) { System.out.println("Attempting to use the service..."); try { WordCount service = (WordCount)context.getService(this, this, WordCount.class, document, this); System.out.println("Got the service!"); service.countWords(); } catch(Exception e) { } } } /** * Called when this bean detects that a service * has been revoked from the context. * * @param bcsre the BeanContextServiceRevokedEvent */ public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) { System.out.println("[Detected a service being revoked from the context]"); } }
File:
WordCountServiceProvider.javaimport java.beans.beancontext.*; import java.util.*; import java.io.*; /** * This class is the factory that delivers a word counting service. * The 3 methods defined in this class are the concrete implementations * of the BeanContextServiceProvider interface. For this demonstration, the primary * method of interest is getService(). The getService() methods returns a new * WordCount instance. It is called by the bean context when a nested * JavaBean requests the service. */ public Object getService(BeanContextServices bcs, Object requestor, Class serviceClass, Object serviceSelector) { // For this demo, we know that the cast from serviceSelector // to File will always work. /* Return an instance of the service. The service itself is * the WordCount interface, which is implemented here using * an anonymous inner class. */ return new WordCount() { public void countWords() { try { // Create a Reader to the DocumentBean's File BufferedReader br = new BufferedReader(new FileReader(document)); String line = null; int wordCount = 0; while ((line = br.readLine()) != null) { StringTokenizer st = new StringTokenizer(line); while (st.hasMoreTokens()) { System.out.println("Word " + (++wordCount) + " is: " + st.nextToken()); } } System.out.println("Total number of words in the document: " + wordCount); System.out.println("[WordCount service brought to you by WordCountServiceProvider]"); br.close(); } catch(Exception e) { } } }; } public void releaseService(BeanContextServices bcs, Object requestor, Object service) { // do nothing } public Iterator getCurrentServiceSelectors(BeanContextServices bcs, Class serviceClass) { return null; // do nothing } }
File:
WordCount.java/** * The WordCount service. Implementations of the * countWords() method are provided by the * WordCountServiceProvider class. */ public interface WordCount { /** * Counts the number of words in the file. */ public abstract void countWords(); }
File:
DocumentTester.javaimport java.beans.beancontext.*; import java.util.*; /** * A test program that creates all of the objects, * a tests the service capabilities. Run this program * from the command line using java DocumentTester */ public class DocumentTester { public static void main(String[] args) { BeanContextServicesSupport context = new BeanContextServicesSupport(); // a bean context DocumentBean doc1 = new DocumentBean("Test.txt"); context.add(doc1); context.addBeanContextServicesListener(doc1); // listen for new services WordCountServiceProvider provider = new WordCountServiceProvider(); context.addService(WordCount.class, provider); // add the service to the context } }
File:
Test.txtThis text will be analyzed by the WordCount service.
Output:
[Detected a service being added to the context] Is the context offering a WordCount service? true Attempting to use the service... Got the service! Word 1 is: This Word 2 is: text Word 3 is: will Word 4 is: be Word 5 is: analyzed Word 6 is: by Word 7 is: the Word 8 is: WordCount Word 9 is: service. Total number of words in the document: 9 [WordCount service brought to you by WordCountServiceProvider]
AWT Containers and the BeanContextProxy Interface
Sometimes, it is desirable for an AWT Container to act as a BeanContext. Such an example is the BeanBox, which ships with the BDK. However, AWT Containers cannot implement the BeanContext interface directly, because of a method name collision between Component and Collection. If some AWT Component needs to act as a BeanContext, it must internally create a BeanContext instance and delegate work to it. Third parties, such as visual builder tools, can discover this BeanContext instance if the Component implements the BeanContextProxy interface.
The BeanContextProxy Interface
public BeanContextChild getBeanContextProxy() - Gets the BeanContextChild (or subinterface) associated with this object.
![]()