Download Java Beans - FIS Incorporated

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
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()){…}