Articles Index
JavaBeansTM Technology: Unlocking The BeanContext API
By Onno Kluyt
Since the introduction of the JavaBeansTM component architecture in the
JavaTM Development Kit v 1.1 (JDKTM) release in February 1997, additional
specifications have been added to this component architecture such as the
Extensible Runtime Containment and Services Protocol and the InfoBus standard
extension. Both of these additions provide APIs that enable Beans to interconnect
during run-time. Read on to learn more about the Extensible Runtime Containment
and Services Protocol API (BeanContext, for short) and see how this API is
implemented in the BeanBox in the Beans Development Kit (BDK) 1.1 and how you can
develop dynamically connecting Beans.
Extensible Runtime Containment and Services Protocol
The JavaBeans architecture sets rules for what makes a Java class a reusable software
component (Bean, for short). It defines how, via reflection and introspection, the
capabilities of a Bean are discovered. Such capabilities can be a Bean's properties,
the events a Bean fires or the public methods of a Bean. This information is used
by a development tool to provide a visual programming environment wherein the
developer interconnects Beans, wiring one Bean's events to another Bean's public
methods. The resulting set of connected components can be compiled into an
applet or application and delivered to the end user. Thus the connections between
the Beans and the customization of their properties occur during what is called
design-time. The result of this process is experienced by the end user during run-time.
The BeanContext API adds much more flexibility to this scenario. It enables a
Bean to interrogate its environment for certain capabilities and available services.
This allows the Bean to dynamically adjust its behavior to the container or context
in which it finds itself.
The BeanContext API is a core API in the Java 2 Standard Edition SDK (J2SDK). The API consists
of two parts: 1) logical containment hierarchy for JavaBeans components and publishing
and 2) discovery of services provided by Beans within such a hierarchy.
Logical Containment
The containment hierarchy for JavaBeans components enables grouping of
Beans in a logical and traversable manner. This grouping is established
through the use of a BeanContext container. This container
is different from an Abstract Window Toolkit (AWT) or Java Foundation
Classes (JFC) container. The BeanContext container will have
a functional or logical meaning, seldom a direct relation to how the
components appear on the screen. A BeanContext container
can of course contain other BeanContexts thus allowing for
any arbitrary grouping of components. An example of logical containment
of Beans is a document management system. The system may consist of
directories and documents. A document may consist of sub-documents.
The API can be used to group the sub-documents together into a
BeanContext container. This container represents the document.
Several documents can be placed in another BeanContext container,
the directory.
BeanContext containers use the Collections API to expose their
structure. Therefore rules that apply for the Collections framework also apply for a
BeanContext framework. In particular, the add() and
remove() methods of a BeanContext must follow
certain semantics. The ones of interest in this article are:
BeanContext
containers are synchronized on the
BeanContext.globalHierarchyLock.
-
Each child object appears only once in the set of children
for that
BeanContext.
-
Each child added to the set remains a part of that set until
removed via the
remove(), removeAll(),
retainAll()
or
clear()
methods.
If a bean is to utilize the BeanContext in which it is contained,
it must implement the BeanContextChild interface. If
BeanContext containers are to be nested, the
BeanContextChild should implement the
BeanContext interface as well.

Fig 1. BeanContexts, Services, and Beans
Services
The Services API of the Extensible Runtime Containment and Services Protocol
gives JavaBeans components a standard mechanism to discover which services other
Beans may provide and to connect to these Beans to make use of those services.
Services are typically interface or abstract-class based. A particular service
provider can implement the service in a variety of ways while maintaining the
same interface signature. Examples of services are spell checking or debugging.
Another example would be a service provided by a BeanContext API that
controls the locale setting for the Beans within the BeanContext
container.
Beans Development Kit 1.1
BDK 1.1 contains an updated version of the BeanBox and some new
sample Beans. The BeanBox is both a BeanContextcontainer and
a BeanContext ServiceProvider. The BeanBox provides a simple
mechanism for propagating its execution environment to its contained
Bean:
-
Design-time, run-time toggle.
The BeanBox also provides two services:
-
An InfoBus service (in preparation for the upcoming InfoBus 2.0).
-
A method tracing service, discussed here in detail.
The BeanBox publishes the method tracing service. The Juggler Bean looks
for this service and uses it to report on certain methods. The Juggler
connects during instantiation to the method tracing service. Then methods
such as paint(), startJuggling() and
stopJuggling() use the service to post a message in the Method
Tracing Window.
The BeanBox publishes the run-time/design-time toggle as a BeanContext
service. When design-time is disabled, the method tracing service is suspended.

