Using BeanExtender's Dipping Technique

by Otto Fox, Component Technology,
IBM Software Solutions Division



Abstract

IBM's BeanExtender introduces the dipping technique, which provides software component assemblers with a new method for dynamically changing JavaBeans behavior. This article elaborates on the dipping technique, how dip code may be written, and how dips can be used with BeanExtender.


Introduction

Software component technology is on the brink of maturity. The idea of building software from "off-the-shelf" components is nothing new, but JavaBeans [1] are making it a more common practice, and, as a result, new software component building techniques are being developed.

The metaphor of "wiring" components together is now widely used. In the wiring metaphor, software components are connected together the way stereo components are. In stereo systems, the tuner is connected to the amplifier, and the amplifier is connected to the speakers. Software components are not tuners, speakers, and amplifiers, but buttons, text fields, and animations. For example, JavaSoft's JavaBeans Development Kit (BDK) comes with a builder environment called the BeanBox. The BeanBox uses lines called "wires" to connect JavaBeans. When a wire connects two JavaBeans, the BeanBox presents a dialog box that allows you to further specify the connection. When the newly created JavaBean are run, information flows across this wire between the JavaBeans and allows them to cooperate. Sun's Java Studio also uses the wiring metaphor to connect software components.

Wiring is a useful metaphor for connecting separate components, which each has some content and some responsibility to fulfill. But sometimes you want to modify a component. Perhaps you want a digital display on a tuner instead of an analog one. Or maybe you'd like a lock on your stereo. Similar changes are desired in the software component world. These kinds of operations don't lend themselves well to the component-wiring metaphor. So BeanExtender has introduced a new metaphor: dipping. Dipping lets you add new "flavors" to the JavaBeans or software component [2]. A dip is a special kind of JavaBean that can be hooked on to another JavaBean. The dip is the new feature you want to add to the component. In the stereo analogy, the digital display would be a dip for the tuner JavaBeans. Software examples of dips include printing and security.

As more applications are developed from software components, developers will find they have some off-the-shelf component, some JavaBeans, that fit their purposes but lack some elements. They will want to add some new function to these Javabeans without having to muck around with the JavaBeans' code. Dipping is the answer to this problem. In order to make dipping possible, the developer must first make the Javabeans extensible. This process is called morphing. Javabeans that have been extended to accept dips, or morphed, is called dippable JavaBeans. Almost any JavaBean or class can be made dippable [3]. Dippable Javbeans can have one or more dips connected to it. Each dip can add new behavior or modify the JavaBeans' existing behavior. The purpose of this article is to survey the dips BeanExtender ships as examples and to describe how to create a dip.

During analysis and design of JavaBeans, you examine functions for particular JavaBeans. If a function is general enough for other JavaBeans to use, then the function is considered generalizable. If the function responds to or is activated by specific state changes [4] in the JavaBeans, then it are integral to the JavaBeans. If a function is both generalizable to other JavaBeans and integral to the Javabeans you are considering, then the function is well suited for dipping.
  • Integral
  • Non-Integral
  • Generalizable
  • printing
  • cut/paste text
  • Program-Specific
  • word wrapping in a word processor
  • cut/paste program-specific data (for example, .gif image)
For example, printing is a function that is generalizable. Any JavaBeans that are visible at run time may have printing requirements. Printing is also integral to JavaBeans; the print function is logically a part of the object to be printed and is usually activated by a user event. Hence printing is an ideal candidate for dipping, and it is one of the sample dips described in this article and provided by BeanExtender.


A Survey of the Dip Samples

When BeanExtender introduces dipping, it also presents sample dips. These dips not only show a variety of functions, but also show some design options you can use when you compose your own dips. The samples show dips that work in client-server relationships, dips that disable behavior based on certain criteria, and dips that add new function; these samples also show a variety of other features.

In order to create a dip, you must implement the dip interface, which allows the dip to interact with the dipping framework and, through the dipping framework, with the dippable JavaBeans.

