Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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: Somewhat self-contained pieces of code that may contain predefined values. Communicate with the outside objects by creating an event (containg relevant information and/or objects) and passing this event on to registered listeners Provide methods for registering outside objects that want to be notified when bean events occur, or removing the notification when outside objects no longer want to be alerted when events occur. 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. 4. 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{ … } 5. 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 } 6. 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); 7. 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); } } 8. 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. b. c. d. e. ParameterDescriptor MethodDecriptor (contains ParameterDescriptor) EventSetDescriptor (contains MethodDescriptor) BeanDescriptor 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 1. Beans should always be instantiated using the instantiate() method rather than new. Instantiate() looks for serialized prototype (.ser) to load first and if not found will create the bean from the class file. Try{ ClassLoader cl = (MyClass.class).getClassLoader(); myClass = (MyClass)Beans.instantiate(cl, “package.MyClass”); catch(ClassNotFoundException cnfe){…} catch(IOException ioe){…} 2. To check the type of a serialized prototype, use the method isInstanceOf(). If (Beans.isInstanceOf(myClass, package.MyClass){…} 3. getInstanceOf() will instantiate a bean MyClass myClass2 = null; myClass2 = (package.MyClass)Beans.getInstanceOf( beanobject, package.MyClass) 4. Design time vs run time can be checked using isDesignTime() If (Beans.isDesignTime()){…} 5. Determining if the bean is in a GUI environment change be checked by guiAvailable() or can be set using setGuiAvailable(). Useful for both design and run time. If (Beans.isDesignTime()){…}