You are on page 1of 59

JavaBeans Concepts

Using the NetBeans GUI Builder

Writing a Simple Bean

Properties

Simple Properties

Bound Properties

Constrained Properties

Indexed Properties

Manipulating Events

Bean Persistence

Long Term Persistence

Introspection

Bean Customization

Using the BeanContext API

Overview of the BeanContext API

Bean Context #1: Containment Only

Bean Context #2: Containment and Services

AWT Containers and the BeanContextProxy Interface


The JavaBeans™ architecture is based on a component model which enables developers
to create software units called components. Components are self-contained, reusable
software units that can be visually assembled into composite components, applets,
applications, and servlets using visual application builder tools. JavaBean components are
known as beans.

A set of APIs describes a component model for a particular language. The JavaBeans API
specificationdescribes the core detailed elaboration for the JavaBeans component
architecture.

Beans are dynamic in that they can be changed or customized. Through the design mode
of a builder tool you can use the Properties window of the bean to customize the bean and
then save (persist) your beans using visual manipulation. You can select a bean from the
toolbox, drop it into a form, modify its appearance and behavior, define its interaction
with other beans, and combine it and other beans into an applet, application, or a new
bean.

The following list briefly describes key bean concepts.

• Builder tools discover a bean's features (that is, its properties, methods, and
events) by a process known as introspection. Beans support introspection in two
ways:
o By adhering to specific rules, known as design patterns, when naming
bean features. The Introspector class examines beans for these design
patterns to discover bean features. The Introspector class relies on the
core reflection API. The trail The Reflection API is an excellent place to
learn about reflection.
o By explicitly providing property, method, and event information with a
related bean information class. A bean information class implements the
BeanInfo interface. A BeanInfo class explicitly lists those bean features
that are to be exposed to application builder tools.
• Properties are the appearance and behavior characteristics of a bean that can be
changed at design time. Builder tools introspect on a bean to discover its
properties and expose those properties for manipulation.
• Beans expose properties so they can be customized at design time. Customization
is supported in two ways: by using property editors, or by using more
sophisticated bean customizers.
• Beans use events to communicate with other beans. A bean that is to receive
events (a listener bean) registers with the bean that fires the event (a source bean).
Builder tools can examine a bean and determine which events that bean can fire
(send) and which it can handle (receive).
• Persistence enables beans to save and restore their state. After changing a bean's
properties, you can save the state of the bean and restore that bean at a later time
with the property changes intact. The JavaBeans architecture uses Java Object
Serialization to support persistence.
• A bean's methods are no different from Java methods, and can be called from
other beans or a scripting environment. By default all public methods are
exported.

Beans vary in functionality and purpose. You have probably met some of the following
beans in your programming practice:

• GUI (graphical user interface)


• Non-visual beans, such as a spelling checker
• Animation applet
• Spreadsheet application

Lesson: Using the NetBeans GUI Builder


his lesson explains how to use the NetBeans IDE GUI Builder to work with beans. In
preparation for working with the GUI Builder, you should be first familiar with the key
NetBeans concepts which are explained in the NetBeans IDE Java Quick Start Tutorial.

This lesson guides you through the process of creating a bean pattern in the NetBeans
projects, introduces the user interface of the GUI Builder, and explains how to add your
bean object to the palette.

Creating a New Project

In the NetBeans IDE, you always work in a project where you store sources and files. To
create a new project, perform the following steps:

1. Select New Project from the File menu. You can also click the New Project
button in the IDE toolbar.
2. In the Categories pane, select the General node. In the Projects pane, choose the
Java Application type. Click the Next button.
3. Enter MyBean in the Project Name field and specify the project location. Do not
create a Main class here, because later you will create a new Java class in this
project.
4. Click the Finish button.

This figure represents the expanded MyBean node in the Projects list.
Creating a New Form

After creating a new project, the next step is to create a form within which the JavaBeans
components and other required GUI components, will be placed.

To create a new form, perform the following sequence of actions:

1. In the Projects list, expand the MyBean node, right-click on the <default
package> node and choose New|JFrame Form from the pop-up menu.
2. Enter MyForm as the Class Name.
3. Click the Finish button.

The IDE creates the MyForm form and the MyForm class within the MyBean application
and opens the MyForm form in the GUI Builder.

This figure represents the Projects list, where the MyForm class is located.
The GUI Builder Interface

When the JFrame form is added to your application, the IDE opens the newly-created
form in an Editor tab with a toolbar containing the following buttons:

• – Selection Mode enables you to select one or more objects in the Design
Area.
• – Connection Mode enables you to set a connection between objects by
specifying an event.
• – Preview Design enables you to preview the form layout.
• – Align commands enable you to align selected objects.
• – Change Resizability enables you to set up vertical and horizontal resizing.

When the MyForm form opens in the GUI Builder's Design view, three additional
windows appear, enabling you to navigate, organize, and edit GUI forms. These windows
include the following:

• Design Area. The primary window for creating and editing Java GUI forms.
Source and Design toggle buttons enable you to switch between view a class's
source code and a graphical view of the GUI components. Click on an object to
select it in the Design Area. For a multiple selection, hold down the Ctrl key
while clicking on objects.
• Inspector. Representation of a tree hierarchy of all the components in your
application. The Inspector highlights the component in the tree that is currently
being edited.
• Palette. A customizable list of available components containing groups for
Swing, AWT, Borders, and Beans components. This window enables you to
create, remove, and rearrange the categories displayed in the palette using the
customizer.
• Properties Window. A display of the properties of the component currently
selected in the GUI Builder, Inspector window, Projects window, or Files
window.

If you click the Source button, the IDE displays the application's Java source code in the
editor. Sections of code that are automatically generated by the GUI Builder are indicated
by blue areas. These blue areas are protected from editing in the Source view. You can
only edit code appearing in the white areas of the editor when in Source view. When you
make your changes in the Design View, the IDE updates the file's sources.

Creating a Bean

To create your own bean object and add it to the palette for the bean group, execute the
following procedure:

1. Select the <default package> node in the MyBean project.


2. Choose New|Java Class from the pop-up menu.
3. Specify the name for the new class, for example, MyBean, then press the Finish
button.
4. Open the MyBean.java file.
5. In the editor window, select inside the class. Right-click and choose Insert Code.
Then select Add Property.
6. In the Name field of the Add Property dialog box, type YourName and press OK.
7. Now you can analyze the automatically generated code. Notice that set and get
methods were included:

public class MyBean {

/** Creates a new instance of MyBean */


public MyBean() {
}
/**
* Holds value of property yourName.
*/
private String yourName;

/**
* Getter for property yourName.
* @return Value of property yourName.
*/
public String getYourName() {
return this.yourName;
}

/**
* Setter for property yourName.
* @param yourName New value of property yourName.
*/
public void setYourName(String yourName) {
this.yourName = yourName;
}

8. Right-click the MyBean node in the MyBean project tree and choose Tools |Add
to Palette from the pop-up menu.
9. Select the Beans group in the Palette tree to add your bean.

Now you can switch to the Palette window by choosing Palette from the Windows menu
and make sure that the MyBean component was added to the Beans group.

So far you have created a bean, set the YourName property, and added this bean as a
component to the palette.

Adding Components to the Form

Now you can use the Free Design of the GUI Builder and add the MyBean component
and other standard Swing components to MyForm.

1. Select the MyForm node in the project tree.


2. Drag the JLabel Swing component from the Palette window to the Design Area.
Double-click the component and change the text property to "Enter your name:".
3. Drag the JTextField component from the Palette window to the Design Area.
Double-click the component and empty the text field.
4. Drag the JButton component from the Palette window to the Design Area.
Double-click the component and enter "OK" as the text property.
5. Add another button and enter "Cancel" as its text property.
6. Align components by using the appropriate align commands.
7. Before you drag the MyBean component from the Pallete you must compile your
project because the MyBean component is non-visual and cannot be operated as a
visual component. When you Drag and Drop the MyBean component it will not
appear in the Design Area. However, you can view it in the Inspector window by
expanding the Other Components node, as shown in the following figure.
8.

To summarize, in the previous steps you created a project, developed a JFrame form,
added a bean object and included it in your project as a non-visual component. Later in
this trail you will learn how to change properties for the bean component and handle
events by using the NetBeans GUI Builder.

In this section you will learn more about beans by


performing the following actions:
• Creating a simple bean
• Compiling the bean
• Generating a Java Archive (JAR) file
• Loading the bean into the GUI Builder of the NetBeans IDE
• Inspecting the bean's properties and events
Your bean will be named SimpleBean. Here are the steps to create it:

1. Write the SimpleBean code. Put it in a file named SimpleBean.java, in the


directory of your choice. Here's the code:
2. import java.awt.Color;
3. import java.beans.XMLDecoder;
4. import javax.swing.JLabel;
5. import java.io.Serializable;
6.
7. public class SimpleBean extends JLabel
8. implements Serializable {
9. public SimpleBean() {
10. setText( "Hello world!" );
11. setOpaque( true );
12. setBackground( Color.RED );
13. setForeground( Color.YELLOW );
14. setVerticalAlignment( CENTER );
15. setHorizontalAlignment( CENTER );
16. }
17. }

SimpleBean extends the javax.swing.JLabel graphic component and inherits


its properties, which makes the SimpleBean a visual component. SimpleBean also
implements the java.io.Serializable interface. Your bean may implement
either the Serializable or the Externalizable interface.

18. Create a manifest, the JAR file, and the class file SimpleBean.class. Use the
Apache Ant tool to create these files. Apache Ant is a Java-based build tool that
enables you to generate XML-based configurations files as follows:
19. <?xml version="1.0" encoding="ISO-8859-1"?>
20.
21. <project default="build">
22.
23. <dirname property="basedir" file="${ant.file}"/>
24.
25. <property name="beanname" value="SimpleBean"/>
26. <property name="jarfile" value="${basedir}/${beanname}.jar"/>
27.
28. <target name="build" depends="compile">
29. <jar destfile="${jarfile}" basedir="${basedir}"
includes="*.class">
30. <manifest>
31. <section name="${beanname}.class">
32. <attribute name="Java-Bean" value="true"/>
33. </section>
34. </manifest>
35. </jar>
36. </target>
37.
38. <target name="compile">
39. <javac destdir="${basedir}">
40. <src location="${basedir}"/>
41. </javac>
42. </target>
43.
44. <target name="clean">
45. <delete file="${jarfile}">
46. <fileset dir="${basedir}" includes="*.class"/>
47. </delete>
48. </target>
49.
50. </project>

It is recommended to save an XML script in the build.xml file, because Ant


recognizes this file name automatically.

51. Load the JAR file. Use the NetBeans IDE GUI Builder to load the jar file as
follows:
1. Start NetBeans.
2. From the File menu select "New Project" to create a new application for
your bean. You can use "Open Project" to add your bean to an existing
application.
3. Create a new application using the New Project Wizard.
4. Select a newly created project in the List of Projects, expand the Source
Packages node, and select the Default Package element.
5. Click the right mouse button and select New|JFrameForm from the pop-up
menu.
6. Select the newly created Form node in the Project Tree. A blank form
opens in the GUI Builder view of an Editor tab.
7. Open the Palette Manager for Swing/AWT components by selecting
Palette Manager in the Tools menu.
8. In the Palette Manager window select the beans components in the Palette
tree and press the "Add from JAR" button.
9. Specify a location for your SimpleBean JAR file and follow the Add from
JAR Wizard instructions.
10. Select the Palette and Properties options from the Windows menu.
11. Expand the beans group in the Palette window. The SimpleBean object
appears. Drag the SimpleBean object to the GUI Builder panel.

The following figure represents the SimpleBean object loaded in the GUI Builder
panel:
52. Inspect Properties and Events. The SimpleBean properties will appear in the
Properties window. For example, you can change a background property by
selecting another color. To preview your form, use the Preview Design button of
the GUI Builder toolbar. To inspect events associated with the SimpleBean
object, switch to the Events tab of the Properties window. You will learn more
about bean properties and events in the lessons that follow.

Lesson: Properties
In the following sections you will learn how to implement bean properties. A bean
property is a named attribute of a bean that can affect its behavior or appearance.
Examples of bean properties include color, label, font, font size, and display size.

The JavaBeans™ specification defines the following types of bean properties:

• Simple – A bean property with a single value whose changes are independent of
changes in any other property.
• Indexed – A bean property that supports a range of values instead of a single
value.
• Bound – A bean property for which a change to the property results in a
notification being sent to some other bean.
• Constrained – A bean property for which a change to the property results in
validation by another bean. The other bean may reject the change if it is not
appropriate.

Bean properties can also be classified as follows:

• Writable – A bean property that can be changed


o Standard
o Expert
o Preferred
• Read Only – A bean property that cannot be changed.
• Hidden – A bean property that can be changed. However, these properties are not
disclosed with the BeanInfo class

BeanBuilder uses this schema to group and represent properties in the Properties window.

Simple Properties

To add simple properties to a bean, add appropriate getXXX and setXXX methods (or
isXXX and setXXX methods for a boolean property).

The names of these methods follow specific rules called design patterns. These design
pattern-based method names allow builder tools such as the NetBeans GUI Builder, to
provide the following features:

• Discover a bean's properties


• Determine the properties' read/write attributes
• Determine the properties' types
• Locate the appropriate property editor for each property type
• Display the properties (usually in the Properties window)
• Alter the properties (at design time)

Adding a Title Property

In previous lessons you learned how to create a simple property by using the NetBeans
GUI Builder. The following procedure shows how to create a simple property in detail:

1. Right-click on the Bean Patterns node in the MyBean class hierarchy.


2. Select Add|Property from the pop-up menu.
3. Fill out the New Property Pattern form as shown in the following figure and click
OK.
4. The following code is automatically generated:
5. public class MyBean {
6.
7. /** Creates a new instance of MyBean */
8. public MyBean() {
9. }
10.
11. /**
12. * Holds value of property title.
13. */
14. private String title;
15.
16. /**
17. * Getter for property title.
18. * @return Value of property title.
19. */
20. public String getTitle() {
21. return this.title;
22. }
23.
24. /**
25. * Setter for property title.
26. * @param title New value of property title.
27. */
28. public void setTitle(String title) {
29. this.title = title;
30. }
31.
32. }
33. Now make your bean visual by extending the JComponent class and implement
the Serializable interface. Then, add the paintComponent method to represent
your bean.
34. import java.awt.Graphics;
35. import java.io.Serializable;
36. import javax.swing.JComponent;
37.
38. /**
39. * Bean with a simple property "title".
40. */
41. public class MyBean
42. extends JComponent
43. implements Serializable
44. {
45. private String title;
46.
47. public String getTitle()
48. {
49. return this.title;
50. }
51.
52. public void setTitle( String title )
53. {
54. this.title = title;
55. }
56.
57. protected void paintComponent( Graphics g )
58. {
59. g.setColor( getForeground() );
60.
61. int height = g.getFontMetrics().getHeight();
62. if ( this.title != null )
63. g.drawString(this.title, 0, height );
64. }
65. }

Inspecting Properties

Select the MyBean component in the Other Components node in the Inspector window.
Now you can analyze the title property in the Properties window and change it. To
change the title property press the "..." button and enter any string you wish.

The following figure represents the title property set to the "The title" value.
The NetBeans GUI Builder enables you to restrict the changing of a property value. To
restrict the changing of the title property, right-click the title property in the Bean
Patterns node of the MyBean project. Select Properties from the pop-up menu and the
Properties window appears. Choose one of the following property access types from the
Mode combo box:

• Read/Write
• Read only
• Write only

The Read only property has only the get method only, while the Write only property has
only the set method only. The Read/Write type property has both of these methods.

Bound Properties

Bound properties support the PropertyChangeListener (in the API reference


documentation) class.

Sometimes when a Bean property changes, another object might need to be notified of the
change, and react to the change.

Whenever a bound property changes, notification of the change is sent to interested


listeners.

The accessor methods for a bound property are defined in the same way as those for
simple properties. However, you also need to provide the event listener registration
methods forPropertyChangeListener classes and fire a PropertyChangeEvent (in the
API reference documentation) event to the PropertyChangeListener objects by calling
their propertyChange methods

The convenience PropertyChangeSupport (in the API reference documentation) class


enables your bean to implement these methods. Your bean can inherit changes from the
PropertyChangeSupportclass, or use it as an inner class.

In order to listen for property changes, an object must be able to add and remove itself
from the listener list on the bean containing the bound property. It must also be able to
respond to the event notification method that signals a property change.

The PropertyChangeEvent class encapsulates property change information, and is sent


from the property change event source to each object in the property change listener list
with the propertyChange method.

Implementing Bound Property Support Within a Bean

To implement a bound property in your application, follow these steps:

1. Import the java.beans package. This gives you access to the


PropertyChangeSupport class.
2. Instantiate a PropertyChangeSupport object. This object maintains the property
change listener list and fires property change events. You can also make your
class a PropertyChangeSupport subclass.
3. Implement methods to maintain the property change listener list. Since a
PropertyChangeSupport subclass implements these methods, you merely wrap
calls to the property-change support object's methods.
4. Modify a property's set method to fire a property change event when the property
is changed.

Creating a Bound Property

To create the title property as a bound property for the MyBean component in the
NetBeans GUI Builder, perform the following sequence of operations:

1. Right-click the Bean Patterns node in the MyBean class hierarchy.


2. Select Add|Property from the pop-up menu.
3. Fill the New Property Pattern form as shown on the following figure and click
OK.
4. Note that the title property and the multicast event source pattern
PropertyChangeListener were added to the Bean Patterns structure.

You can also modify existing code generated in the previous lesson to convert the title
and lines properties to the bound type as follows (where newly added code is shown in
bold):

import java.awt.Graphics;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import javax.swing.JComponent;

/**
* Bean with bound properties.
*/
public class MyBean
extends JComponent
implements Serializable
{
private String title;
private String[] lines = new String[10];

private final PropertyChangeSupport pcs = new


PropertyChangeSupport( this );

public String getTitle()


{
return this.title;
}

public void setTitle( String title )


{
String old = this.title;
this.title = title;
this.pcs.firePropertyChange( "title", old, title );
}

public String[] getLines()


{
return this.lines.clone();
}

public String getLines( int index )


{
return this.lines[index];
}

public void setLines( String[] lines )


{
String[] old = this.lines;
this.lines = lines;
this.pcs.firePropertyChange( "lines", old, lines );
}

public void setLines( int index, String line )


{
String old = this.lines[index];
this.lines[index] = line;
this.pcs.fireIndexedPropertyChange( "lines", index, old,
lines );
}

public void addPropertyChangeListener( PropertyChangeListener


listener )
{
this.pcs.addPropertyChangeListener( listener );
}

public void removePropertyChangeListener( PropertyChangeListener


listener )
{
this.pcs.removePropertyChangeListener( listener );
}

protected void paintComponent( Graphics g )


{
g.setColor( getForeground() );

int height = g.getFontMetrics().getHeight();


paintString( g, this.title, height );

if ( this.lines != null )
{
int step = height;
for ( String line : this.lines )
paintString( g, line, height += step );
}
}

private void paintString( Graphics g, String str, int height )


{
if ( str != null )
g.drawString( str, 0, height );
}
}

Constrained Properties
A bean property is constrained if the bean supports the VetoableChangeListener(in the
API reference documentation) and PropertyChangeEvent(in the API reference
documentation) classes, and if the set method for this property throws a
PropertyVetoException(in the API reference documentation).

Constrained properties are more complicated than bound properties because they also
support property change listeners which happen to be vetoers.

The following operations in the setXXX method for the constrained property must be
implemented in this order:

1. Save the old value in case the change is vetoed.


2. Notify listeners of the new proposed value, allowing them to veto the change.
3. If no listener vetoes the change (no exception is thrown), set the property to the
new value.

The accessor methods for a constrained property are defined in the same way as those for
simple properties, with the addition that the setXXX method throws a
PropertyVetoException exception. The syntax is as follows:

public void setPropertyName(PropertyType pt)


throws PropertyVetoException {code}

Handling Vetoes

If a registered listener vetoes a proposed property change by throwing a


PropertyVetoException exception, the source bean with the constrained property is
responsible for the following actions:

• Catching exceptions.
• Reverting to the old value for the property.
• Issuing a new VetoableChangeListener.vetoableChange call to all listeners to
report the reversion.

The VetoableChangeListener class throws a PropertyVetoException and handles the


PropertyChangeEvent event fired by the bean with the constrained property.

The VetoableChangeSupport provides the following operations:

• Keeping track of VetoableChangeListener objects.


• Issuing the vetoableChange method on all registered listeners.
• Catching any vetoes (exceptions) thrown by listeners.
• Informing all listeners of a veto by calling vetoableChange again, but with the
old property value as the proposed "new" value.

Creating a Constrained Property

To create a constrained property, set the appropriate option in the New Property Pattern
form as shown on the following figure.

Note that the Multicast Source Event Pattern - vetoableChangeListener was added to the
Bean Patterns hierarchy.

You can also modify the existing code generated in the previous lesson to make the
title and lines properties constrained as follows (where newly added code is shown in
bold):
import java.io.Serializable;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.awt.Graphics;
import javax.swing.JComponent;

/**
* Bean with constrained properties.
*/
public class MyBean
extends JComponent
implements Serializable
{
private String title;
private String[] lines = new String[10];

private final PropertyChangeSupport pcs = new


PropertyChangeSupport( this );
private final VetoableChangeSupport vcs = new
VetoableChangeSupport( this );

public String getTitle()


{
return this.title;
}
/**
* This method was modified to throw the PropertyVetoException
* if some vetoable listeners reject the new title value
*/
public void setTitle( String title )
throws PropertyVetoException
{
String old = this.title;
this.vcs.fireVetoableChange( "title", old, title );
this.title = title;
this.pcs.firePropertyChange( "title", old, title );
}

public String[] getLines()


{
return this.lines.clone();
}

public String getLines( int index )


{
return this.lines[index];
}
/**
* This method throws the PropertyVetoException
* if some vetoable listeners reject the new lines value
*/
public void setLines( String[] lines )
throws PropertyVetoException
{
String[] old = this.lines;
this.vcs.fireVetoableChange( "lines", old, lines );
this.lines = lines;
this.pcs.firePropertyChange( "lines", old, lines );
}

public void setLines( int index, String line )


throws PropertyVetoException
{
String old = this.lines[index];
this.vcs.fireVetoableChange( "lines", old, line );
this.lines[index] = line;
this.pcs.fireIndexedPropertyChange( "lines", index, old, line );
}

public void addPropertyChangeListener( PropertyChangeListener


listener )
{
this.pcs.addPropertyChangeListener( listener );
}

public void removePropertyChangeListener( PropertyChangeListener


listener )
{
this.pcs.removePropertyChangeListener( listener );
}
/**
* Registration of the VetoableChangeListener
*/
public void addVetoableChangeListener( VetoableChangeListener
listener )
{
this.vcs.addVetoableChangeListener( listener );
}

public void removeVetoableChangeListener( VetoableChangeListener


listener )
{
this.vcs.removeVetoableChangeListener( listener );
}

protected void paintComponent( Graphics g )


{
g.setColor( getForeground() );

int height = g.getFontMetrics().getHeight();


paintString( g, this.title, height );

if ( this.lines != null )
{
int step = height;
for ( String line : this.lines )
paintString( g, line, height += step );
}
}
private void paintString( Graphics g, String str, int height )
{
if ( str != null )
g.drawString( str, 0, height );
}
}

Indexed Properties

An indexed property is an array of properties or objects that supports a range of values


and enables the accessor to specify an element of a property to read or write.

Indexed properties are specified by the following methods:

//Methods to access individual values


public PropertyElement getPropertyName(int index)
public void setPropertyName(int index, PropertyElement element)
and
//Methods to access the entire indexed property array
public PropertyElement[] getPropertyName()
public void setPropertyName(PropertyElement element[])
Note that the distinction between the get and set methods for indexed properties is
subtle. The get method either has an argument that is the array index of the property, or
returns an array. The set method either has two arguments, namely an integer array
index and the property element object that is being set, or has the entire array as an
argument.

Creating an Indexed Property

To create an indexed property for your MyBean component, right-click the Bean Patterns
node and select Add|Indexed Property from the pop-up menu. Set up Non-Index Options
as shown in the following figure.
The code in the Source window will be changed automatically as follows:

import java.awt.Graphics;
import java.io.Serializable;
import javax.swing.JComponent;

/**
* Bean with simple property 'title'.
*/
public class MyBean
extends JComponent
implements Serializable
{
private String title;

public String getTitle()


{
return this.title;
}

public void setTitle( String title )


{
this.title = title;
}
protected void paintComponent( Graphics g )
{
g.setColor( getForeground() );

int height = g.getFontMetrics().getHeight();


if ( this.title != null )
g.drawString(this.title, 0, height );
}

/**
* Holds value of property lines.
*/
private String[] lines;

/**
* Indexed getter for property lines.
* @param index Index of the property.
* @return Value of the property at index.
*/
public String getLines(int index) {
return this.lines[index];
}

/**
* Getter for property lines.
* @return Value of property lines.
*/
public String[] getLines() {
return this.lines;
}

/**
* Indexed setter for property lines.
* @param index Index of the property.
* @param lines New value of the property at index.
*/
public void setLines(int index, String lines) {
this.lines[index] = lines;
}

/**
* Setter for property lines.
* @param lines New value of property lines.
*/
public void setLines(String[] lines) {
this.lines = lines;
}
}
Add the following code to the MyBean.java component to present the user with a list of
choices. You can provide and change these choices at design time. (Newly added code is
shown in bold.)
import java.awt.Graphics;
import java.io.Serializable;
import javax.swing.JComponent;

/**
* Bean with a simple property "title"
* and an indexed property "lines".
*/
public class MyBean
extends JComponent
implements Serializable
{
private String title;
private String[] lines = new String[10];

public String getTitle()


{
return this.title;
}

public void setTitle( String title )


{
this.title = title;
}

public String[] getLines()


{
return this.lines.clone();
}

public String getLines( int index )


{
return this.lines[index];
}

public void setLines( String[] lines )


{
this.lines = lines;
}

public void setLines( int index, String line )


{
this.lines[index] = line;
}

protected void paintComponent( Graphics g )


{
g.setColor( getForeground() );

int height = g.getFontMetrics().getHeight();


paintString( g, this.title, height );

if ( this.lines != null )
{
int step = height;
for ( String line : this.lines )
paintString( g, line, height += step );
}
}

private void paintString( Graphics g, String str, int height )


{
if ( str != null )
g.drawString( str, 0, height );
}
}

The following figure represents the lines property in the Properties window.

Notice that this property has a null value. To set up an alternative value, press the "..."
button. The form shown in the following figure enables you to add ten items for the
lines property list. First remove the default null items. Then add custom items to the list
by entering each item value into the Item field and pressing the Add button each time.
Lesson: Manipulating Events

Event passing is the means by which components communicate with each other.
Components broadcast events, and the underlying framework delivers the events to the
components that are to be notified. The notified components usually perform some action
based on the event that took place.

The event model was designed to accommodate the JavaBeans™ architecture. To


understand how events and event handling work in the JavaBeans component model, you
must understand the concepts of events, listeners, and sources. To refresh your
knowledge in these areas, read the Writing Event Listeners lesson of the Swing tutorial.

The event model that is used by the JavaBeans architecture is a delegation model. This
model is composed of three main parts: sources, events, and listeners.

The source of an event is the object that originates or fires the event. The source must
define the events it will fire, as well as the methods for registering listeners of those
events. A listener is an object that indicates that it is to be notified of events of a
particular type. Listeners register for events using the methods defined by the sources of
those events.

From the Properties lesson you discovered two event listeners. The
PropertyChangeListener(in the API reference documentation) interface provides a
notification whenever a bound property value is changed and the
VetoableChangeListener(in the API reference documentation) creates a notification
whenever a bean changes a constrained property value.

Simple Event Example

This example represents an application that performs an action when a button is clicked.
Button components are defined as sources of an event type called ActionEvent(in the
API reference documentation). Listeners of events of this type must register for these
events using the addActionListener method.

Therefore, the addActionListener method is used to register the ButtonHandler object


as a listener of the ActionEvent event that is fired by the button.

In addition, according to the requirements of the ActionListener class, you must define
an actionPerformed method, which is the method that is called when the button is
clicked.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextArea;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

public class ButtonHandler implements ActionListener {


/**
* Component that will contain messages about
* events generated.
*/
private JTextArea output;
/**
* Creates an ActionListener that will put messages in
* JTextArea everytime event received.
*/
public ButtonHandler( JTextArea output )
{
this.output = output;
}

/**
* When receives action event notification, appends
* message to the JTextArea passed into the constructor.
*/
public void actionPerformed( ActionEvent event )
{
this.output.append( "Action occurred: " + event + '\n' );
}
}

class ActionTester {
public static void main(String args[]) {
JFrame frame = new JFrame( "Button Handler" );
JTextArea area = new JTextArea( 6, 80 );
JButton button = new JButton( "Fire Event" );
button.addActionListener( new ButtonHandler( area ) );
frame.add( button, BorderLayout.NORTH );
frame.add( area, BorderLayout.CENTER );
frame.pack();
frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOS
E );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}

Using Introspection to Discover the Events A Bean Fires

The JavaBeans API provides event-oriented design patterns to give introspecting tools
the ability to discover what events a bean can fire. For a bean to be the source of an
event, it must implement methods that add and remove listener objects for that type of
event. The design patterns for these methods are the following:

public void add<EventListenerType>(<EventListenerType> a)


public void remove<EventListenerType>(<EventListenerType> a)
These methods let a source bean know where to fire events. The source bean then fires
events at those listener beans using the methods for those particular interfaces. For
example, if a source bean registers ActionListener objects, it will fire events at those
objects by calling the actionPerformed method on those listeners.
package java.awt.event;
import java.util.EventListener;

public interface ActionListener extends EventListener {

public void actionPerformed(ActionEvent e);


}

Using the NetBeans GUI Builder to Set Events

In the lesson "Using the NetBeans GUI Builder," you learned how to create a MyBean
component, add the yourName property, and design a simple form. Now you will set an
event by which a value entered in the JTextField component is stored in the yourName
property. Use the GUI Builder as follows to set such an event:

1. Left click the MyForm node.


2. Switch to the Connection Mode by clicking the appropriate button on the GUI
Builder toolbar.
3. In the Design Area or Inspector window select the OK button (jButton1). Notice
that the button is highlighted in red when it is selected.
4. In the Inspector window select the myBean1 component.
5. In the Connection wizard's Select Source Event page, select the action|
actionPerformed[jButton1ActionPerformed1] event by expanding the event
type directory nodes, as represented in the following figure.

6. Click the Next button.


7. In the Specify Target Operation page, specify the yourName property in the
MyBean component, and click the Next button.
8. In the Enter Parameters page, specify the target property by selecting the Property
radio button.
9. Press the ellipsis (...) button to display the Select Property dialog box.
10. In the Select Property dialog box select the jTextField component from the
Component combobox and choose the text property from the list that is presented,
as shown on the following figure.
11. Click the Finish button.

The Source Editor window is now displayed. Since the GUI Builder automatically
generates the code to connect the form's components, the following code will be added to
the MyForm class:

private void jButton1ActionPerformed(java.awt.event.ActionEvent


evt) {
myBean1.setYourName(jTextField1.getText());

Lesson: Bean Persistence

A bean has the property of persistence when its properties, fields, and state information
are saved to and retrieved from storage. Component models provide a mechanism for
persistence that enables the state of components to be stored in a non-volatile place for
later retrieval.

The mechanism that makes persistence possible is called serialization. Object


serialization means converting an object into a data stream and writing it to storage. Any
applet, application, or tool that uses that bean can then "reconstitute" it by deserialization.
The object is then restored to its original state.

For example, a Java application can serialize a Frame window on a Microsoft Windows
machine, the serialized file can be sent with e-mail to a Solaris machine, and then a Java
application can restore the Frame window to the exact state which existed on the
Microsoft Windows machine.
Any applet, application, or tool that uses that bean can then "reconstitute" it by
deserialization.

All beans must persist. To persist, your beans must support serialization by implementing
either the java.io.Serializable(in the API reference documentation) interface, or the
java.io.Externalizable(in the API reference documentation) interface. These
interfaces offer you the choices of automatic serialization and customized serialization. If
any class in a class's inheritance hierarchy implements Serializable or
Externalizable, then that class is serializable.

Classes That Are Serializable

Any class is serializable as long as that class or a parent class implements the
java.io.Serializable interface. Examples of serializable classes include Component,
String, Date, Vector, and Hashtable. Thus, any subclass of the Component class,
including Applet, can be serialized. Notable classes not supporting serialization include
Image, Thread, Socket, and InputStream. Attempting to serialize objects of these types
will result in an NotSerializableException.

The Java Object Serialization API automatically serializes most fields of a Serializable
object to the storage stream. This includes primitive types, arrays,and strings. The API
does not serialize or deserialize fields that are marked transient or static.

Controlling Serialization

You can control the level of serialization that your beans undergo. Three ways to control
serilization are:

• Automatic serialization, implemented by the Serializable interface. The Java


serialization software serializes the entire object, except transient and static fields.
• Customized serialization. Selectively exclude fields you do not want serialized by
marking with the transient (or static) modifier.
• Customized file format, implemented by the Externalizable interface and its
two methods. Beans are written in a specific file format.

Default Serialization: The Serializable Interface

The Serializable interface provides automatic serialization by using the Java Object
Serialization tools. Serializable declares no methods; it acts as a marker, telling the
Object Serialization tools that your bean class is serializable. Marking your class
Serializable means you are telling the Java Virtual Machine (JVM) that you have
made sure your class will work with default serialization. Here are some important points
about working with the Serializable interface:
• Classes that implement Serializable must have an access to a no-argument
constructor of supertype. This constructor will be called when an object is
"reconstituted" from a .ser file.
• You don't need to implement Serializable in your class if it is already
implemented in a superclass.
• All fields except static and transient fields are serialized. Use the transient
modifier to specify fields you do not want serialized, and to specify classes that
are not serializable.

Selective Serialization Using the transient Keyword

To exclude fields from serialization in a Serializable object mark the fields with the
transient modifier.

transient int status;


Default serialization will not serialize transient and static fields.

Selective Serialization: writeObject and readObject

If your serializable class contains either of the following two methods (the signatures
must be exact), then the default serialization will not take place.

private void writeObject(java.io.ObjectOutputStream out)


throws IOException;
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;

You can control how more complex objects are serialized, by writing your own
implementations of the writeObject and readObject methods. Implement
writeObject when you need to exercise greater control over what gets serialized when
you need to serialize objects that default serialization cannot handle, or when you need to
add data to the serialization stream that is not an object data member. Implement
readObject to reconstruct the data stream you wrote with writeObject.

The Externalizable Interface

Use the Externalizable interface when you need complete control over your bean's
serialization (for example, when writing and reading a specific file format). To use the
Externalizable interface you need to implement two methods: readExternal and
writeExternal. Classes that implement Externalizable must have a no-argument
constructor.

Lesson: Long Term Persistence

Long-term persistence is a model that enables beans to be saved in XML format.


Information on the XML format and on how to implement long-term persistence for non-
beans can be found in XML Schema and Using XMLEncoder .

Encoder and Decoder

The XMLEncoder class is assigned to write output files for textual representation of
Serializable objects. The following code fragment is an example of writing a Java
bean and its properties in XML format:

XMLEncoder encoder = new XMLEncoder(


new BufferedOutputStream(
new FileOutputStream( "Beanarchive.xml" ) ) );

encoder.writeObject( object );
encoder.close();

The XMLDecoder class reads an XML document that was created with XMLEncoder:

XMLDecoder decoder = new XMLDecoder(


new BufferedInputStream(
new FileInputStream( "Beanarchive.xml" ) ) );

Object object = decoder.readObject();


decoder.close();

What's in XML?

An XML bean archive has its own specific syntax, which includes the following tags to
represent each bean element:

• an XML preamble to describe a version of XML and type of encoding


• a <java> tag to embody all object elements of the bean
• an <object> tag to represent a set of method calls needed to reconstruct an object
from its serialized form
• <object class="javax.swing.JButton" method="new">
• <string>Ok</string>
• </object>

or statements

<object class="javax.swing.JButton">
<void method="setText">
<string>Cancel</string>
</void>
</object>

• tags to define appropriate primitive types:


o <boolean>
o <byte>
o <char>
o <short>
o <int>
o <long>
o <float>
o <double>
• <int>5555</int>

• a <class> tag to represent an instance of Class.


• <class>java.swing.JFrame</class>

• an <array> tag to define an array


• <array class="java.lang.String" length="5">
• </array>

The following code represents an XML archive that will be generated for the
SimpleBean component:

<?xml version="1.0" encoding="UTF-8" ?>


<java>
<object class="javax.swing.JFrame">
<void method="add">
<object class="java.awt.BorderLayout" field="CENTER"/>
<object class="SimpleBean"/>
</void>
<void property="defaultCloseOperation">
<object class="javax.swing.WindowConstants"
field="DISPOSE_ON_CLOSE"/>
</void>
<void method="pack"/>
<void property="visible">
<boolean>true</boolean>
</void>
</object>
</java>

Lesson: Introspection

Introspection is the automatic process of analyzing a bean's design patterns to reveal the
bean's properties, events, and methods. This process controls the publishing and
discovery of bean operations and properties. This lesson explains the purpose of
introspection, introduces the Introspection API, and gives an example of introspection
code.
Purpose of Introspection

A growing number of Java object repository sites exist on the Internet in answer to the
demand for centralized deployment of applets, classes, and source code in general. Any
developer who has spent time hunting through these sites for licensable Java code to
incorporate into a program has undoubtedly struggled with issues of how to quickly and
cleanly integrate code from one particular source into an application.

The way in which introspection is implemented provides great advantages, including:

1. Portability - Everything is done in the Java platform, so you can write


components once, reuse them everywhere. There are no extra specification files
that need to be maintained independently from your component code. There are
no platform-specific issues to contend with. Your component is not tied to one
component model or one proprietary platform. You get all the advantages of the
evolving Java APIs, while maintaining the portability of your components.
2. Reuse - By following the JavaBeans design conventions, implementing the
appropriate interfaces, and extending the appropriate classes, you provide your
component with reuse potential that possibly exceeds your expectations.

Introspection API

The JavaBeans API architecture supplies a set of classes and interfaces to provide
introspection.

The BeanInfo (in the API reference documentation) interface of the java.beans
package defines a set of methods that allow bean implementors to provide explicit
information about their beans. By specifying BeanInfo for a bean component, a developer
can hide methods, specify an icon for the toolbox, provide descriptive names for
properties, define which properties are bound properties, and much more.

The getBeanInfo(beanName) (in the API reference documentation) of the


Introspector (in the API reference documentation) class can be used by builder tools
and other automated environments to provide detailed information about a bean. The
getBeanInfo method relies on the naming conventions for the bean's properties, events,
and methods. A call to getBeanInfo results in the introspection process analyzing the
bean’s classes and superclasses.

The Introspector class provides descriptor classes with information about properties,
events, and methods of a bean. Methods of this class locate any descriptor information
that has been explicitly supplied by the developer through BeanInfo classes. Then the
Introspector class applies the naming conventions to determine what properties the
bean has, the events to which it can listen, and those which it can send.

The following figure represents a hierarchy of the FeatureDescriptor classes:


Each class represented in this group describes a particular attribute of the bean. For
example, the isBound method of the PropertyDescriptor class indicates whether a
PropertyChangeEvent event is fired when the value of this property changes.

Editing Bean Info with the NetBeans BeanInfo Editor

To open the BeanInfo dialog box, expand the appropriate class hierarchy to the bean
Patterns node. Right-click the bean Patterns node and choose BeanInfo Editor from the
pop-up menu. All elements of the selected class that match bean-naming conventions will
be displayed at the left in the BeanInfo Editor dialog box as shown in the following
figure:
Select one of the following nodes to view and edit its properties at the right of the dialog
box:

• BeanInfo
• Bean
• Properties
• Methods
• Event Sources

Special symbols (green and red) appear next to the subnode to indicate whether an
element will be included or excluded from the BeanInfo class.

If the Get From Introspection option is not selected, the node's subnodes are available for
inclusion in the BeanInfo class. To include all subnodes, right-click a node and choose
Include All. You can also include each element individually by selecting its subnode and
setting the Include in BeanInfo property. If the Get From Introspection option is selected,
the setting the properties of subnodes has no effect in the generated BeanInfo code.

The following attributes are available for the nodes for each bean, property, event
sources, and method:

• Name - A name of the selected element as it appears in code.


• Preferred - An attribute to specify where this property appears in the Inspector
window under the Properties node.
• Expert - An attribute to specify where this property appears in the Inspector
window under the Other Properties node.
• Hidden - An attribute to mark an element for tool use only.
• Display Name Code - A display name of the property.
• Short Description Code - A short description of the property.
• Include in BeanInfo - An attribute to include the selected element in the BeanInfo
class.
• Bound - An attribute to make the bean property bound.
• Constrained - An attribute to make the bean property constrained.
• Mode - An attribute to set the property's mode and generate getter and setter
methods.
• Property Editor Class - An attribute to specify a custom class to act as a property
editor for the property.

For Event Source nodes, the following Expert properties are available:

• Unicast (read-only)
• In Default Event Set

Introspection Sample

The following example represents code to perform introspection:


import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;

public class SimpleBean


{
private final String name = "SimpleBean";
private int size;

public String getName()


{
return this.name;
}

public int getSize()


{
return this.size;
}

public void setSize( int size )


{
this.size = size;
}

public static void main( String[] args )


throws IntrospectionException
{
BeanInfo info = Introspector.getBeanInfo( SimpleBean.class );
for ( PropertyDescriptor pd : info.getPropertyDescriptors() )
System.out.println( pd.getName() );
}
}

This example creates a non-visual bean and displays the following properties derived
from the BeanInfo object:

• class
• name
• size

Note that a class property was not defined in the SimpleBean class. This property was
inherited from the Object class. To get properties defined only in the SimpleBean class,
use the following form of the getBeanInfo method:

Introspector.getBeanInfo( SimpleBean.class, Object.class );

Lesson: Bean Customization


Customization provides a means for modifying the appearance and behavior of a bean
within an application builder so it meets your specific needs. There are several levels of
customization available for a bean developer to allow other developers to get maximum
benefit from a bean’s potential functionality.

The following links are useful for learning about property editors and customizers:

• PropertyEditor(in the API reference documentation) interface


• PropertyEditorSupport(in the API reference documentation) class
• PropertyEditorManager(in the API reference documentation) class
• Customizer(in the API reference documentation) interface
• BeanInfo(in the API reference documentation) interface

A bean's appearance and behavior can be customized at design time within beans-
compliant builder tools. There are two ways to customize a bean:

• By using a property editor. Each bean property has its own property editor. The
NetBeans GUI Builder usually displays a bean's property editors in the Properties
window. The property editor that is associated with a particular property type
edits that property type.
• By using customizers. Customizers give you complete GUI control over bean
customization. Customizers are used where property editors are not practical or
applicable. Unlike a property editor, which is associated with a property, a
customizer is associated with a bean.

Property Editors

A property editor is a tool for customizing a particular property type. Property editors are
activated in the Properties window. This window determines a property's type, searches
for a relevant property editor, and displays the property's current value in a relevant way.

Property editors must implement the PropertyEditor interface, which provides methods
to specify how a property should be displayed in a property sheet. The following figure
represents the Properties window containing myBean1 properties:
You begin the process of editing these properties by clicking the property entry. Clicking
most of these entries will bring up separate panels. For example, to set up the
foreground or background use selection boxes with choices of colors, or press the "..."
button to work with a standard ColorEditor window. Clicking on the toolTipText
property opens a StringEditor window.

The support class PropertyEditorSupport provides a default implementation of the


PropertyEditor interface. By subclassing your property editor from
PropertyEditorSupport, you can simply override the methods you need.

To display the current property value "sample" within the Properties window, you need to
override isPaintable to return true. You then must override paintValue to paint the
current property value in a rectangle in the property sheet. Here's how ColorEditor
implements paintValue:

public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {


Color oldColor = gfx.getColor();
gfx.setColor(Color.black);
gfx.drawRect(box.x, box.y, box.width-3, box.height-3);
gfx.setColor(color);
gfx.fillRect(box.x+1, box.y+1, box.width-4, box.height-4);
gfx.setColor(oldColor);
}

To support the custom property editor, override two more methods. Override
supportsCustomEditor to return true, and then override getCustomEditor to return a
custom editor instance. ColorEditor.getCustomEditor returns this.
In addition, the PropertyEditorSupport class maintains a PropertyChangeListener
list, and fires property change event notifications to those listeners when a bound
property is changed.

How Property Editors are Associated with Properties

Property editors are discovered and associated with a given property in the following
ways:

• Explicit association by way of a BeanInfo object. The editor of the title's property
is set with the following line of code:
• pd.setPropertyEditorClass(TitleEditor.class);
• Explicit registration by way of the
java.beans.PropertyEditorManager.registerEditor method. This method
takes two arguments: the bean class type, and the editor class to be associated
with that type.
• Name search. If a class has no explicitly associated property editor, then the
PropertyEditorManager searchs for that class's property editor in the following
ways:
o Appending "Editor" to the fully qualified class name. For example, for the
my.package.ComplexNumber class, the property editor manager would
search for the my.package.ComplexNumberEditor class.
o Appending "Editor" to the class name and searching a class path.

Customizers

You have learned that builder tools provide support for you to create your own property
editors. What other needs should visual builders meet for complex, industrial-strength
beans? Often it is undesirable to have all the properties of a bean revealed on a single
(sometimes huge) property sheet. What if one single root choice about the type of the
bean rendered half the properties irrelevant? The JavaBeans specification provides for
user-defined customizers, through which you can define a higher level of customization
for bean properties than is available with property editors.

When you use a bean Customizer, you have complete control over how to configure or
edit a bean. A Customizer is an application that specifically targets a bean's
customization. Sometimes properties are insufficient for representing a bean's
configurable attributes. Customizers are used where sophisticated instructions would be
needed to change a bean, and where property editors are too primitive to achieve bean
customization.

All customizers must:

• Extend java.awt.Component or one of its subclasses.


• Implement the java.beans.Customizer interface This means implementing
methods to register PropertyChangeListener objects, and firing property
change events at those listeners when a change to the target bean has occurred.
• Implement a default constructor.
• Associate the customizer with its target class via BeanInfo.getBeanDescriptor.

Lesson: Using the BeanContext API

As stated in the specification, the purpose of the Extensible Runtime Containment and
Services Protocol is "to introduce the concept of a relationship between a Component
and its environment, or Container, wherein a newly instantiated Component is provided
with a reference to its Container or Embedding Context. The Container, or Embedding
Context not only establishes the hierarchy or logical structure, but it also acts as a
service provider that Components may interrogate in order to determine, and
subsequently employ, the services provided by their Context."

Overview of the BeanContext API

This section introduces extensible mechanisms and represents inheritance diagram of the
BeanContext API.

Bean Context #1: Containment Only

This section teaches how to use the BeanContextSupport class to provide the basic
BeanContext functionality.

Bean Context #2: Containment and Services

This section teaches how to use service capability defined by the BeanContextServices
interface.

AWT Containers and the BeanContextProxy Interface

This section describes how an AWT Container can act as a BeanContext.

Additional Resources

• The Extensible Runtime Containment and Services Protocol Specification


• The java.beans.beancontext API documentation

Overview of the BeanContext API


The Extensible Runtime Containment and Services Protocol supports extensible
mechanisms that:

• Introduce an abstraction for the environment, or context, in which a JavaBean


logically functions during its life-cycle, that is a hierarchy of JavaBeans
• Enable the dynamic addition of arbitrary services to a JavaBean's environment
• Provide a single service discovery mechanism through which JavaBeans may
interrogate their environment in order both to ascertain the availability of
particular services and to subsequently employ those services.
• Provide better support for JavaBeans that are also Applets.

In English, this means that there now exists a standard mechanism through which Java
developers can logically group a set of related JavaBeans into a "context" that the beans
can become aware of and/or interact with. This context, or "containing environment", is
known as the BeanContext.

There are two distinct types of BeanContext included in this protocol: one which
supports membership only (interface java.beans.beancontext.BeanContext) and one
which supports membership and offers services (interface
java.beans.beancontext.BeanContextServices) to its JavaBeans nested within.

To orient yourself with the classes and interfaces of java.beans.beancontext, take a


minute to look over the following hierarchy diagram. You will notice that the majority of
the package is defined as interfaces, which allow for multiple inheritance.

Inheritance Diagram of the BeanContext API

The classes and interfaces relevant to the BeanContext API are listed in the following
diagrams. As you study the diagrams, take note of the BeanContext and
BeanContextServices interfaces, and that each has its own concrete implementation
that you can subclass or instantiate directly (classes
java.beans.beancontext.BeanContextSupport and
java.beans.beancontext.BeanContextServicesSupport respectively). Also take
note of the location of the java.beans.beancontext.BeanContextChild interface.
This is the interface that allows nested JavaBeans to become aware of their enclosing
BeanContext.
Bean Context #1: Containment Only

The "containment" portion of the Extensible Runtime Containment and Services Protocol
is defined by the BeanContext interface. In its most basic form, a BeanContext is used
to logically group a set of related java beans, bean contexts, or arbitrary objects.
JavaBeans nested into a BeanContext are known as "child" beans. Once nested, a child
bean can query its BeanContext for various membership information, as illustrated in the
following examples.

Here are some possible BeanContext containment scenarios:


The sample code presented in this chapter uses instances of the BeanContextSupport
helper class to provide the basic BeanContext functionality. A BeanContextSupport
object is simply a concrete implementation of the BeanContext interface.

With a BeanContextSupport instance, it is possible to:

• Add an object, bean, or BeanContext: boolean add(Object o)


• Remove an object, bean, or BeanContext: boolean remove(Object o)
• Add a BeanContextMembershipListener: void
addBeanContextMembershipListener(BeanContextMembershipListener
bcml)
• Remove a BeanContextMembershipListener: void
removeBeanContextMembershipListener(BeanContextMembershipListener
bcml)
• Get all JavaBean or BeanContext instances currently nested in this BeanContext
as an array or as an Iterator: Object[] toArray(), Object[]
toArray(Object[] a), and Iterator iterator()
• Determine whether or not a specified object is currently a child of the
BeanContext: boolean contains(Object o)
• Get the number of children currently nested in this BeanContext: int size()
• Determine whether or not the BeanContext currently has zero children: boolean
isEmpty()
• Instantiate a new JavaBean instance as a child of the target BeanContext: Object
instantiateChild(String beanName)

The following test programs, which are run from the command line, illustrate the use of
these methods.

The comments in the source code explain the purpose of each.

File: Example1.java
import java.beans.beancontext.*;

public class Example1 {


private static BeanContextSupport context = new
BeanContextSupport(); // The BeanContext
private static BeanContextChildSupport bean = new
BeanContextChildSupport(); // The JavaBean

public static void main(String[] args) {


report();

// Add the bean to the context


System.out.println("Adding bean to context...");
context.add(bean);

report();
}

private static void report() {


// Print out a report of the context's membership state.
System.out.println("===========================================
==");

// Is the context empty?


System.out.println("Is the context empty? " +
context.isEmpty());

// Has the context been set for the child bean?


boolean result = (bean.getBeanContext()!=null);
System.out.println("Does the bean have a context yet? " +
result);

// Number of children in the context


System.out.println("Number of children in the context: " +
context.size());

// Is the specific bean a member of the context?


System.out.println("Is the bean a member of the context? " +
context.contains(bean));

// Equality test
if (bean.getBeanContext() != null) {
boolean isEqual = (bean.getBeanContext()==context); // true
means both references point to the same object
System.out.println("Contexts are the same? " + isEqual);
}
System.out.println("===========================================
==");
}
}
Output:
=============================================
Is the context empty? true
Does the bean have a context yet? false
Number of children in the context: 0
Is the bean a member of the context? false
=============================================
Adding bean to context...
=============================================
Is the context empty? false
Does the bean have a context yet? true
Number of children in the context: 1
Is the bean a member of the context? true
Contexts are the same? true
=============================================
File: Example2.java
import java.beans.beancontext.*;

public class Example2 {


public static void main(String[] args) {

// A BeanContext
BeanContextSupport context = new BeanContextSupport();

// Many JavaBeans
BeanContextChildSupport[] beans = new
BeanContextChildSupport[100];

System.out.println("Number of children in the context: " +


context.size());

// Create the beans and add them to the context


for (int i = 0; i < beans.length; i++) {
beans[i] = new BeanContextSupport();
context.add(beans[i]);
}
System.out.println("Number of children in the context: " +
context.size());

// Context now has 100 beans in it, get references to them all
Object[] children = context.toArray();
System.out.println("Number of objects retrieved from the
context: " + children.length);
}
}
Output:
Number of children in the context: 0
Number of children in the context: 100
Number of objects retrieved from the context: 100
File: Example3.java
import java.beans.beancontext.*;
import java.io.*;

public class Example3 {


public static void main(String[] args) {
BeanContextSupport context = new BeanContextSupport();
System.out.println("Number of children nested into the context:
" + context.size());

BeanContextChildSupport child = null;


try {
child =
(BeanContextChildSupport)context.instantiateChild("java.beans.beanconte
xt.BeanContextChildSupport");
}
catch(IOException e){
System.out.println("IOException occurred: " +
e.getMessage());
}
catch(ClassNotFoundException e){
System.out.println("Class not found: " + e.getMessage());
}
System.out.println("Number of children nested into the context:
" + context.size());
}
}
Output:
Number of children nested into the context: 0
Number of children nested into the context: 1

BeanContextMembershipEvent Notification

The BeanContext API uses the standard Java event model to register listeners and
deliver events. For an overview of this standard event model, refer to Writing Event
Listeners. For details about handling specific events, see Writing Event Listeners.

In a basic BeanContext, the event classes and interfaces involved are:

• java.beans.beancontext.BeanContextMembershipEvent: Encapsulates the


list of children added to, or removed from, the membership of a particular
BeanContext. An instance of this event is fired whenever a successful add(),
remove(), retainAll(), removeAll(), or clear() is invoked on a given BeanContext
instance.
• java.beans.BeanContextMembershipListener: Objects wishing to receive
BeanContextMembershipEvents implement this interface. It defines methods
void childrenAdded(BeanContextMembershipEvent bcme) and void
childrenRemoved(BeanContextMembershipEvent bcme), which are called
when a child is added to or removed from a given BeanContext instance.

BeanContextMembershipEvent Notification: Sample Code

File: MembershipTest.java
import java.beans.beancontext.*;

public class MembershipTest {


public static void main(String[] args) {
BeanContextSupport context = new BeanContextSupport(); // the
context
MyMembershipListener listener = new MyMembershipListener();
BeanContextChildSupport bean = new
BeanContextChildSupport(); // a JavaBean
context.addBeanContextMembershipListener(listener); // now
listening!
context.add(bean);
context.remove(bean);
}
}

class MyMembershipListener implements BeanContextMembershipListener {


public void childrenAdded(BeanContextMembershipEvent bcme) {
System.out.println("Another bean has been added to the
context.");
}

public void childrenRemoved(BeanContextMembershipEvent bcme) {


System.out.println("A bean has been removed from the context.");
}
}
Output:
Another bean has been added to the context.
A bean has been removed from the context.

The same example, implemented using an anonymous inner class


import java.beans.beancontext.*;

public class MembershipTest {


public static void main(String[] args) {
BeanContextSupport context = new BeanContextSupport();
context.addBeanContextMembershipListener(new
BeanContextMembershipListener() {
public void childrenAdded(BeanContextMembershipEvent bcme) {
System.out.println("Another bean has been added to the
context.");
}

public void childrenRemoved(BeanContextMembershipEvent


bcme) {
System.out.println("A bean has been removed from the
context.");
}
});
BeanContextChildSupport bean = new BeanContextChildSupport();
context.add(bean);
context.remove(bean);
}
}
Output:
Another bean has been added to the context.
A bean has been removed from the context.
« Previous • Trail • Next »

Bean Context #2: Containment and Services

As mentioned in the introduction, the BeanContext API also provides a standard


mechanism through which JavaBeans can discover and utilize the services offered by
their enclosing BeanContext. Service capability is defined by the BeanContextServices
interface. Because this interface is a BeanContext extension, it inherits all BeanContext
membership capabilities. The discovery and requesting of services can be summarized in
the following steps:

1. A JavaBean that implements the


java.beans.beancontext.BeanContextServicesListener interface joins the
bean context (the context itself is a BeanContextServices implementation), and
registers its intent to be notified of new services via the context's
addBeanContextServicesListener(BeanContextServicesListener bcsl)
method.
2. A java.beans.beancontext.BeanContextServiceProvider registers a new
service with the context via the context's addService() method. The context
notifies all currently registered listeners that this new service has been added.
3. After being notified of the newly available service, the listening JavaBean
requests an instance of the service from the context.
4. The context tells the service provider to deliver the service to the requesting
JavaBean.

BeanContextServices: Service Related Methods

Using a java.beans.beancontext.BeanContextServicesSupport object as the bean


context, it is possible to:

• Add a service to this BeanContext: boolean addService(java.lang.Class


serviceClass, BeanContextServiceProvider serviceProvider)
• Add a service to this BeanContext: boolean addService(Class
serviceClass, BeanContextServiceProvider bcsp, boolean fireEvent)
• Revoke a service: void revokeService(java.lang.Class serviceClass,
BeanContextServiceProvider serviceProvider, boolean
revokeCurrentServicesNow)
• Release a BeanContextChild's (or any arbitrary object associated with a
BeanContextChild) reference to the specified service: void
releaseService(BeanContextChild child, java.lang.Object
requestor, java.lang.Object service)
• Add a BeanContextServicesListener void
addBeanContextServicesListener(BeanContextServicesListener bcsl)
• Remove a BeanContextServicesListener: void
removeBeanContextServicesListener(BeanContextServicesListener
bcsl)
• Get the currently available services for this context: Iterator
getCurrentServiceClasses()
• Determine whether or not a given service is currently available from this context:
boolean hasService(java.lang.Class serviceClass)
• Get a service from the context: Object getService(BeanContextChild
child, java.lang.Object requestor, java.lang.Class serviceClass,
java.lang.Object serviceSelector,
BeanContextServiceRevokedListener bcsrl)
• Get the list of service dependent service parameters (Service Selectors) for the
specified service: Iterator getCurrentServiceSelectors(java.lang.Class
serviceClass)

Service Event Notification

JavaBeans nested into a BeanContextServices implement


BeanContextServicesListener to listen for new services being added, and/or
BeanContextServiceRevokedListener to listen for services being revoked.

There are two event types that may be intercepted by such listeners:

• BeanContextServiceAvailableEvent: received by the


BeanContextServicesListener in order to identify the service being registered.
• BeanContextServiceRevokedEvent: received by the
BeanContextServiceRevokedListener in order to identify the service being
revoked.

The Service Provider

JavaBeans can query their enclosing bean context for a list of available services, or ask
for a specific service by name. The service itself, however, is actually delivered by a
BeanContextServiceProvider. The provider can be any object that implements the
java.beans.beancontext.BeanContextServiceProvider interface. Services become
available in a context via the bean context's addService() registration method.

BeanContextServiceProvider offers the following three methods, which will be


automatically called when a bean requests (or releases) a service from its context:

• Object getService(BeanContextServices bcs, java.lang.Object


requestor, java.lang.Class serviceClass, java.lang.Object
serviceSelector)
• Iterator getCurrentServiceSelectors(BeanContextServices bcs,
java.lang.Class serviceClass)
• public void releaseService(BeanContextServices bcs,
java.lang.Object requestor, java.lang.Object service) Release a
service from any object that currently has a reference to it

The Service

The service itself is best described by this paragraph from the specification:

A service, represented by a Class object, is typically a reference to either an interface, or


to an implementation that is not publicly instantiable. This Class defines an interface
protocol or contract between a BeanContextServiceProvider, the factory of the service,
and an arbitrary object associated with a BeanContextChild that is currently nested
within the BeanContext the service is registered with.
The following section presents a sample application that uses a word counting service to
count the number of words in a given text file.

A Word Counting Service Example

The classes defined in this sample application are:

• DocumentBean.java: A JavaBean that encapsulates a File object. Create an


instance of this bean by passing it a String indicating the name of the text file to
represent. This bean extends BeanContextChildSupport, which allows it to
listen for addition/revocation of services in its context. When the bean detects that
a WordCount service has been added to the context, it requests the service to count
the number of words it contains.
• WordCountServiceProvider.java: A class that acts as the factory for delivering
the WordCount service. This class implements the
BeanContextServiceProvider interface.
• WordCount.java: This interface defines the service itself.
• DocumentTester.java: The main test program.

File: DocumentBean.java

import java.beans.beancontext.*;
import java.io.*;
import java.util.*;

public final class DocumentBean extends BeanContextChildSupport {

private File document;


private BeanContextServices context;

public DocumentBean(String fileName) {


document = new File(fileName);
}

public void serviceAvailable(BeanContextServiceAvailableEvent


bcsae) {
System.out.println("[Detected a service being added to the
context]");

// Get a reference to the context


BeanContextServices context =
bcsae.getSourceAsBeanContextServices();
System.out.println("Is the context offering a WordCount
service? "
+ context.hasService(WordCount.class));

// Use the service, if it's available


if (context.hasService(WordCount.class)) {
System.out.println("Attempting to use the service...");
try {
WordCount service = (WordCount)context.getService(this,
this,

WordCount.class, document, this);


System.out.println("Got the service!");
service.countWords();
} catch(Exception e) { }
}
}

public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) {


System.out.println("[Detected a service being revoked from the
context]");
}
}

File: WordCountServiceProvider.java

import java.beans.beancontext.*;
import java.util.*;
import java.io.*;

public final class WordCountServiceProvider implements


BeanContextServiceProvider {

public Object getService(BeanContextServices bcs,


Object requestor,
Class serviceClass,
Object serviceSelector) {

// For this demo, we know that the cast from serviceSelector


// to File will always work.
final File document = (File)serviceSelector;

return new WordCount() {


public void countWords() {
try {
// Create a Reader to the DocumentBean's File
BufferedReader br = new BufferedReader(new
FileReader(document));
String line = null;
int wordCount = 0;
while ((line = br.readLine()) != null) {
StringTokenizer st = new StringTokenizer(line);
while (st.hasMoreTokens()) {
System.out.println("Word " + (++wordCount)
+ " is: " +
st.nextToken());
}
}
System.out.println("Total number of words in the
document: "
+ wordCount);
System.out.println("[WordCount service brought to
you by WordCountServiceProvider]");
br.close();
} catch(Exception e) { }
}
};
}

public void releaseService(BeanContextServices bcs,


Object requestor,
Object service) {
// do nothing
}

public Iterator getCurrentServiceSelectors(BeanContextServices bcs,


Class serviceClass) {
return null; // do nothing
}
}

File: WordCount.java

public interface WordCount {

public abstract void countWords();

File: DocumentTester.java

import java.beans.beancontext.*;
import java.util.*;

public class DocumentTester {

public static void main(String[] args) {


BeanContextServicesSupport context = new
BeanContextServicesSupport(); // a bean context
DocumentBean doc1 = new DocumentBean("Test.txt");
context.add(doc1);
context.addBeanContextServicesListener(doc1); // listen for
new services
WordCountServiceProvider provider = new
WordCountServiceProvider();
context.addService(WordCount.class, provider); // add the
service to the context
}
}

File: Test.txt

This text will be analyzed

by the WordCount
service.

Output:

[Detected a service being added to the context]


Is the context offering a WordCount service? true
Attempting to use the service...
Got the service!
Word 1 is: This
Word 2 is: text
Word 3 is: will
Word 4 is: be
Word 5 is: analyzed
Word 6 is: by
Word 7 is: the
Word 8 is: WordCount
Word 9 is: service.
Total number of words in the document: 9
[WordCount service brought to you by WordCountServiceProvider]

AWT Containers and the BeanContextProxy Interface

Sometimes, it is desirable for an AWT Container to act as a BeanContext. However,


AWT Containers cannot implement the BeanContext interface directly, because of a
method name collision between Component and Collection. If some AWT Component
needs to act as a BeanContext, it must internally create a BeanContext instance and
delegate work to it. Third parties, such as visual builder tools, can discover this
BeanContext instance if the Component implements the BeanContextProxy interface.

The BeanContextProxy Interface

public BeanContextChild getBeanContextProxy() - Gets the BeanContextChild (or


subinterface) associated with this object.

You might also like