The Dip Interface

The dip interface establishes a protocol for interaction between dips and the dip framework. The dip framework coordinates the dip's interaction with the dippable JavaBeans and with other dips; it allows dips to modify the JavaBeans' behavior. The dip interface defines the dip's manner of cooperation with the dip framework. The dip must implement methods for this interaction that include worksWith, isRemovable, and typeName. The worksWith method allows a dip applied to dippable JavaBeans to accept or reject the addition of new dips to the dippable JavaBeans. The isRemovable method allows the dip to make itself "permanently attached." The typeName method returns an identifying string, which is how other BeanExtender tools identify your dip.

The dip interface also establishes a protocol for interaction between dips and the dippable Javabeans. The dip implements methods for communication that include createPropertyChangeListener and createVetoablePropertyChangeListener. The "vetoable" listener method is called by the dip framework before the property change call is made to the dippable JavaBeans, so if you want the dip to do something before a property changes, put that function here. If you want to add function to JavaBeans after a property change occurs, use the createPropertyChangeListener. There are methods with similar signatures for methodCall and eventFire. One note about createVetoableMethodCallListener: you can't intercept constructors and you can't veto calls to readObject(). This is because the dip and the dip framework are initialized during these calls and are not able to interact with the JavaBeans until their initialization is complete. All the methods for the dip interface are included in the appendix.


Simple Dip

As you can see from the description of the dip interface, quite a bit of infrastructure must be established for a dip to work. BeanExtender includes an abstract implementation of this interface called SimpleDip. If you subclass SimpleDip, you needn't bother with methods in the interface for which a default value is acceptable. If the priority of your dip's processing in relation to other dips is unimportant to you, take the default value provided by SimpleDip. If your dip only hooks events, you needn't bother with the methods' and properties' changed listeners; SimpleDip takes care of their returning null. Subclassing from SimpleDip provides the minimum interface necessary for dipping.


Session Authentication

This dip allows the user to password-protect access to a JavaBeans. The design uses a principal-and-authenticator scheme. The principal is analogous to a mechanical lock's key. The authenticator is the code that verifies the principal; it is like the mechanical lock. In this scheme, the authenticator verifies the principal when it receives a principal query. BeanExtender's sample session-authentication dip provides the dip and the related classes as well as a sample principal, a sample authenticator, and a sample principal query. This sample uses a user ID and password that are set when the dip is applied to the JavaBeans. This, of course, is not suitable for a real session authenticator, since the user must control the password. However, this dip can be made commercial-grade by revising the sample authenticator, principal, and principal query to use a more robust protocol.

The SessionAuthenticationDip works by hooking all methods, properties, and events as a vetoable listener. A vetoable listener is a Java object that is notified by the Java event mechanism before the event is passed to the target JavaBeans. A Java object acting as a vetoable listener can reject the change event. When the listener vetoes the event, the target does not receive the event. In the SessionAuthenticationDip, when calls are made to the JavaBeans, the listeners query the authenticator to see if the principal is valid. If the principal is valid, the dip does nothing. If the principal is invalid, the listener vetoes the method call, event fire, or property change.


Trace

This dip allows a user to log event firings, property changes, and method calls on a dipped Javabeans. When this dip is applied, the customizer allows the user to set traces on the properties, events, or methods in any combination.

The TraceDip sends its output to a message display utility called MsgDisp. MsgDisp is partitioned into a client and server piece and allows the output from the JavaBeans being traced to be viewed on a remote machine. You can specify for the host to view the trace log when you customize the trace dip. MsgDisp uses java.net.* package for communicating between the trace JavaBeans and the remote trace viewer.


Print

