| 1.124 Lecture 22 | 11/28/2000 |
Software components must, in general, adopt standard techniques for interacting with the rest of the world. For example, all GUI components inherit the java.awt.Component class, which means that one can rely on them to have certain standard methods like paint(), setSize(), etc. Java Beans are not actually required to inherit a particular base class or implement a particular interface. However, they do provide support for some or all of the following key features
Now that the program has been designed, you can run it within the BeanBox. Simply press the start button to start juggling and press the stop button to stop juggling. If you wish, you can turn your program into an applet by choosing File | MakeApplet in the BeanBox. This will automatically generate a complete set of files for the applet, which can be run in the appletviewer. (Do not expect current versions of Netscape and Internet Explorer to work with this applet.)
Let's take a closer look at how the BeanBox works. On start up, it scans the directory beans/jars for files with the .jar extension that contain Java Beans. These beans are displayed in the ToolBox window, from where they can be selected and dropped into the BeanBox window. Next, we edited the labels of the two instances of OurButton. The BeanBox determined that OurButton has a member named label by looking for setter and getter methods that follow standard naming conventions called design patterns. If you look at the source code in beans\demo\sunw\demo\buttons\OurButton.java, you will see that OurButton has two methods named
public void setLabel(String newLabel) {
...
}
public String getLabel() {
...
}
Design patterns are an implicit technique by which builder tools can introspect a Java Bean. There is also an explicit technique for exposing properties, methods and events. This involves writing a bean information class, which implements the BeanInfo interface.
When we wired the start button to the juggler, the BeanBox set up the juggler to respond to action events generated by the start button. The BeanBox again used design patterns to determine the type of events that can be generated by an OurButton object. The following design patterns indicate that OurButton is capable of firing ActionEvents.
public synchronized void addActionListener(ActionListener
l) {
...
}
public synchronized void removeActionListener(ActionListener
l) {
...
}
By choosing Edit | Events | action | actionPerformed to connect the start button to the juggler, we were really registering an ActionListener with the start button. The Juggler bean itself is does not implement the ActionListener interface. Instead the BeanBox generated an event hookup adaptor, which implements ActionListener and simply calls the juggler's startJuggling method in its actionPerformed method:
// Automatically generated event hookup file.
package tmp.sunw.beanbox;
import sunw.demo.juggler.Juggler;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ___Hookup_1474c0159e implements
java.awt.event.ActionListener,
java.io.Serializable {
public void setTarget(sunw.demo.juggler.Juggler
t) {
target = t;
}
public void actionPerformed(java.awt.event.ActionEvent
arg0) {
target.startJuggling(arg0);
}
private sunw.demo.juggler.Juggler
target;
}
A similar event hookup adaptor was generated when we wired the stop button to the juggler's stopJuggling method.
Why not make Juggler implement the ActionListener interface
directly? This is mainly a matter of convenience. Suppose that
Juggler
implemented ActionListener and that it was registered to receive
ActionEvents
from both the start button and the stop button. Then the Juggler's
actionPerformed
method would need to examine incoming events to determine the event source,
before it could know whether to call startJuggling or stopJuggling.
SimpleBean.java
import java.awt.*;
import java.io.Serializable;
public class SimpleBean extends Canvas implements
Serializable {
//Constructor sets inherited
properties
public SimpleBean() {
setSize(60,40);
setBackground(Color.red);
}
}
Since this class extends a GUI component, java.awt.Canvas, it will be a visible Java Bean. Java Beans may also be invisible.
Now the Java Bean must be compiled and packaged into a JAR file. First run the compiler:
javac SimpleBean.java
Then create a manifest file
manifest.tmp
Name: SimpleBean.class
Java-Bean: True
Finally create the JAR file:
jar cfmv SimpleBean.jar manifest.tmp SimpleBean.class
The JAR file can now be placed in the beans/jars so that the
BeanBox will find it on startup, or it can be loaded subsequently by choosing
File | LoadJar.
SimpleBean.java
import java.awt.*;
import java.io.Serializable;
import java.beans.*;
public class SimpleBean extends Canvas implements
Serializable,
java.beans.PropertyChangeListener
{
// Constructor sets inherited
properties
public SimpleBean() {
setSize(60,40);
setBackground(Color.red);
}
// This section illustrates
how to add customizable properties to the Java Bean. The names
// of the property setter
and getter methods must follow specific design patterns that allow
// the BeanBox (or builder
tool) to determine the name of the property variable upon
// introspection.
private Color beanColor = Color.green;
public void setBeanColor(Color
newColor) {
Color oldColor = beanColor;
beanColor = newColor;
repaint();
// This relates to bound property support (see below).
changes.firePropertyChange("beanColor", oldColor, newColor);
}
public Color getBeanColor()
{
return beanColor;
}
public void paint(Graphics
g) {
g.setColor(beanColor);
g.fillRect(20,5,20,30);
}
// This section illustrates
how to implement bound property support. Bound property
// support allows other
objects to respond when a property change occurs in this Java
// Bean. Remember
that each property setter method must fire a property change event,
// so that registered
listeners can be properly notified. The addPropertyChangeListener
// and removePropertyChangeListener
methods follow design patterns that indicate the
// ability of this Java
Bean to generate property change events. As it happens, these
// methods overide methods
of the same names, which are inherited through java.awt.Canvas.
private PropertyChangeSupport changes = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener
l) {
changes.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener
l) {
changes.removePropertyChangeListener(l);
}
// This section illustrates
how to implement a bound property listener, which will allow this
// Java Bean to register
itself to receive property change events fired by other objects.
// Registration simply
involves making a call to the other object's addPropertyChangeListener
// method with this Java
Bean as the argument. If you are using the BeanBox, however, you
// will typically use
the event hookup adaptor mechanism to receive the events. In this
case,
// you can set the target
method to be the propertyChange method. (Another word about the
// BeanBox: the Edit
| bind property option is a useful way to make a property change in one
// object automatically
trigger a property change in another object. In this case, the BeanBox
// will invoke the correct
property setter using code in sunw/beanbox/PropertyHookup.java.
// An adaptor class will
not be generated in this case.)
public void propertyChange(PropertyChangeEvent
evt) {
String propertyName = evt.getPropertyName();
System.out.println("Received property change event " + propertyName);
}
}