JavaBeans Technology: Unlocking The BeanContext API

   
   

Articles Index

Since the introduction of the JavaBeans component architecture in the Java Development Kit v 1.1 (JDK) 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.
BeanContextBeanContextChildBeanContextBeanContextChildBeanContext

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.

 

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();

BeanContext
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) {
      }
    }
  };

java.beans.Beans.instantiate()BeanContextBeanContextinstantiate()BeanContextSupport.add()Juggler.getBeanContextProxy().setBeanContext()initializeBeanContextResources()BeanContextChildSupportBeanContextBeanContextBeanContext

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.

  •  

  • The BDK specification:
    Glasgow.
  •  

  • The main JavaBeans web page:
    link.

Note:
BDK 1.1 requires Java 2 Standard Edition SDK (formerly JDK 1.2), which can be downloaded from here.

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.