PrintDip allows the dipped JavaBeans to be printed. This dip also shows another form of interaction with the dippable JavaBeans: that of rejection. A dip can establish required criteria for JavaBeans before the dip can be applied. The print dip will only "adhere" to JavaBeans that are visible at run time. If PrintDip is applied to JavaBeans not visible at run time, PrintDip throws an exception and will not connect to the JavaBeans. The dip determines visibility by finding out if the dippable JavaBeans are descendents of java.awt.Component. If it is not, the dip throws a com.ibm.beans.dip.DipRejectAdditionException. This exception does not bring but is caught by the dipping framework, which informs the user that the dip is rejected.

The print dip has a visible and invisible mode. The invisible mode provides no default user interface, so the application assembler must wire an event from the dippable JavaBeans to the dip in order to invoke printing. The visible mode provides a floating print button. The user presses this button to invoke the standard print dialog box for the platform on which the Javabeans are running.

The print method for the PrintDip is printDippableBean(). This method uses a class in COM.ibm.beans.util, which in turn uses the print facilities in the java.awt.Toolkit. The printDippableBean() is the method that an application developer hooks onto if the dip is applied in invisible mode. This is also the method called when the print button is pushed in visible mode.


Print License

This sample shows how to restrict access to the print function of the JavaBeans. Printing is often disabled in the free shareware version of a product to encourage purchasing of the software. This dip can be used as a sample to demonstrate how to manage access to other software functions based on a license.

Any software license arrangement involves a developer, a license issuer, and a user. The developer creates the software and disables or enables functions based on a license. The license issuer creates a license that allows a user to enable function in the software. The License Dip uses java.security.Signature and related classes to create a public/private key pair. The license issuer keeps the private key to authenticate the user's public key. The print dip listens for calls to methods print() and printAll(). The licensing is checked and the call to these methods is vetoed if the license is not found or is invalid.

The PrintLicenseDip sample also provides a stand-alone applet for license generation. This is the applet that would be used by a license issuer to generate licenses for the dipped JavaBeans.


Context

Context, in common usage, is the set of circumstances and facts that surround something and give it meaning. Software contexts are relationships between a piece of data or program and the programmers and users. JavaBeans have different users and they change over time. All these facts necessitate context dips. These dips capture information relevant to the context of the JavaBeans. The context that is important for an individual JavaBean is set by the user when the dip is customized. The context dips use properties to determine when context changes. The three context dips are Version, Author, and Annotation. The VersionContextDip captures previous values of a property when the property changes. The AuthorContextDip prompts the user for author information as a specified property changes. The AnnotationContextDip allows the user to affix annotations to JavaBeans.


Details of The Annotation Dip

In order to give more detail about how a dip is created, the following details are provided to describe what the AnnotationContextDip classes do and why.


AnnotationContextDip

public final class AnnotationContextDip implements Dip,

ContextElement, Serializable, DipPropertyChangeListener

Two things establish the AnnotationContextDip identity as a dip. First, it implements the Dip interface. This is a programming requirement. Second, it follows the pattern Dip. This allows BeanExtender tools to recognize it as a dip.

As you can see from the class signature, AnnotationContextDip implements java.io.Serializable. This is a requirement of all JavaBeans [5], and dips are JavaBeans, too.

Since the context dips use property changes to determine context change, AnnotationContextDip is a DipPropertyChangeListener. This provides a protocol for communication between the dipping framework and the dip as the dippable JavaBean changes.

The ContextElement interface defines the protocol for accessing the elements common to all context dips. These elements include context data, which is the information that describes the context, and ContextUpdatePolicy, which describes the conditions that constitute a significant context change. In these dips, these would be the properties that the user indicates as being significant.

AnnotationContextDip implements the dip interface to provide APIs so that the dip can communicate with the dip framework and, through the framework, with the dippable JavaBeans. The context dips are a cooperative set of dips. They will work with other dips, they can be removed, and they don't ask for any special priority. In the method createPropertyChangeListener, the dip creates a list of pointers to itself for each property change method on the dippable JavaBeans. When the dipping framework intercepts a property change call on the dippable Javabeans, it will call the AnnotationContextDip and let the dip decide if the property change is significant to the context of the JavaBeans. This is done in the propertyChange() method.


