http://java.sun.com/beans/index.html
JWAVE Bean tester
(which generates some data), and two visible Beans, JWAVE Surface Tool
and OrangeButton
, to produce a surface plot.
CLASSPATH
variable:
VNI_DIR/classes/JWaveConnectInfo.jar
VNI_DIR/classes/JWave.jar
SWINGDIR/swing.jar
VNI_DIR\classes\JWaveConnectInfo.jar
VNI_DIR\classes\JWave.jar
SWINGDIR\swing.jar
VNI_DIR
is the main Visual Numerics Installation directory, and SWINGDIR
is the directory where Swing version 1.1 or later is installed (see the Glossary).
VNI_DIR/classes/JWaveBeans.jar
VNI_DIR\classes\JWaveBeans.jar
VNI_DIR
is the main Visual Numerics installation directory. to
BDKDIR\jars
BDKDIR
is the directory in which the BeanBox is installed. When the BeanBox starts, this JAR file is automatically opened and inspected.
BDKDIR/beanbox/run.sh
BDKDIR\beanbox\run.bat
BDKDIR
is the directory where the BeanBox is installed.
CLASSPATH modifications described in Step 1 directly in this script, so that
TIP: You can make the
CLASSPATH
includes $CLASSPATH
(UNIX) or %classpath%
(Windows).
NOTE: You must CD to theBDKDIR/beanbox
directory to runrun.sh
orrun.bat
. whereBDKDIR
is the directory in which the BeanBox is installed.
BDKDIR
type:
run.sh
run.bat
JWAVE Bean tester
Bean are listed in the ToolBox along with other Beans that came with the BDK or that you added (Figure 7-1).
NOTE: Remember, the JWAVE Manager must be running to use JWAVE Beans.
JWAVE Bean tester
, then click in an open area of the BeanBox window to place it as an object in the BeanBox.
OrangeButton
) from the ToolBox and place it near the bottom of the BeanBox window.
JWAVE Bean tester
object in the BeanBox. This links the button object to the JWAVE Bean tester
object.
start2d
target method (this method will form the connection between the JWAVE Bean tester
and the push button), then click OK
. Now, every time you click the button, the tester Bean will generate new 2D data.
Figure 7-1 JWAVE Beans listed in the BeanBox ToolBox
JWAVE Surface Tool
and place it above the button object in the BeanBox.
Surface Tool
Bean until it is big enough to hold a surface plot.
JWAVE Bean tester
object in the BeanBox, then from the Edit menu, select Events=Bean tester
to the Surface Tool
.
propertyChange
target method as the event, then click OK
.
Bean tester
generates 2D data and sends that data to the Surface Tool
. Next, the Surface Tool
instructs JWAVE to generate a plot. Then the surface plot displays in the BeanBox (see Figure 7-2).
TIP: The customizable parameters for JWAVE Beans are described in Appendix E, JWAVE Bean Tools Reference.
JWAVE Surface Tool
Bean appears.
Done
.
Figure 7-2 The surface plot displayed by the JWAVE Surface Tool. The JWAVE Surface Tool has been customized to add a skirt and change the default colors.
JWAVE Bean tester
Bean (used in the previous section) is used in the discussion of the Bean development techniques. You can find this source code in:
VNI_DIR/classes/com/visualnumerics/jwave/beans/JWaveBeanTest.java
VNI_DIR\classes\com\visualnumerics\jwave\beans\JWaveBeanTest.java
NOTE: The intended audience for this section is Java developers and those familiar with object-oriented programming. This section is by no means a comprehensive source for constructing a Bean. There are many books and Web sites on the subject that take a much more thorough look at the capabilities of JavaBeans.
Once the Bean's requirements are defined, it should be fairly obvious what the Bean should look like. Most Beans are visible components (buttons, fields, or canvases-the JWAVE Beans Tools are canvases). However, it is not required that Beans have any visible attributes at all. The
JWAVE Bean tester is one of these "invisible" Beans that has no GUI elements at all. It is just a class that has input and output, and that generates data and events.
If your Bean does need to have visible components, just code them as you would any other class. Beans are required to have a no argument constructor, but you may use this constructor like any other, building the elements your class needs in it.
In terms of Beans, properties are exposed to visual programming tools and are usually editable through some sort of property editing interface (see Telling a Bean Environment How to Use Your Bean, and Building a Customizer for the Bean).
public void setPropertyName(PropertyType value);
public PropertyType getPropertyName();
set
method writes the data to the Bean, while the get
method reads the data from the Bean. By having both methods, the property is defined as read/write enabled. By omitting one of the methods, the property becomes either read-only or write-only.Example 7-1 Aget/set pair, taken from JWAVE Bean tester
DoubleTable dataTable_; /** * Set the dataTable Proxy * @see JWaveBeanTest#getDataTable * @param Proxy The dataTable Proxy this bean will use */ public void setDataTable(Proxy p) { proxy_=p; Object pObject=proxy_.retrieve(); try { dataTable_=(DoubleTable)pObject; } catch(ClassCastException e) { System.out.println("Data was not a DoubleTable"); System.out.println(e); } } /** * Get the dataTable Proxy * @see JWaveBeanTest#setDataTable * @return Proxy The dataTable Proxy */ public Proxy getDataTable() { if(dataTable_!=null) { Proxy p=new com.visualnumerics.data.LocalProxyImpl(dataTable_); return(p); } else return(null); }
TIP: Properties do not have to be data members of the class. There can beget
/set
methods that calculate values or return hardcoded constants.
Using the
com.visualnumerics.data.Proxy
objects. The Proxy class is an interface that contains a reference to a data object. The intent of the Proxy class is to allow data to be passed that is not necessarily inside your virtual machine (such as a CORBA
object). The class
com.visualnumerics.jwave.JWaveDataProxy implements the Proxy class, making it valid to pass to the Beans. The JWaveDataProxy
class is a client side proxy for data that is resident on the JWAVE server.
When data is to be sent to a JWAVE Bean (either through an event or a bound property), it must be a
Proxy
object. set2D_Data
) and output (get2D_Data
and start2d
) for a two-dimensional array of data (data2d_
). The use of these methods is discussed further in a later section (page 108), but for now, notice how the data either comes in or goes out of the methods as a Proxy
object. The class com.visualnumerics.data.LocalProxyImpl
is an implementation of Proxy
for data that is already resident inside your virtual machine.
Example 7-2 Data being sent as aProxy object in JWAVE Bean tester
double[][] data2d_; /** * Set the 2D data Proxy * @see JWaveBeanTest#get2D_Data * @param Proxy The 2D data Proxy this bean will use */ public void set2D_Data(Proxy p) { proxy_=p; Object pObject=proxy_.retrieve(); try { data2d_=(double[][])pObject; Proxy p=new com.visualnumerics.data.LocalProxyImpl(data2d_); changes_.firePropertyChange("data_2d_change",null,p); } catch(ClassCastException e) { System.out.println("Data was not a 2D double"); System.out.println(e); } } /** * Get the 2D data Proxy * @see JWaveBeanTest#set2D_Data * @return Proxy The 2D data Proxy */ public Proxy get2D_Data() { if(data2d_!=null) { Proxy p=new com.visualnumerics.data.LocalProxyImpl(data2d_); return(p); } else return(null); } /** * Fire an event that the 2D data has changed */ public void start2d() { Proxy p=new com.visualnumerics.data.LocalProxyImpl(data2d_); changes_.firePropertyChange("data_2d_change",null,p); }
Using Events to Exchange Data
In the case of the JWAVE Beans, one way of passing data between them is via a
PropertyChangeEvent
. This class is part of the java.beans
package and is a standard way for Beans to communicate. java.beans.PropertyChangeListener
and are therefore capable of receiving and handling PropertyChangeEvents
. java.beans.PropertyChangeSupport. This class maintains a list of classes that have registered as listeners and allows events to be sent to those listeners.
Example 7-3 Instances ofPropertyChangeEvents from JWAVE Bean tester
PropertyChangeSupport changes_=new PropertyChangeSupport(this); /** * Adds a property change listener * @param PropertyChangeListener listener */ public synchronized void addPropertyChangeListener(PropertyChangeListener l) { changes_.addPropertyChangeListener(l); } /** * Removes a property change listener * @param PropertyChangeListener listener */ public synchronized void removePropertyChangeListener(PropertyChangeListener l) { changes_.removePropertyChangeListener(l); } /** * Fire an event that the 2D data has changed */ public void start2d() { Proxy p=new com.visualnumerics.data.LocalProxyImpl(data2d_); changes_.firePropertyChange("data_2d_change",null,p); }
changes_
maintains a list of classes that wish to listen to this Bean's events. Any class that calls addPropertyChangeListener
will be added to that list. Similarly, any class that calls removePropertyChangeListener
will be removed from the list.
When the
start2d
method is called, any class that has registered as a listener will receive the data found in the data2d_
array (as a Proxy
object of course). firePropertyChange
method of the PropertyChangeSupport
object takes as arguments the name of the property that has changed, the old value of that property, and the new value of the property. These are the exact fields of the java.beans.PropertyChangeEvent class that
PropertyChangeListeners
expect to see.
Including Bound Properties to Exchange New Data
All of the data properties in the JWAVE Beans are bound properties. When the data in a Bean changes, it will fire an event to all registered listeners. This event will contain the new data.
PropertyChangeEvent
. All Beans listening will receive a Proxy
with the new data array inside.
Example 7-4 An input method to the JWAVE Bean tester Bean
/** * Set the 2D data Proxy * @see JWaveBeanTest#get2D_Data * @param Proxy The 2D data Proxy this bean will use */ public void set2D_Data(Proxy p) { proxy_=p; Object pObject=proxy_.retrieve(); try { data2d_=(double[][])pObject; Proxy p=new com.visualnumerics.data.LocalProxyImpl(data2d_); changes_.firePropertyChange("data_2d_change",null,p); } catch(ClassCastException e) { System.out.println("Data was not a 2D double"); System.out.println(e); } }
Bind Property
choice is available under the Edit
menu for displaying a list of the Bean's bound properties. Selecting one of those bound properties allows you to connect it to another Bean's bound property. The source bean will have its get
method called and the destination Bean will have its set
method called with the data retrieved from the get
call. In this way, two Beans can share property values without any events.
Using a BeanInfo Class
java.beans.BeanInfo
interface. However, since implementing this class requires implementations for each method in the BeanInfo interface, Java has provided a java.beans.SimpleBeanInfo
class. Subclassing or extending the java.beans.SimpleBeanInfo
class allows you to use only the methods you need for your Bean (the remainder have dummy implementations already defined). These methods allow you to tell the Bean environment what your Bean can do.BeanInfo.java
BeanInfo class is not in the same package as your Bean, your CLASSPATH
is searched. The down side of all of this is that the Bean and the BeanInfo are tightly coupled and it's up to the developer to keep this relationship. The developer must be careful to not allow one class to evolve without the other.Let's take a look at for the BeanInfo for the
JWAVE Bean tester
Bean. The Java code, purpose, and details of these methods are presented on the following pages:
Example 7-5 BeanDescriptors
private BeanDescriptor beanDescriptor_; private final static Class beanClass_ = com.visualnumerics.jwave.beans.JWaveBeanTest.class; /** * Default Constructor. This just sets-up the BeanDescriptor */ public JWaveBeanTestBeanInfo() { beanDescriptor_ = new BeanDescriptor(beanClass_); beanDescriptor_.setDisplayName("JWAVE Bean tester"); beanDescriptor_.setShortDescription("A test bean for use with JWAVE Tools"); } /** * This method returns the BeanDescriptor. * @return BeanDescriptor */ public BeanDescriptor getBeanDescriptor() { return beanDescriptor_; }
Constructor
and the getBeanDescriptor
methods both deal with the java.beans.BeanDescriptor
class. This class is needed by the Bean environments to describe features of the Bean. The only fields in this class are the Bean's Customizer
(see Building a Customizer for the Bean) and the Bean itself.
Example 7-6 BeanEvents
/** * This method describes which events of the Bean will be exposed. * The only event exposed is propertyChange. * @return EventSetDescriptor[] an array of EventSetDescriptors * detailing which events of this bean are exposed. */ public EventSetDescriptor[] getEventSetDescriptors() { try { EventSetDescriptor changed = new EventSetDescriptor (beanClass, "propertyChange", java.beans.PropertyChangeListener.class, "propertyChange"); changed.setDisplayName("bound property change"); EventSetDescriptor[] rv = {changed}; return rv; } catch (IntrospectionException e) { throw new Error(e.toString()); } }
getEventSetDescriptors
method returns an array of java.beans.EventSetDescriptor
objects. Each object in the array describes an event supported by the Bean. The getEventSetDescriptors
method therefore allows you to limit which events your Bean can fire. This is especially useful when your Bean is a Java component. Most components have many events they can deal with (actions, mouse events, window events, and so on) and you may not want your Bean to have to deal with all of them. The getEventSetDescriptors
method allows you to set which events your Bean can handle.In the source code in Example 7-6:
propertyChange
, will be seen by the Bean environments
EventSetDescriptor
class is instantiated
EventSetDescriptor
is passed back to the caller
Example 7-7 BeanMethods
/** * This method describes which methods of the Bean will be exposed. * @return MethodDescriptor[] an array of MethodDescriptors * detailing which methods of this bean are exposed. */ public MethodDescriptor[] getMethodDescriptors() { // First find the "method" objects. Method proxy2d,proxy2dRand,proxy1dRand; Method proxySin,proxyCos; Method proxyDataTable,proxyPieTable; Method start2d,startSin,startCos,startDataTable,startPieTable; Method start2dRand,start1dRand; Class proxyEventArgs[] = {com.visualnumerics.data.Proxy.class}; try { proxy2d = beanClass_.getMethod("set2D_Data", proxyEventArgs); proxy2dRand = beanClass_.getMethod("set2D_RandomData", proxyEventArgs); proxy1dRand = beanClass_.getMethod("set1D_RandomData", proxyEventArgs); proxySin = beanClass_.getMethod("setSineData", proxyEventArgs); proxyCos = beanClass_.getMethod("setCosineData", proxyEventArgs); proxyDataTable = beanClass_.getMethod("setDataTable", proxyEventArgs); proxyPieTable = beanClass_.getMethod("setPieTable", proxyEventArgs); startPieTable= beanClass_.getMethod("startPieTable", null); startDataTable= beanClass_.getMethod("startDataTable", null); startSin = beanClass_.getMethod("startSine", null); startCos = beanClass_.getMethod("startCosine", null); start2d = beanClass_.getMethod("start2d", null); start2dRand = beanClass_.getMethod("start2dRandom", null); start1dRand = beanClass_.getMethod("start1dRandom", null); } catch (Exception ex) { throw new Error( "Missing method: " + ex ); } // Now create the MethodDescriptor array // with visible event response methods: MethodDescriptor result[] = {new MethodDescriptor(proxy2d), new MethodDescriptor(proxy2dRand), new MethodDescriptor(proxy1dRand), new MethodDescriptor(proxySin), new MethodDescriptor(proxyCos), new MethodDescriptor(proxyDataTable), new MethodDescriptor(startDataTable), new MethodDescriptor(proxyPieTable), new MethodDescriptor(startPieTable), new MethodDescriptor(startSin), new MethodDescriptor(startCos), new MethodDescriptor(start2d), new MethodDescriptor(start2dRand), new MethodDescriptor(start1dRand)}; return result; }
getMethodDescriptors
method returns an array of java.beans.MethodDescriptor
objects. This object describes a method supported by a Bean. This array of descriptors details all of the methods your Bean will have exposed to the Bean environment. The use of the getMethodDescriptors
methods allows you to limit which methods can be exposed.In the source code in Example 7-7, 14 methods are returned to the caller:
java.lang.reflect.Method
class is created for each method to be exposed. The
null
is supplied to the Method
constructor.
Method
classes created earlier. These MethodDescriptor
classes are returned to the caller.
Example 7-8 Beanproperties
/** * This method details which bean properties are exposed and how * they are to be edited. * @return PropertyDescriptor[] */ public PropertyDescriptor[] getPropertyDescriptors() { try { PropertyDescriptor data2PD = new PropertyDescriptor("data2d", beanClass_, "get2D_Data", "set2D_Data"); data2PD.setDisplayName("2D Data"); data2PD.setBound(true); PropertyDescriptor sinPD = new PropertyDescriptor("sine wave", beanClass_, "getSineData", "setSineData"); sinPD.setDisplayName("Sine Data"); sinPD.setBound(true); PropertyDescriptor cosPD = new PropertyDescriptor("cosine wave", beanClass_, "getCosineData", "setCosineData"); cosPD.setDisplayName("Cosine Data"); cosPD.setBound(true); PropertyDescriptor tablePD = new PropertyDescriptor("dataTable", beanClass_, "getDataTable", "setDataTable"); tablePD.setDisplayName("2D Table Data"); tablePD.setBound(true); PropertyDescriptor piePD = new PropertyDescriptor("pieTable", beanClass_, "getPieTable", "setPieTable"); piePD.setDisplayName("Pie Data"); piePD.setBound(true); PropertyDescriptor data2RPD=new PropertyDescriptor("randData2d", beanClass_, "get2D_RandomData", "set2D_RandomData"); data2RPD.setDisplayName("2D Random Data"); data2RPD.setBound(true); PropertyDescriptor data1RPD=new PropertyDescriptor("randData1d", beanClass_, "get1D_RandomData", "set1D_RandomData"); data1RPD.setDisplayName("1D Random Data"); data1RPD.setBound(true); PropertyDescriptor rv[] = {data2PD,sinPD,tablePD,piePD,cosPD, data2RPD,data1RPD}; return rv; } catch (IntrospectionException e) { throw new Error(e.toString()); } }
getPropertyDescriptors
method returns an array of java.beans.PropertyDescriptor
objects. These objects describe properties the Bean will expose. The use of getPropertyDescriptors
not only allows you to limit which properties are available to the Bean environment, but specific property attributes (such as names, which methods get
/set
this property, bound status, and so on) and specific property editor classes can also be set.
Using Property Editor Classes
Color
object, the BeanBox assigns a class that allows the user to edit colors. If the property is a String
, the BeanBox assigns a TextField
so the user can change the string.
There are two instances when this automatic assignment of editors fails:
PropertyDescriptor
object using the setPropertyEditorClass
method.
setPropertyEditorClass
method. If the property does not need and editor (such as is the case in this source), then simply don't assign one.
The source code shown in Example 7-8 creates a PropertyDescriptor
object for each property to be exposed. This object is created by supplying:
get
' this property
set
' this property
get
/set
convention; you may supply any method name you wish. Each property is supplied with a display name. This is the name that appears on the Property Sheet. Finally, each property is set as a bound property. The default for this value is false, so this method call is not needed if the property is not to be bound. All of these PropertyDescriptor
objects are then returned to the caller.
java.bean.Customizer
class and they must be a subclass of java.awt.Component
. Customizers are most often GUI interfaces that allow the user to change Bean properties. It is a way for a developer to design a professional looking editor class for the Bean.
Methods
java.beans.Customizer
interface has just three methods:
addPropertyChangeListener
add/removePropertyChangeListener
methods are described in Using Events to Exchange Data. The Customizer should fire a PropertyChangeEvent
when it changes a property of the Bean.
The
setObject(Object
bean)
method of java.beans.Customizer
is automatically called by the Bean environment when a Customizer is started. This method should query the bean object for its current status and use that information to fill in the GUI Customizer components. The method of querying the Bean is the set
/get
methods which are used in almost all Bean transactions.no argument
constructor. As with all other Bean classes, there is no method instantiating the Customizer so there are no available arguments to pass.
Example 7-9 Example from a
BeanInfo class
/** * java.lang.Class object. This is just the name of the bean * this BeanInfo is representing. */ private final static Class beanClass = com.visualnumerics.jwave.beans.JWaveBar3dTool.class; /** * java.lang.Class object. This is just the name of the Customizer class * this bean uses. */ private final static Class beanCustomizerClass = com.visualnumerics.jwave.beans.JWaveBar3dToolCustomizer.class; public JWaveBar3dToolBeanInfo() { beanDescriptor_ = new BeanDescriptor(beanClass,beanCustomizerClass); String displayName = resources.getString("display_name"); beanDescriptor_.setDisplayName(displayName); String descName = resources.getString("short_description"); beanDescriptor_.setShortDescription(descName); }
JWAVE Bean tester
Bean has no Customizer. This is because it has no properties that are editable by the user. However, all of the remaining JWAVE Beans do have Customizers. These Customizers are based on the Java Foundation Classes (JFC) graphical components and allow the user to edit every property of the Bean except for the data.
TIP: To open a Customizer in the BeanBox, select a Bean and choose Edit=Customize. If the Bean does not have a Customizer, then this option is not available.
java.io.FileOutputStream
object to end-up in the applet code.Unfortunately, serializing is not something that happens automatically. Each Bean must implement the
java.io.Serializable interface. In most cases, this makes the Bean serializable. There are, of course, cases when this is not enough.
When serialization takes place, Java does not deal with transient variables of your Beans. They're transient; they are not part of the state of your Bean. However, any data member that is part of your class will be serialized. In general, most standard Java API classes are serializable and won't pose any problem. There are a few however, that are not (such as java.awt.Image
). For these classes, or those not part of the Java API, you need to implement two methods to do the serialization. These methods are the writeObject
and readObject
methods of the Serializable class. The implementation of the methods is beyond the scope of this document, but it entails the breaking down of classes that aren't serializable into their serializable parts. For instance, java.awt.Image
is not serializable. To serialize java.awt.Image
you must get the data values out of the class and write those values to the stream. For a more thorough discussion on serializing, consult a book or Web site on JavaBeans development.
TIP: If you generate an applet from your JWAVE Beans, you must modify the HTML file generated by the BDK. TheARCHIVE
tag in the HTML file must includeJWave.jar
,JWaveBeans.jar
,JWaveConnectInfo.jar
, andswing.jar
.
NOTE: If you want to run your generated applet withappletviewer
, you must put the JAR files listed in the previous Tip in yourCLASSPATH
. This is becauseappletviewer
does not pick up the settings of theARCHIVE
.