Subpages: 1. JComponent properties, size, and positioning
2. Event handling and dispatching
3. Multithreading
4. Timers
5. AppContext service
6. Inside Timers & the TimerQueue
7. JavaBeans architecture
8. Fonts, Colors, Graphics and text
9. Using the Graphics clipping area
10. Graphics debugging
11. Painting and validation
12. Focus Management
13. Keyboard input, KeyStrokes, and Actions
14. SwingUtilities
2.7 JavaBeans architecture
Since we are concerned with creating Swing applications in this book, we need to understand and appreciate the fact that every component in Swing is a JavaBean.
Note: If you are familiar with the JavaBeans component model you may want to skip to the next section.
2.7.1 The JavaBeans component model
The JavaBeans specification identifies five features that each bean is expected to provide. We will review these features here, along with the classes and mechanisms that make them possible. The first thing to do is think of a simple component, such as a button, and apply what we discuss here to this component. Second, we are assuming basic knowledge of the Java Reflection API:
"Instances of Class represent classes and interfaces in a running Java application."API
"A Method provides information about, and access to, a single method on a class or interface."API
"A Field provides information about, and dynamic access to, a single field of a class or an interface."API
2.7.2 Introspection
Introspection is the ability to discover the methods, properties, and events information of a bean. This is accomplished through use of the java.beans.Introspector class. Introspector provides static methods to generate a BeanInfo object containing all discoverable information about a specific bean. This includes information from each of a bean's superclasses, unless we specify which superclass introspection should stop at (i.e. we can specify the 'depth' of an instrospection). The following retrieves all discoverable information of a bean:
BeanInfo myJavaBeanInfo =
Introspector.getBeanInfo(myJavaBean);
A BeanInfo object partitions all of a bean's information into several groups, some of which are:
A BeanDescriptor: provides general descriptive information such as a display name.
An array of EventSetDescriptors: provides information about a set of events a bean fires. These can be used to, among other things, retrieve that bean's event listener related methods as Method instances.
An array of MethodDescriptors: provides information about the methods of a bean that are externally accessible (this would include, for instance, all public methods). This information is used to construct a Method instance for each method.
An array of PropertyDescriptors: provides information about each property that a bean maintains which can be accessed through get, set, and/or is methods. These objects can be used to construct Method and Class instances corresponding to that property's accessor methods and class type respectively.
2.7.3 Properties
As we discussed in section 2.1.1, beans support different types of properties. Simple properties are variables such that, when modified, a bean will do nothing. Bound and constrained properties are variables such that, when modified, a bean will send notification events to any listeners. This notification takes the form of an event object which contains the property name, the old property value, and the new property value. Whenever a bound property changes, the bean should send out a PropertyChangeEvent. Whenever a constrained property is about to change, the bean should send out a PropertyChangeEvent before the change occurs, allowing it to possibly be vetoed. Other objects can listen for these events and process them accordingly (which leads to communication).
Associated with properties are a bean's setXX()/getXX() and isXX() methods. If a setXX() method is available the associated property is said to be writeable. If a getXX() or isXX() method is available the associated property is said to be readable. An isXX() method normally corresponds to retrieval of a boolean property (occasionaly getXX() methods are used for this as well).
2.7.4 Customization
A bean's properties are exposed through its setXX()/getXX() and isXX() methods, and can be modified at run-time (or design-time). JavaBeans are commonly used in interface development environments where property sheets can be displayed for each bean allowing read/write (depending on the available accessors) property functionality.
2.7.5 Communication
Beans are designed to send events that notify all event listeners registered with that bean, when a bound or constrained property changes value. Apps are constructed by registering listeners from bean to bean. Since we can use introspection to determine event sending and receiving information about any bean, design tools can take advantage of this knowledge to allow more powerful, design-time customization. Communication is the basic glue that holds an interactive GUI together.
2.7.6 Persistency
All JavaBeans must implement the Serializable interface (directly or indirectly) to allow serialization of their state into persistent storage (storage that exists beyond program termination). All objects are saved except those declared transient. (Note that JComponent directly implements this interface.)
Classes which need special processing during serialization need to implement the following private methods:
private void writeObject(java.io.ObjectOutputStreamout) and
private void readObject(java.io.ObjectInputStream in )
These methods are called to write or read an instance of this class to a stream. Note that the default serialization mechanism will be invoked to serialize all sub-classes because these are private methods. (Refer to the API documentation or Java tutorial for more information about serialization.)
Note: As of the first release of Java 2, JComponent implements readObject() and writeObject() as private. All subclasses need to implement these methods if special processing is desired. Currently long-term persistance is not recommended and is subject to change in future releases. However, there is nothing wrong with implementing Short-term persistance (e.g. for RMI, misc. data transfer, etc.).
Classes that intend to take comple control of their serialization and deserialization should, instead, implement the Externalizable interface.
Two methods are defined in the Externalizable interface:
public void writeExternal(ObjectOutput out)
public void readExternal(ObjectInput in)
These methods will be invoked when writeObject() and readObject() (discussed above) are invoked to handle any serialization/deserialization.
2.7.7 A simple Swing-based JavaBean
The following code demonstrates how to build a Swing-based JavaBean with simple, bound, constrained, and 'change' properties.
The code: BakedBean.java
see \Chapter1\1
import javax.swing.*;
import javax.swing.event.*;
import java.beans.*;
import java.awt.*;
import java.io.*;
public class BakedBean extends JComponent implements Externalizable
{
// Property names (only needed for bound or constrained properties)
public static final String BEAN_VALUE = "Value";
public static final String BEAN_COLOR = "Color";
// Properties
private Font m_beanFont; // simple
private Dimension m_beanDimension; // simple
private int m_beanValue; // bound
private Color m_beanColor; // constrained
private String m_beanString; // change
// Manages all PropertyChangeListeners
protected SwingPropertyChangeSupport m_supporter =
new SwingPropertyChangeSupport(this);
// Manages all VetoableChangeListeners
protected VetoableChangeSupport m_vetoer =
new VetoableChangeSupport(this);
// Only one ChangeEvent is needed since the event's only
// state is the source property. The source of events generated
// is always "this". You'll see this in lots of Swing source.
protected transient ChangeEvent m_changeEvent = null;
// This can manage all types of listeners, as long as we set
// up the fireXX methods to correctly look through this list.
// This makes you appreciate the XXSupport classes.
protected EventListenerList m_listenerList =
new EventListenerList();
public BakedBean() {
m_beanFont = new Font("SanSerif", Font.BOLD | Font.ITALIC, 12);
m_beanDimension = new Dimension(150,100);
m_beanValue = 0;
m_beanColor = Color.black;
m_beanString = "BakedBean #";
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(m_beanColor);
g.setFont(m_beanFont);
g.drawString(m_beanString + m_beanValue,30,30);
}
public void setBeanFont(Font font) {
m_beanFont = font;
}
public Font getBeanFont() {
return m_beanFont;
}
public void setBeanValue(int newValue) {
int oldValue = m_beanValue;
m_beanValue = newValue;
// Notify all PropertyChangeListeners
m_supporter.firePropertyChange(BEAN_VALUE,
new Integer(oldValue), new Integer(newValue));
}
public int getBeanValue() {
return m_beanValue;
}
public void setBeanColor(Color newColor)
throws PropertyVetoException {
Color oldColor = m_beanColor;
// Notify all VetoableChangeListeners before making change
// ...exception will be thrown here if there is a veto
// ...if not we continue on and make the change
m_vetoer.fireVetoableChange(BEAN_COLOR, oldColor, newColor);
m_beanColor = newColor;
m_supporter.firePropertyChange(BEAN_COLOR, oldColor, newColor);
}
public Color getBeanColor() {
return m_beanColor;
}
public void setBeanString(String newString) {
m_beanString = newString;
// Notify all ChangeListeners
fireStateChanged();
}
public String getBeanString() {
return m_beanString;
}
public void setPreferredSize(Dimension dim) {
m_beanDimension = dim;
}
public Dimension getPreferredSize() {
return m_beanDimension;
}
public void setMinimumSize(Dimension dim) {
m_beanDimension = dim;
}
public Dimension getMinimumSize() {
return m_beanDimension;
}
public void addPropertyChangeListener(
PropertyChangeListener l) {
m_supporter.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(
PropertyChangeListener l) {
m_supporter.removePropertyChangeListener(l);
}
public void addVetoableChangeListener(
VetoableChangeListener l) {
m_vetoer.addVetoableChangeListener(l);
}
public void removeVetoableChangeListener(
VetoableChangeListener l) {
m_vetoer.removeVetoableChangeListener(l);
}
// Remember that EventListenerList is an array of
// key/value pairs:
// key = XXListener class reference
// value = XXListener instance
public void addChangeListener(ChangeListener l) {
m_listenerList.add(ChangeListener.class, l);
}
public void removeChangeListener(ChangeListener l) {
m_listenerList.remove(ChangeListener.class, l);
}
// This is typical EventListenerList dispatching code.
// You'll see this in lots of Swing source.
protected void fireStateChanged() {
Object[] listeners = m_listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ChangeListener.class) {
if (m_changeEvent == null)
m_changeEvent = new ChangeEvent(this);
((ChangeListener)listeners[i+1]).stateChanged(m_changeEvent);
}
}
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(m_beanFont);
out.writeObject(m_beanDimension);
out.writeInt(m_beanValue);
out.writeObject(m_beanColor);
out.writeObject(m_beanString);
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
setBeanFont((Font)in.readObject());
setPreferredSize((Dimension)in.readObject());
// Use preferred size for minimum size..
setMinimumSize(getPreferredSize());
setBeanValue(in.readInt());
try {
setBeanColor((Color)in.readObject());
}
catch (PropertyVetoException pve) {
System.out.println("Color change vetoed..");
}
setBeanString((String)in.readObject());
}
public static void main(String[] args) {
JFrame frame = new JFrame("BakedBean");
frame.getContentPane().add(new BakedBean());
frame.setVisible(true);
frame.pack();
}
}
BakedBean has a visual representation (not a requirement for a bean). It has properties: m_beanValue, m_beanColor, m_beanFont, m_beanDimension, and m_beanString. It supports persistency by implementing the Externalizable interface and implementing the writeExternal() and readExternal() methods to control its own serialization (note that the order in which data is written and read match). BakedBean supports customization through its setXX() and getXX() methods, and it supports communication by allowing the registration of PropertyChangeListeners, VetoableChangeListeners, and ChangeListeners. And, without having to do anything special, it supports introspection.
Attaching a main method to display BakedBean in a frame does not get in the way of any JavaBeans functionality. Figure 2.1 shows BakedBean when executed as an application.

Figure 2.1 BakedBean in our custom JavaBeans property editor
<<file figure2-1.gif>>
In chapter 18 (section 18.9) we construct a full-featured JavaBeans property editing environment. Figure 2.2 shows a BakedBean instance in this environment. The BakedBean shown has had its m_beanDimension, m_beanColor, and m_beanValue properties modified with our property editor and was then serialized to disk. What figure 2.2 really shows is an instance of that BakedBean after it had been deserialized (loaded from disk). Note that any Swing component can be created, modified, serialized, and deserialized using this environment because they are all JavaBeans compliant!

Figure 2.2 BakedBean in our custom JavaBeans property editor
<<file figure2-2.gif>>



RSS feed Java FAQ News