Java Beans

 

Notes taken from: The Awesome Power of Java Beans by Lawrence H. Rodrigues (http://www.manning.com/Rodrigues) and from the Java Tutorials.

 

 

 

 

In general Java beans are:

 

General run-time Java Bean construction

 

  1. A constructor with 0 arguments.
  2. The bean classes must be serializable, ie. Implement the Serializable interface
  3. The properties, methods and events in the bean class must conform to design patterns. Properties are updated by set… methods and obtained by get… methods.


The set/get methods for a simple property should be:

 

            public void set<PropertyName>(propertytype  value)

            public propertyType  get<PropertyName>

 

Set/Get methods for Indexed properties should be:

           

            public void set<PropertyName>(int index, propertype value)

            public propertyType get<PropertyName>(int index)

 

Indexed property set and get methods may throw an ArrayIndexOutOfBoundsException.

 

Set/Get methods for array properties should be:

 

            public void set<PropertyName>(propertyType value[ ])

            public propertyType[ ] void get<ProperyType>()

 

The set methods for boolean properties are the same as the simple propertied by instead of a get… method name it is a ‘is…’ method name:

           

            public   Boolean is<PropertyName>()

 

The propertyType references above can be a Java primitive – such as int, float, or an Java class – such as Vector, String, JFrame, etc.

 

 

  1. Contain methods for registering target objects that want to be notified when bean events occur, and deregistering when target objects no longer want to be notified when events occur.

 

Usually more than one target object can be registered for receiving bean events. However, it is possible that the bean should only notify one target for the event.

 

For multiple targets a vector is used to hold the targets, and methods add or remove the targets

 

            Vector listeners = new Vector();

           

            public void addActionListener(ActionListener target){

                        listeners.addElement(target);

            }

 

            public void removeActionListener(ActionListener target){

                        if (!listeners.isEmpty()) listeners.removeElement(target);

            }

 

      For beans allowing only one target the vector is not needed and the addEvent method must throw an exception if more than one target tries to register.

 

                  public void addActionListener(ActionListener target) throws java.util.TooManyListernersException{

                             

                  }

 

 

  1. Information is passed from the bean (source) to a destination (target) by creating events.

 

public class MyEvent extends java.util.EventObject (or subclass of EventObject){

            public MyEvent(Object sourceObject, int event id, ….) {

                        ….

            }

            // all set/get methods

            // any other utility methods

}

           

  1. One or more event listeners are created to allow passing the event to the target. Each event listeners can have one or more methods but are coded ‘empty’ .(code provided by the target).

 

public interface MyEventListener extends java.util.EventListener{

            public void   processMyEvent(MyEvent myEvent);

            public void   doSomethingElseWithMyEvent(MyEvent myEvent);

 

 

  1. Methods in the bean are called as needed to create an Event object and pass it on to the target listeners. It is best to create a copy of the target listeners prior to starting the notification process.

 

public void fireEvent(){

            if (listeners.empty()) return;

            Vector copyOfListeners;

            MyEvent myEvent = new MyEvent(….. )    // pass in all needed info and/or use additional set or other methods to add info to event

            synchronized(this) {

                        copyOfListeners = (Vector) listeners.clone();

            }

            for (Enumeration e = copyofListeners.elements(), e.hasMoreElements();){

                        ((MyEventListener)(e.nextElement()).processMyEvent( myEvent);

            }

}

                       

             

  1. The bean classes,serialized objects and resources must be in a jar file

Psuedo Code Java Bean

 

Putting it all together looks something like the following:

 

public class SomeEvent extends java.util.EventObject (or subclass of EventObject){

            public SomeEvent(Object sourceObject,  ….) {  // info to pass to event object

                        ….

            }

            // add additional set/get methods

            // add any other utility methods

}

 

public interface SomeEventListener extends java.util.EventListener{    

public void   processSomeEvent(MyEvent myEvent);

public void   doSomethingElseWithSomeEvent(MyEvent myEvent);

 

 

 

public class SourceBean implements Serializable     // Serializable not needed if superclass implements it an your code doesn’t do anything

                                                                            // to make in non-serializable

            public SourceBean(){                  // empty constructor

                       

            }                                  

 

                        public void addEventListeners (eventListener e){      // the eventListener is the listener interface

                                    // add to vector

                        }

                        public void removeEventListener(eventListener e){

                                    // remove listener from vector

                        }

                        public void fireEvent(…){

                                    // if no listeners registered return

                                    // create the event with all info to be passed

                                    // create copy of listener vector

                                    // for each registered listener, perform eventListener method and pass event

                        }

 

Target (User of) Java Bean

 

1.       The target can implement logic in several ways to receive events generated by the JavaBean. For example:

 

a. The target can implement the listener

 

public class Target implements MyEventListener{

           

}

 

 

b. The target can implement an adapter (a class that just implements the listener interface

 

public class Target {

            SourceBean sourceBean = new SourceBean();        // create the source java bean

MyEventAdapter myEventAdapter = new MyEventAdapter( this);  // create the adapter

sourceBean.addMyEventListener(myEventAdapter);   // register listener with source bean

Public void executeCommand(…){                       // some logic to be executed when source bean fires event

           

}

 

           

 

public MyEventAdapter implements MyEventListener{     // class used to catch event

            TargetBean  targetBean;

            public MyEventAdapter(TargetBean targetBean){   // save object that is to process event

                        this.targetBean = targetBean;

            }

            public void performMyEvent(MyEvent myEvent){   //   called by source and event object passed from source to adapter

                        targetBean.executeCommand(…);           // call logic in target to process event      

            }

}

 

c. The target can implement an anonymous inner class to implement interface and process events

 


public class Target {

            SourceBean sourceBean = new SourceBean();        // create the source java bean

sourceBean.addMyEventListener  (

                        new MyEventListener(){                // create the listener

                                    public void processEvent(MyEvent me){

                                                executeMyCommand(…);

                                                ….

                                    }

                        }

}

 

2.       If the JavaBean is serialized, the java ‘instantiate’ command is used instead of ‘new’.

 

A class loader is needed to load the serialized object. The instantiate command can throw a ClassNotFoundException or IOException. The general code is:

 

                                    try {

ClassLoader cl = (MyJavaBean.class).getClassLoader();  

                                                myJavaBean = (MyJavaBean).instantiate (cl, “MyJavaBean”);

                                                myJavaBean.callMethodsAsNeeded(…..);           

}

catch IOException(IOException ioe){  ….}

catch ClassNotFoundException(ClassNotFoundException cnfe){….}

 

If a serialized version of the bean class is not available the bean class is instantiated the same as ‘new’.

 

 

 

Serialization of a Java Bean

 

A Java Bean can be customized during application construction then saved with the customized values. The bean could also be used during application execution and then saved with updated values for later use. A bean needs to be serializable (to be saved externally and recallable with its values intact) as needed.

 

1.       The class must implement Serializable (not needed if superclass it is derived from implements Serializable) or Externalizable

 

2.       Properties of beans that should not be saved when a bean is serialized should be defined with ‘transient’

transient int tempValue;

transient  Object tempObject;

int saveValue;

object saveObject;

 

3.       If implemented as Serializable, and if needed the class implements writeObject(ObjectOutputStream out)  and readObject(ObjectInputStream in) methods to execute special serialization/deserialization logic. If no special logic needed then these methods aren’t needed either.

 

private void writeObject(ObjectOutputStream out) throws IOException{

            saveValue = something;

            out.defaultWriteObject();   // writes out all non-transient data

}

 

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{

            in.defaultReadObject();   // read in stored object

            if (saveValue == something) {

                                 // do some reinitialization logic as needed

            }

 

           

4.       If implemented as Externalizable the writeExternal(ObjectOutput oo) and readExternal(ObjectInput in ) must be written

 

5.       Registered listeners must be checked when a bean is serialized to determine if they are serializable, and when a bean is deserialized any listeners must be reregistered.

 

pivate void writeObject(ObjectOutputStream out) throws IOException{

            for (Enumeration e = listeners.elements(); e.hasMoreElements();){

                        SomeListener someListener = (SomeListener).e.nextElement();

                        if (someListener instanceof Serializable){

                                    serListeners.addElement(someListener);   //serListeners vector previously defined

                        }

            }

            out.defaultWriteObject;

}

 

 

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{

            in.defaultReadObject();   // read in stored object

            for (Enumeration e =serListeners.elements(); e.hasMoreElements();){

                        SomeListener someListener = (SomeListener).e.nextElement();

                        addSomeListener(someListener);  // reregister listener

            }

}

 

 

6.       When a bean is instantiated from a serialized prototype or class file and the object is a GUI (visible) object, the addNotify() method is invoked. Provide an override method is special logic is needed.

 

public void addNotify(){

            super.addNotify(){

            // some special logic if needed

            if (Beans.isDesignTime()){

                       

            }

 }

 

7.       To ensure that a serialized bean is compatible with the current class, versioning is used. A utility program is used to create a version number (long) can be assigned to the bean during bean coding to unique identify that version of the code.

 

a.       Type the command “serialver –show”  to start the utility

b.       Type in the class name of the bean and click show

c.       Copy (cut and paste) the generated version number into the bean java code.

 

Implementing the PropertyChange Event

 

1.       A property can be ‘bound’ such that when the property changes in source bean all registered listeners in target beans may be notified of the change.

 

2.       The property change is communicated by PropertyChangeEvent object. It’s constructor an methods are:

 

public PropertyChangeEvent(Object source, String propertyName, Object oldValue, Object newValue)

pce.getPropertyName()

pce.getOldValue();

pce.getNewValue();

 

Note that primitive Java property types (eg int, float, etc.) must be converted to corresponding wrapper classes (Integer(), Float(), etc.).

 

3.       A PropertyChangeSupport object is used to record listeners, with the constructor typically being passed the creating object.

 

PropertyChangeSupport pcsNotifier = new PropertyChangeSupport(this);

 

4.       The source bean registers/deregisters listeners

 

public void addPropertyChangeListener(PropertyChangeListener pce){

            pcsNotifier.addPropertyChangeListener(pce);

}

 

public void removePropertyChangeListener(PropertyChangeListener pce){

             pcsNotifier.removePropertyChangeListener(pce);

}

 

5.       The notifications are generally fired in the setter method

 

public void set<propertyName>(someType newValue){

            someType oldValue = property;

            pcsNotifier.firePropertyChange(propertyName, oldValue, newValue);  // values in wrapper if primitive Java type

            property = newValue;

            // any other needed logic

}          

 

 

6.       The target object will implement the PropertyChangeListener interface with just one method to override.

 

public propertyChange(PropertyChangeEvent pce){

            if (pce.getPropertyName().equals(“xxx”) {

                        xxxValue = pce.getOldValue();

            }

}

 

 

Implementing the VetoablePropertyChange Event

 

1.       A change to a constrained property in a source bean can be rejected by a target bean. The use of the VetoablePropertyChange is very similar to a PropertyChange.

 

2.       The source bean can implement a VetoableChangeSupport object passing itself in the constructor and make available the following methods.

 

VetoableChangeSupport vcsNotifier = new VetoableChangeSupport(this);

Public void addVetoableChangeListener(VetoableChangeListener vcl){

            vcsNotifier.addVetoableChangeListener(vcl);

}

public void removeVeoableChangeListener(VetoableChangeListener vcl){

            vcsNotifier.removeVetoableChangeListener(vcl);

}

 

Specific properties can also be constrained using methods:

 

               void addVetoableChangeListener(String propertyName, VetoableChangeListener listener);
                               void removeVetoableChangeListener(String propertyName,VetoableChangeListener listener);
 

As an alternative, for each constrained property a Bean can provide methods with the following signature to register and unregister vetoable change listeners on a per property basis:

                                              void add<PropertyName>Listener(VetoableChangeListener p);

                                              void remove<PropertyName>Listener(VetoableChangeListener p);

 

 

3.       In the constrained properties setter method call the fireVetoableChange() method.

 

public void set<propertyName>(someType newValue){

            try{

                        someType oldValue = property;

                        vcs.fireVetoableChange(propertyName, oldValue, newValue);  // values in wrapper if primitive Java type

            catch (PropertyVetoException pve){

                        // display and/or log error, or any other required logic

            }

            property = newValue;

            // any other needed logic

}          

 

Or have the setter method throw the PropertyVetoException so ‘higher’ logic can catch and process it.

 

4.       Both VetoableChangeSupport and PropertyChangeSupport can be implemented if needed to both constrain and bind the property. Add the firePropertyChange() method after the  fireVetoableChange() method.

 

5.       The target bean will implement the VetoableChangeListener interface with it’s one method vetoableChange()

 

public void vetoSomePropertyChange(PropertyChangeEvent pce) throws PropertyVetoException{

            Propertytype newValue = (PropertyType) e. getNewValue();

            // some logic to see if new value acceptable or should be rejected

            if (contraintsFailed)

                        throw new PropertyVetoException(“Error msg”, e);  // rolls back change

}

 

Each contrained property must have a method as written above.

 

 

Building BeanInfo classes

 

1.       BeanInfo classes can be built from scratch or some tools build automatically build the skeleton for you.

 

2.       Naming convention must be <ClassName>BeanInfo.

 

3.       To build a BeanInfo class, extend SimpleBeanInfo (which implements BeanInfo interface) and override appropriate methods.

 

4.       The beans API provides classes to describe the properties, methods and events the bean exposes. They are

 

a.       PropertyDescriptor

i. IndexPropertyDescriptor (subclass of PropertyDescriptor)

b.       MethodDescriptor

c.       EventsetDescriptor

d.       BeanDescriptor

 

5.       FeatureDescriptor class is the base class for feature descriptor classes that have methods to set and get information about a feature.

 

6.       The BeanInfo interface specifies several methods to fetch feature descriptor objects and the bean icon.

 

a. getBeanDescriptor() – returns a BeanDescriptor (information about the bean in general)

b. getMethodDescriptors() returns MethodDescriptor[] (array of info about the methods)

c. getPropertyDescriptors() returns PropertyDescriptor[] (array of info about the bean properties)

d. getEventSetDescriptors() returns EventSetDescriptor[] (array of info about the bean events)

e. getIcon() – (the SimpleBeanInfo class provides loadImage(String resourceName) so use loadImage in getIcon())

 

7.       Within each method above you code the construction of the feature descriptor objects

 

8.       If the run-time class inherits features from it’s superclass, you can use additionalBeanInfo() method to fetch BeanInfo object of another class. (It is not done automatically)

 

9.       Steps in building a BeanInfo class

 

a.       Create a BeanInfo source file using <ClassName>BeanInfo extending SimpleBeanInfo or any other BeanInfo class that implements BeanInfo interface or subclass of SimpleBeanInfo

b.       Implement getBeanDescriptor() and provide code to create a BeanDescriptor

c.       Implement (optional) getPropertyDescriptors() and provide code to create array of PropertyDescriptors and IndexedPropertyDescriptors

d.       Implement (optional) getMethodDescriptors() and provide code to create array of MethodDescriptors

e.       Implement (optional) getEventSetDescriptors() and provide code to create array of EventSetDescriptors

 

Overview of Descriptor classes

 

1.       The base class of descriptors in FeatureDescriptor. Subclasses are:

 

a.                    ParameterDescriptor

b.                   MethodDecriptor (contains ParameterDescriptor)

c.                    EventSetDescriptor (contains MethodDescriptor)

d.                   BeanDescriptor

e.                    PropertyDescriptor

                                                                                                   i.      IndexPropertyDescriptor

 

2.       Most descriptor classes have setter (usually used by bean creator), getter and Boolean (used by builder tools) methods.

 

3.       The main purpose of the BeanDescriptor class is to provide the builder tool with bean meta class.

 

a. Constructor are

      BeanDescriptor(Class beanClass)

      BeanDescriptor(Class beanClass, Class customizer)

b. Example

      public BeanDescriptor getBeanDescriptor(){

                  BeanDescriptor beanDescriptor = new BeanDescriptor(movieClass, ScreenCustomizer.class);

                  beanDescriptor.setDisplayName(“Movie setup”);

                  registerEditor();   // register the custom property editors

                  return beanDescriptor;

      }

 

4.       The MethodDescriptor class provides info about a method

 

a.       Constructors are

MethodDescriptor(Method methodObj) // typically for methods w/o arguments

MethodDescriptor(Method methodOjb, ParameterDescriptors parameterDescriptors[])   // for methods w/ arguments

b.       Construction a MethodDescriptor

 

// create  array with parameter data types

Class parameterClasses[] = {list of parameter types used in method, eg. Float.class, JFame, etc.};

// create the method object with the parameter array

Method setScreenMethod = (Movie.class).getMethod(setScreen”, parameterClasses);  // uses introspection

// Provide info about each parameter

ParameterDescriptor pdScreenWidth = new ParameterDescriptor();   // parm 1

pdScreenWidth.setDisplayName(“Width of Screen”);

pdScreenWidth.shortDescription(“Width of the Screen… Max/Min are….  Default is….”);

ParameterDescriptor pdScreenHeight = …..  // parm 2…

// create array of parameter descriptors just defined

ParameterDescriptor[]  pdScreen = {pdScreenWidth, pdScreenHeight};

// now create method descriptor using Method and parameter descriptor array

                                    MethodDescriptor mdScreen = new MethodDescriptor(setScreenMethod, pdScreen);

 

5.       The EventDescriptor class provides info about the events fired by a bean. There are several different constructors depending on the number of events to be defined

 

a.       Constructor for a single event listener method uses design patterns to construct an object

EventSetDescriptor(Class beanClass, String eventSetName, Class listenerType, String listenerMethod) // single event Method

 

                  try {

                              EventSetDescriptor edStart = EventSetDescriptor ( movieSelector.class, “select”, SelectListener, “selectPerformed”);

return new EventSetDescriptor[] {edStart};

                  catch(IntrospectionException ie) {….}                             

 

b.       Constructor for a multiple event listener methods

EventSetDescriptor(Class beanClass, String eventSetName, Class listenerType, String listenerMethodNames[], String addListenerMethodName, String removeListenerMethodName)

 

                  try {

                              String methodNameList[] = {“movieSelected”,”movieDeselected”};

EventSetDescriptor edStart = EventSetDescriptor ( movieSelector.class, “movie”, movieListenerClass,methodNameList,   addMovieListener”, “removeMovieListener”);

return new EventSetDescriptor[] {edStart};

                  catch(IntrospectionException ie) {….}                             

 

 

c.       Constructor using the reflection API

EventSetDescriptor(String eventSetName, Class listenerType, String listenerMethodNames[], Method addListenerMethod, Method removeListenerMethod)

 

                  try {

                              Class args[] = {MovieEvent.class};

                              Method movieStart = MovieListener.class.getMethod(startMovie”, args);

                              Method movieStop = MovieListener.class.getMethod(stopMovie”,args);

                              Method methods[] = {movieStart, movieStop};

 

Class args[] = {MovieListener.class};

                              Method addMovieListener = Movie.class.getMethod(addMovieListener”, args);

                              Method removeMovieListener = Movie.class.getMethod(removeMovieListener”,args);

 

EventSetDescriptor ed = EventSetDescriptor ( “movie”, movieListener.class,methodNameList,  methods, addMovieListener,  removeMovieListener);

return new EventSetDescriptor[] {ed};

 

                  catch(NoSuchMethodException nsme) {….}

catch(IntrospectionException ie) {….}     

 

 

d.       Constructor using the reflection and beans API allows the inclusion of information about listener method arguments.

EventSetDescriptor(String eventSetName, Class listenerType, MethodDescriptor listenerMethodDescriptors[], Method addListenerMethod, Method removeListenerMethod)

 

try {

            Class args[] = {MovieEvent.class};   // the event class

            ParameterDescriptor pd1 = new ParameterDescriptor();  // the parameters within the event class

            pd1.setShortDescription(“…..”);

            ParameterDescriptor[] startDescriptor = {pd1};     

 

            ParameterDescriptor pd2 = new ParameterDescriptor();

            pd1.setShortDescription(“…..”);

            ParameterDescriptor[] stopDescriptor = {pd2};

 

            Method startMethod = MovieListener.class.getMethod(“start”, args);   // the event listener method

            MethodDescriptor startMd = new MethodDescriptor(startMethod, startDescriptor);

 

            Method stopMethod = MovieListener.class.getMethod(“stop”, args);   // the event listener method

MethodDescriptor stopMd = new MethodDescriptor(stopMethod, stopDescriptor);

 

MethodDescriptor movieMd[] = {startMd, stopdMd};  // create arry of event listener method descriptors

 

Args = new Class[] {MovieListener.class}; 

Method addMethod = Movie.class.getMethod(addMovieListener, args); // create methods for add/remove

Method removeMethod = .class.getMethod(removeMovieListener, args);   

 

EventSetDescriptor esd = new EventSetDescriptor(“movie”, MovieListener.class, movieMd,addMethod, removeMethod);

 

return new EventSetDescriptor[] {esd};

                                    }

catch(NoSuchMethodException nsme) {….}

catch(IntrospectionException ie) {….}     

 

6.       The PropertyDescriptor describes the properties of a bean. There are 3 constructors:

 

PropertyDescriptor(String propertyName, Class beanClass) throws IntrospectionException;

PropertyDescriptor(String propertyName, Class beanClass, String getterName, String setterName) throws IntrospectionException;

PropertyDescriptor(String propertyName, String getterName, String setterName) throws IntrospectionException;

 

The first constructor assumes design patterns are being followed, the second and three constructors explicitly name the getter/setters for the property.

 

 

7.       The IndexedPropertyDescriptor (a subclass of PropertyDescriptor) describes the indexed properties of a bean. There are 3 constructors:

 

IndexedPropertyDescriptor(String propertyName, Class beanClass) throws IntrospectionException;

IndexedPropertyDescriptor(String propertyName, Class beanClass, String getterName, String setterName, String indexedGetterName, String indexedSetterName) throws IntrospectionException;

IndexedPropertyDescriptor(String propertyName, Class beanClass, Method getter, Method setter, Method indexedGetter, Method indexedSetter) throws IntrospectionException;

 

 

 

Class Hierarchy and BeanInfo Classes

 

1.       Beans created as subclass of other beans (which have their own BeanInfo classes) can have BeanInfo classes created in several ways.

     

a.       Use getAdditionalBeanInfo(). Useful if no overlap/conflict with properties/methods/events between super and subclass.

 

public BeanInfo[] getAdditionalBeanInfo(){  // append all the feature descriptors from other classes to the current one

            BeanInfo  superclassInfo = new SuperClassBeanInfo();

            BeanInfo[] infoArray = {superclassInfo};

            return infoArray;

}

 

If the ‘parent’ bean does not belong to the classes in the bean’s class hierarchy, the TargetInvocationException is raised at run time (construction)

 

More than one BeanInfo object can be returned in the array. The lower indexed items have precedence over higher indexed items.

 

Methods, properties, and/or events can be overridden or hidden in the corresponding descriptor methods.

 

b.       Create a BeanInfo subclass of the superclass. Use the get…Descriptors to obtain the superclass descriptors and add/modify as needed.

 

 

Updated BeanInfo Construction Checklist

 

1.       Create a <ClassName>BeanInfo class, extending from either SimpleBeanInfo from another BeanInfo (implementing either BeanInfo interface or subclass of SimpleBeanInfo)

 

2.       Override get<Feature>Descriptor methods as needed.

 

      Common updates

                  Display names

                  Hide property

                  Add short description

                  Set as expert property

      Update feature specific information

                  Override getBeanDescriptor() if need to register

                              A customizer

                              Class/interface specific property editors

                  Override getPropertyDescriptor() if need to

                              Expose specific properties

                              Set property as bound and/or constrained

                              Register the property specific editor

                              If getter and setter methods do not follow design patterns

                  Override getMethodDescriptors() to expose specific methods

                  Override getEventSetDescriptors()

                              To expose specific events

                              If listener and registration methods do not follow the design pattern

                  Override getDefaultEventSet() if there is a default event set

                  Override getDefaultPropertySet() if there is a default property

                  Override getIcon() if there is an icon (loadimage()) to identify the bean

 

 

 

Bean Instantiation

 

 

Try{

            ClassLoader cl = (MyClass.class).getClassLoader();

            myClass = (MyClass)Beans.instantiate(cl, “package.MyClass”);

catch(ClassNotFoundException cnfe){…}

catch(IOException ioe){…}

 

 

 

If (Beans.isInstanceOf(myClass, package.MyClass){…}

 

 

MyClass myClass2 = null;

myClass2 = (package.MyClass)Beans.getInstanceOf( beanobject, package.MyClass)

 

 

If (Beans.isDesignTime()){…}

 

 

If (Beans.isDesignTime()){…}