Fig 2. The Method Tracing Service in the BeanBox.
(Click on the above image for a full-sized version of the image.)
The BeanBox as a BeanContext container.
In BeanBox.java the BeanBox class implements the BeanContextProxy
interface, or the BeanContextServicesSupport helper class. The
method tracing is contained in the MethodTracer class:
transient
java.beans.beancontext.BeanContextServicesSupport
bcss = new
java.beans.beancontext.BeanContextServicesSupport();
private final sunw.demo.methodtracer.MethodTracer mt =
new sunw.demo.methodtracer.MethodTracer();
The bcss property is the BeanContext proxy:
public BeanContextChild getBeanContextProxy() {
return bcss;
}
When a Bean gets placed in the BeanBox it is added to the
BeanContextcontainer:
bcss.add(bean);
Apart from the services it provides, the BeanBox also localizes the notion of
design-time or run-time to the BeanContext container. Although at the
moment the BeanBox has only one BeanContext, one could use
this in the case of multiple BeanContexts to allow some to be in
design-time and others to be in run-time. So if the Bean knows about design time and run
time, the setting of the BeanContext is forced on the Bean:
if (bean instanceof java.beans.DesignMode) {
java.beans.DesignMode bdm =
(java.beans.DesignMode)bean;
boolean bcdmode =
(boolean)(
(DesignMode)getBeanContextProxy().isDesignTime();
bdm.setDesignTime(bcdmode);
}
It gets more interesting when we look at the implementation of the BeanBox
as a BeanContextServiceProvider. In the BeanBox constructor an
anonymous inner class is used to implement the basics:
public BeanBox() {
....
bcss.addService(
sunw.demo.methodtracer.MethodTracer.class,
new BeanContextServiceProvider() {
public Object getService(
BeanContextServices bcs,
Object requestor,
Class serviceClass,
Object serviceSelector) {
mt.logText("Method Tracing service
requested by:\n " +
requestor.getClass().toString());
return mt;
}
public void releaseService(
BeanContextServices bcs,
Object requestor,
Object service) {
mt.logText("Method Tracing service
released by\n " +
requestor.getClass().toString());
}
public Iterator getCurrentServiceSelectors(
BeanContextServices bcs,
Class serviceClass) {
return null;
}
});
}
The service itself is implemented in MethodTracer.java:
public final class MethodTracer
extends Frame implements PropertChangeListener,
DesignMode {
...
public void traceMethod() {
try {
StringWriter sw = new StringWriter();
(new Exception()).printStackTrace(
new PrintWriter(sw));
StringReader sr =
new StringReader(sw.getBuffer().toString());
BufferedReader br = new BufferedReader(sr);
br.readLine();
br.readLine(); // we discard the first 2 lines
String line = br.readLine();
line = line.substring(" at".length());
line = line.substring(0, line.indexOf('('));
ta.append(line);
ta.append("\n");
} catch (Exception e) {
e.printStackTrace();
}
}
The method tracing service is only active and visible while the
BeanContext is in design time. This is controlled
by adding it as a PropertyChangeListener to the
BeanContextServiceProvider in BeanBox's constructor.
public void propertyChange(
PropertyChangeEvent evt) {
if (evt.propertyName().equals("designMode")) {
boolean dmode =
((Boolean)evt.getNewValue().booleanValue();
setDesignTime(dmode);
}
}
public void setDesignTime(boolean dmode) {
if (this.isVisible() != dmode) setVisible(dmode);
}
public boolean isDesignTime() {
return isVisible();
}
The Juggler Bean uses the BeanContext provided by the BeanBox
to request the method tracing service. To do this, the Juggler is created as a
BeanContextChild, which will attach itself as a listener for
BeanContextServices events so that it stays abreast of when the
service is available and when it is being revoked. The Juggler uses the
BeanContextChildSupport helper class to do most of the work.
It also monitors the designTime toggle to suspend
usage of the service during run time.
public class Juggler extends Applet implements
Runnable,
BeanContextProxy,
BeanContextServicesListener,
PropertyChangeListener,
DesignMode {
private BeanContextChildSupport bccs =
new BeanContextChildSupport() {
protected void initializeBeanContextResources() {
try {
BeanContextServices bcs =
(BeanContextServices)bccs.getBeanContext();
if (bcs.hasService(MethodTracer.class) {
mtService =
(MethodTracer)bcs.getService(
getBeanContextProxy(),
Juggler.this,
MethodTracer.class,
null,
Juggler.this);
}else{
bcs.addBeanContextServicesListener(
Juggler.this);
}
bcs.addPropertyChangeListener(
"designMode", Juggler.this);
} catch (ClassCastException ex) {
// Nesting BeanContext is not a
// BeanContextServices so do nothing
} catch (Exception e) {
System.err.println("Error
initializing BeanContext resources.");
System.err.println(e);
}
}
protected void releaseBeanContextServices() {
if (mtService != null) {
mtService = mt = null;
}
try {
BeanContextServices bcs =
(BeanContextServices)getBeanContext();
bcs.removePropertyChangeListener("designMode",
Juggler.this);
bcs.removeBeanContextServicesListener(
Juggler.this);
} catch (Exception e) {
}
}
};
The java.beans.Beans.instantiate() method was amended in
Java 2 to provide support for BeanContext API. Using this
method and passing in a reference to the BeanContext as
a third parameter leads to the instantiate() method invoking
BeanContextSupport.add() as a side effect. That method will
then invoke Juggler.getBeanContextProxy().setBeanContext()
which finally leads to initializeBeanContextResources() on
Juggler's BeanContextChildSupport field. Note that because
initialization of BeanContext resources is invoked from the
nesting BeanContext container, the Juggler applet will still run
outside of the BeanBox, where these services or the BeanContext
itself may not exist.
The Juggler's startJuggling() method is one of the methods that
uses the method tracing service:
public void startJuggling() {
if (mt != null) {
mt.traceMethod();
}
....
}
Finally, the Juggler implements the BeanContextServices listener
interface as follows:
public void serviceRevoked(
BeanContextServiceRevokedEvent event) {
System.err.println(
"Method Tracing service revoked");
setDebug(false);
mtService =null;
}
public void serviceAvailable(
BeanContextServiceAvailableEvent event) {
if (event.getServiceClass() == MethodTracer.class) {
try {
mtService = (MethodTracer)
event.getSourceAsBeanContextServices().
getService(getBeanContextProxy(),
this,
MethodTracer.class,
null, this);
} catch (Exception e) {
System.err.println(e);
}
}
}
Summary
In BDK 1.1, the use of the BeanContext API is illustrated.
The BeanBox itself is a BeanContext container and provides
a BeanContextService which is used by the Juggler Bean. The
source code for the BeanBox and the sample Beans is included in the BDK.
To fully understand this protocol you may want to read up on the BDK specification.
Note:
BDK 1.1 requires Java 2 Standard Edition SDK (formerly JDK 1.2), which can be downloaded from java.sun.com
http://java.sun.com/products/jdk/1.2/.
Please email any feedback or questions to
java-beans@eng.sun.com.
About the Author
Onno Kluyt began his career writing software for PCs and Macintoshes.
Nowadays he fills his time as product manager for JavaBeans Technology
and JFC.
Reader Feedback
Tell us what you think of this article.