AnnotationDialog

public class AnnotationDialog extends Dialog implements WindowListener, ActionListener

If the propertyChange() method determines that the context has changed, the user is presented with a dialog box. This dialog box has a text field and buttons so that the user can set the notes when the property changes. The dialog box implements ActionListener so that it can respond to the buttons pushed. The OK button sets the text field contents as the annotation associated with that context change. A view button is also presented. This button causes another dialog box to be presented and shows the user past annotations on the JavaBeans. The cancel button disposes of the dialog box without setting any annotation text.


AnnotationDipCustomizer

public class AnnotationContextCustomizer extends Panel implements Customizer, ActionListener

The customizer is a user interface for JavaBeans. The AnnotationContextCustomizer presents the user with a list of all the properties on the dippable JavaBeans. The user selects the properties in which changes are significant enough to require an Annotation update. The ActionListener interface is implemented to detect when the user presses the OK button. When the OK button is pressed, the ContextUpdatePolicy (the object that keeps track of what constitutes a context change) can be updated. Because the customizer interacts with the ContextUpdatePolicy, some coordination is required between these two objects. The myReadyFlag is a Boolean provided to keep the customizer from trying to reference the policy options before the dippable JavaBeans are accessible. The policy options are created by introspection on the dippable JavaBeans, and the customizer can be created before the dippable JavaBeans are accessible.


Conclusion

The use of software components can greatly increase software flexibility and reuse. In order to gain this flexibility and reusability, we need not only a rich set of components, but also a variety of connection techniques. This paper introduces a new connection technique: dipping. There are numerous applications for dips in component technology, and only a few are mentioned here.


Explore Some More

You can explore BeanExtender in greater detail by downloading the source from IBM's alphaWorks Web site. Good documentation is available, including JavaDoc, a Guide to Features, and a Guide to Demos. CommunityXchange allows you to contribute feedback and communicate with the developers.


Footnotes

  1. Sun Microsystems, "A JavaBean Is a Reusable Software Component That Can Be Manipulated Visually in a Builder Tool," JavaBeans API Specification, Version 1.01, 1997, p.9.

  2. Henri Jubin, JavaBeans by Example, Prentice-Hall, 1997.

  3. The few exceptions are described in the BeanExtender Guide to Features, "The Dipping Framework " chapter, in the section "Creating a Dippable JavaBean."

  4. "A state is an abstraction of the attribute values and links of an object....A state specifies the response of an object to input events." Rumbaugh, et. al., Object Oriented Modeling and Design, Prentice-Hall, 1991, p. 87.

  5. Sun Microsystems, JavaBeans API Specification, Version 1.01, 1997, p.22.


Appendix

