The properties of a bean represent and control its behaviour and appearance. JavaBean API allows us to create the following primary property types.
1. Simple Properties
Simple properties of a bean are those that do not depend on other beans or control properties of another bean. Properties are typically declared as private. To access them, get and set methods are used. The names of the get and set methods follow specific rules, known as design patterns. JavaBean-enabled builder/tester tools (such as NetBeans and BeanBox) use these design patterns to do the following:
- Discover the properties of a bean
- Determine the types of the properties
- Display the properties in the property window
- Determine the read/write attribute of the properties
- Find the appropriate property editor for each property type
- Allow us to change the properties
Suppose a bean builder/tester tool encounters the following methods on a bean:
public int getValue() { … }
public void setValue(int v) { … }
The tool infers the following:
- There exists a property name value.
- Its type is int.
- It is readable and writable.
- It displays the value of this property in the property editor.
- Moreover, it finds the property editor that allows us to change the value of the property.
Creating a Simple Property
The following code is used in the bean to create the property propertyName:
private PropertyType propertyName = initialValue;
Providing a Reader Method
The following code is used to provide a reader method:
PropertyType getPropertyName() {
return propertyName;
}
Providing a Writer Method
The following code is used to provide a writer method:
void getPropertyName(PropertyType value) {
propertyName = value;
}
The following example shows how to create a simple property:
//MyBean.java
public class MyBean {
private int value = 0;
/**
* Get the value of value
* @return the value of value
*/
public int getValue() { return value;
}
/**
* Set the value of value
* @param value new value of value
*/
public void setValue(int value) {
this.value = value;
}
}
2. Bound Properties
Sometimes, when a property of a bean changes, you might want to notify another object about this change. This object typically reacts to the change by changing one of its properties. For example, consider two beans Parent and Child, each having two properties, firstName and familyName. The familyName property of Child and Parent must have the same value. So, whenever the familyName property of Parent changes, the familyName property of Child must also be changed to make them synchronized.
This synchronization can be implemented using the bound property. The accessor or modifier methods for a bound property are defined in the same way, except that whenever a bound property changes, a notification is sent to the interested listeners. Whenever a property of a bean changes, a “PropertyChange” event gets fired. The bean generating the event is called source bean. We can register one or more “Listener” objects with a source, so that these objects get notified when a bound property of the source bean is updated.
Bean Development Kit (BDK) provides special classes to accomplish coordination between the notifier and listener.
PropertyChangeListener
If an object is interested in being notified about the property changes of a source bean, its class must implement the PropertyChangeListener interface. This interface defines the following method:
void propertyChange(PropertyChangeEvent pce)
The listener’s class must implement this method. If the listener is registered for a property change event, this method gets called when a bound property of the source bean changes. In this method, the listener object reacts to the property change by modifying one or more of its properties.
PropertyChangeEvent
A PropertyChangeEvent object is generated whenever a bound property of a bean changes. This object is then sent from the source bean to all registered PropertyChangeListener objects, as an argument of their respective PropertyChange() method. This class encapsulates the property change information. It provides several useful methods as follows:
String getPropertyName()
Returns the name of the property that was changed and caused this event firing Object getOldValue()
Returns the value of the property as an object before it was changed. The bean writer should typecast it to the desired type.
Object getNewValue()
Returns the value of the property as an object after it was changed. The bean writer must typecast it to the desired type.
PropertyChangeSupport
This class is used by the beans having at least one bound property to keep track of registered listeners. It is also used to deliver PropertyChangeEvent objects to those registered listeners when a bound property changes. A source bean can instantiate this class as a member field or inherit the functionality of this class by extending it. Figure 26.2 gives the framework for bound property support.
Figure 26.2: Bound Property support framework
Let us now discuss how to write a bound property. The following class acts as a source bean.
//Parent.java import java.beans.*;
public class Parent {
private String firstName = ”Uttam”, familyName = “Roy”;
private PropertyChangeSupport pcs; public Parent() {
pcs = new PropertyChangeSupport(this);
}
public String getFirstName() {
return firstName;
public void setFirstName(String fname) {
firstName = fname;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String newFamilyName) {
String oldFamilyName = familyName;
familyName = newFamilyName;
pcs.firePropertyChange(“familyName”, oldFamilyName, newFamilyName);
}
public void addPropertyChangeListener(PropertyChangeListener pcl) {
pcs.addPropertyChangeListener(pcl);
}
public void removePropertyChangeListener(PropertyChangeListener pcl) {
pcs.removePropertyChangeListener(pcl);
}
}
The following bean is a listener that wants to be notified when the familyName property of the Parent bean changes. So, it implements the PropertyChangeListener interface and defines the method propertyChange() . In the propertyChange() method, it changes its own familyName property to the new value of the familyName property of the Parent bean by using the getNewValue() method on the PropertyChangeEvent object.
//Child.java
import java.beans.*;
public class Child implements PropertyChangeListener {
private String firstName = “Rimisha”, familyName = ”Roy”;
public String getFirstName() {
return firstName;
}
public void setFirstName(String fname) {
firstName = fname;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String fname) {
familyName = fname;
}
public void propertyChange(PropertyChangeEvent pce) {
if(pce.getPropertyName().equals(”familyName”)) setFamilyName((String)pce.getNewValue());
}
}
For demonstration purposes, we have created the following Java application program. Typically, a bean builder (such as BeanBox provided by Sun) may be used.
//BoundDemo.java public class BoundDemo {
public static void main(String args[]) {
Parent p = new Parent();
Child c = new Child();
p.addPropertyChangeListener(c);
System.out.println(”Before changing family name”);
System.out.println(”Parent: ” + p.getFirstName() + ” ” +
р. getFamilyName());
System.out.println(”Child: ” + c.getFirstName() + ” ” +
с. getFamilyName());
p.setFamilyName(nBiswas”);
System.out.println(”After changing family name of parent to ‘Biswas’”);
System.out.println(”Parent: ” + p.getFirstName() + ” ” +
р. getFamilyName());
System.out.println(”Child: ” + c.getFirstName() + ” ” +
с. getFamilyName());
}
}
This program creates a Parent bean and a Child bean. The Child bean is then registered with the Parent bean. So, whenever the familyName property of the Parent bean changes, the Child bean gets notified and it can change its own familyName property. Finally, we have changed the familyName property of the Parent bean using the setFamilyName() method. We have also displayed the details of parent and child before and after changing the property. The output is as shown in Figure 26.3.
3. Constrained Properties
A constrained property of a bean is a special type of property, which can be changed subject to prior permission taken from external object(s). External objects act as vetoers and exercise such authority in this case. For example, think about Broker and ShareHolder beans. The Broker can sell the shares at a rate provided ShareHolder allows it to do so.
JavaBean API provides a mechanism very similar to the one used for the bound property, which allows other objects to veto the change of the source bean’s property. The basic idea for implementing a constrained property is as follows:
- Store the old value of source bean’s property if it is to be vetoed.
- Ask listeners (vetoers) of the new proposed value.
- Vetoers are authoritative to process this new value. They disallow by throwing an exception. Note that no exception is thrown if a vetoer gives the permission.
- If no listener vetoes the change, i.e., no exception is thrown, the property is set to the new value; optionally notify “PropertyChange” listeners, if any.
Three objects are involved in this process.
- The source bean containing one or more constrained property
- A listener object that accepts or rejects changes proposed by the source bean
- A PropertyChangeEvent object encapsulating the property change information
3.1. Implementing Constrained Property Support
In the source bean, the accessor method for the constrained property is defined in the same way, except that it throws a PropertyVetoException as follows:
public void setPropertyName(PropertyType pt) throws PropertyVetoException {
//method body
}
A source bean containing one or more constrained properties implements the following functionalities:
- Provides methods to add and remove VetoableChangeListener objects to register and unregister them so that they receive notification for property change proposal.
- When a property change is proposed, the source bean sends the PropertyChangeEvent object containing the proposed information to interested listeners. This should be done before the actual property change takes place. It allows listeners to accept or refuse a proposal.
- If any one listener vetoes by throwing an exception, continue to notify other listeners (if any) with the old value of the property.
JavaBean API provides a utility class, VetoableChangeSupport, similar to PropertyChangeSupport. The VetoableChangeSupport class provides the methods addVetoableChangeListener() and removeVetoableChangeListener() to add and remove VetoableChangeListener objects, respectively, and keeps track of such listeners. It also provides a method, fireVetoableChangeO , to send the PropertyChangeEvent object to each registered listener when a property change is proposed.
The source bean can instantiate this class as a member field or inherit the functionality of this class by extending it. A VetoableChangeSupport object is instantiated as follows:
VetoableChangeSupport vcs = new VetoableChangeSupport(this);
3.2. Implementing constrained property listener
If an object’s class wants to act as a vetoer, it must implement the VetoableChangeListener interface. This interface defines a single method as follows:
void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException;
A VetoableChangeListener must implement this method. This is the method where the vetoer exercises its power and agrees or disagrees with the proposal. In this method, the listener processes proposed property change and disagrees (vetoes) by throwing a PropertyVetoException exception. A typical code looks like this:
void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
if(the_condition_is_not_fulfilled)
throw new PropertyVetoException(“NO”, pce);
}
Figure 26.4 shows the constrained property support framework.
3.3. Example
Let us now demonstrate the constrained property with our Broker and ShareHoider beans. The Broker is the source bean that has a single constrained property, price. A Broker can sell a share owned by a ShareHoider, provided that the ShareHoider object allows it. So, Broker should take permission before it sells the share. If the permission is given, it actually sells the shares. Otherwise, it does not do anything. The following is the source code of the Broker bean.
//Broker.java import java.beans.[1];
public class Broker {
private float price = 10;
private VetoableChangeSupport vcs;
public Broker() {
vcs = new VetoableChangeSupport(this);
}
public float getPrice() {
return price;
}
public void setPrice(float newPrice) throws PropertyVetoException {
float oldPrice = price;
vcs.fireVetoableChange(“price”, oldPrice, newPrice);
System.out.println(”Setting new price limit”);
price = newPrice;
}
public void addVetoableChangeListener(VetoableChangeListener pcl) {
vcs.addVetoableChangeListener(pcl);
}
public void removeVetoableChangeListener(VetoableChangeListener pcl) {
vcs.removeVetoableChangeListener(pcl);
}
}
The ShareHoider is the listener bean. It decides whether the Broker can sell shares at the specified price. It allows the Broker selling shares if the proposed price is greater than or equal to H15. Otherwise, it disallows, by throwing the PropertyVetoException object. Here is the source code of ShareHoider.
//ShareHolder.java import java.beans.*;
public class ShareHoider implements VetoableChangeListener {
public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException {
if(pce.getPropertyName().equals(”price”)) {
float price = ((Float)pce.getNewValue()).floatValue();
if(!price >= 15 ) throw new PropertyVetoException(”NO”, pce);
}
}
}
For demonstration purposes, we have created a Java application that creates a Broker and a ShareHoider bean. It then registers the ShareHoider with the Broker so that it receives notification for the property change proposal. Finally, it tries to change the price limit of the share passed as an argument.
public class ConstrainedDemo {
public static void main(String args[]) {
Broker b = new Broker();
ShareHolder s = new ShareHolder();
b.addVetoableChangeListener(s);
float price = Float.parseFloat(args[0]);
try {
System.out.println(”Old price limit: ” + b.getPrice());
b.setPrice(price);
System.out.println(”New price limit: ” + b.getPrice()); }catch(Exception e) {
System.out.println(”Ah! failed to set price at ” + price);
}
}
}
4. Indexed Properties
An index property is an array of properties. It can hold a range of values that can be accessed through accessor functions. To access an individual element, the following methods are specified:
public PropertyElementType getPropertyName(int index)
public void setPropertyName(int index, PropertyElementType element)
The get method takes an array index and returns the element at that index. The set method takes two arguments: an index of the property array and its value, and sets the element at the specified index to the specified value. Methods to access the entire array are also specified as follows:
public PropertyElementType[] getPropertyName()
public void setPropertyName(PropertyElementType element[])
In this case, the get method returns the entire property array and the set method takes an entire array to be used to set the property array.
The following bean class stores the temperatures of the last seven days.
//TemperatureBean.java
public class TemperatureBean {
private float[] temperatures = new float[7];
/**
* Get the value of temperatures
*
* @return the value of temperatures
*/
public float[] getTemperatures() {
return temperatures;
}
/**
* Set the value of temperatures
*
* @param temperatures new value of temperatures
*/
public void setTemperatures(float[] temperatures) {
this.temperatures = temperatures;
}
/**
* Get the value of temperatures at specified index
*
* @param index
* @return the value of temperatures at specified index
*/
public float getTemperatures(int index) {
return this.temperatures[index];
}
/**
* Set the value of temperatures at specified index.
*
* @param index
* @param newTemperatures new value of temperatures at specified index
*/
public void setTemperatures(int index, float newTemperatures) {
this.temperatures[index] = newTemperatures;
}
}
Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.