A complete listing can be found at IBM's alphaWorks Web site.
COM.ibm.beans.dip.Dip interface 
package COM.ibm.beans.dip;
import java.io.Serializable;
public interface Dip extends Serializable {
 /**
  * Gets the priority of this dip in relation to all other
  * dips.  Priority determines the execution order of the dips applied
  * to dippable Javabeans implemented with DippableSupportImp.  
  * ...
  */
  int priority();
 /**
  * Gets the dip type name as a String.
  * The type name may not be the class name of the dip.
  * ...
  */
  String typeName();
 /**
  * Indicates if another dip can work with this dip.  Once this
  * dip is applied to JavaBeans, this dip can reject
  * any subsequent dips asking to be applied to the same JavaBeans.
  * ...
  */
  boolean worksWith(String dipTypeName);
  boolean isRemovable();
  Object createImplementation(Object obj) throws DipRejectAdditionException;
  void releaseImplementation();
  DipPropertyChangeListener[] createPropertyChangeListener(Object imp, String[] methods);
  DipVetoableChangeListener[] createVetoableChangeListener(Object imp, String[] methods);
  DipMethodCallListener[] createMethodCallListener(Object imp, String[] methods);
  DipVetoableMethodListener[] createVetoableMethodListener(Object imp, String[] methods);
  DipEventFireListener[] createEventFireListener(Object imp, String[] methods);
  DipVetoableEventListener[] createVetoableEventListener(Object imp, String[] methods);
}
COM.ibm.beans.dips.context.AnnotationContextDip class
package COM.ibm.beans.dips.context;
import COM.ibm.beans.dip.Dip;
import COM.ibm.beans.dip.Dippable;
import COM.ibm.beans.dip.DipRejectAdditionException;
import COM.ibm.beans.dip.DipPropertyChangeListener;
import COM.ibm.beans.dip.DipVetoableChangeListener;
import COM.ibm.beans.dip.DipMethodCallListener;
import COM.ibm.beans.dip.DipVetoableMethodListener;
import COM.ibm.beans.dip.DipEventFireListener;
import COM.ibm.beans.dip.DipVetoableEventListener;
import java.io.Serializable;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
import java.util.Vector;
import java.lang.reflect.Method;
import java.awt.Frame;
import java.beans.PropertyChangeEvent;
/**
 * The AnnotationContext class is a dip that allows the user to add notes to 
 * an instance of JavaBeans. These notes are restricted to text. 
 * 

* Each annotation allows multiple text notes to a single JavaBean instance. */ public final class AnnotationContextDip implements Dip, ContextElement, Serializable, DipPropertyChangeListener { /** * Constructs an AnnotationContext instance. */ public AnnotationContextDip () { myUpdatePolicy=new ContextUpdatePolicy(); myAnnotations=new Vector(); } /** * Gets the dip type name. * @return The type name of the dip. */ public final String typeName() { String x=new String(getMessage("AnnotationDip")); return x; } /** * Gets the priority for this dip. * Returns An integer value 500, to indicate average priority. */ public int priority() { return 500; } /** * Determines whether this dip works with another * dip specified in the dipTypename parameter. The annotation * dip will not prohibit the addition of any dip. *... */ public boolean worksWith(String dipTypeName) { return true; } /** * Determines whether this dip is removable. This dip * always returns true, because the annotation dip is removable. * ... */ public boolean isRemovable() { return true; } /** * Creates an implementation of the dip. * For the annotation context, createImplementation returns a pointer * to itself. * ... */ public Object createImplementation(Object obj) throws DipRejectAdditionException { myDip = (Dippable)obj; myUpdatePolicy.initPolicyOptions(obj); return this; } /** * Returns an array of PropertyChangeListeners instances describing the method to * call after the dipped JavaBeans property change method is called. * The annotation context dip listens for property change events itself. * ... */ public DipPropertyChangeListener[] createPropertyChangeListener(Object imp, String[] methods) { DipPropertyChangeListener[] returnArray = new DipPropertyChangeListener[methods.length]; for (int i=0; i<methods.length; i++) { returnArray[i] = this; } return returnArray; } /** * Satisfies the DipPropertyChangeListener interface. * ... */ public Dip retrieveDip() { return this; } /** * Checks to see if the property changed is in the * context update policy. If it is, the method invokes an annotation Dialog. * The annotation dialog allows the user to attach notes to instances of * JavaBeans. * ... */ public synchronized void propertyChange(PropertyChangeEvent evt) { //Compare the event with UpdatePolicy String[] policyNames=myUpdatePolicy.getUpdatePolicy(); boolean match=false; for (int i=0; i


About the Author

Otto Fox Otto Fox, a software engineer at IBM, has been working in the computer industry since 1984 and at IBM since 1988. He holds an MS in computer science from the University of Texas at Dallas. Mr. Fox can be reached at
ofox@us.ibm.com



Java is a trademark of Sun Microsystems, Inc.

Other companies, products, and service names may be trademarks or service marks of others.

Copyright    Trademark



  Java Feature Java Home  
IBM HomeOrderEmployment