references:
'Java 1.2 Developer's
Handbook',
Heller & Roberts, 'Java 1.1 The Complete Reference'
Naughton & Schildt, JDK
1.2.2 API documentation, The Jar Guide', & 'The Manifest File
Format',
The Sun web site 'Introduction
the the JavaBeans API, John Zukowski
What is a Java Bean?
A JavaBean is a reusable component. JavaBeans
are all built to a specification that
allows them to work together. A simple example
of a reusable, interconnectable
component is a lego block. We take for granted
they can be connected together to
build large and complex structures. What allows
them to be interconnected is the fact
that they are built to an exact specification.
There are a specific number of protrusions
and depressions that are positioned in exactly
the right places so they can interlock.
The legos also have exact overall dimensions
that allow them to fit together. Making
software interconnect like lego blocks is a
considerably
more complicated business
especially considering that javabeans, beyond
fitting together, can provide extra
functionality to a program and are able to
communicate
with each other. Still the
fundamental idea remains the same in that a
blueprint
allows different objects to
interconnect by virtue of them adhering to a
a uniform standard.
The javabean architecture also allows the
beans
to be pieced together using a java bean
compliant visual development environment The
bean design provides the development
environment the means to find out what properties
the bean has and what the bean can
do. As might be anticipated, developing javabeans
is more complicated than developing
regular java programs because of the added need
to conform to the java bean blueprint.
Fortunately, the extra restraints on the code
are not that hard to learn and implement.
The advantage gained is that your code
will automatically be interconnectable with other
javabeans.
An example of java beans are java's visual
components,
the AWT widgets and the
newer swing components. They are all javabeans
and conform to the javabean architecture.
This may lead you to think that java beans are
all about visual components and GUIs.
While the the design has been very successfully
applied to producing visual components,
the architecture can be equally well applied
to non-visual functions. The only thing hitch is
that to run a java bean that provides
non-visually
oriented functionality in a visual development
environment, you will need to supply a visual
component to act as a host for your java bean
functionality.
Patterns and Reusability
The desire to make code reusable is not really
a new idea. Computer programmers
for a long time recognized they were often
reinventing
the wheel with each new project
they undertook. In 1968 M.D.McIlroy's published
a request that a catalog of software
components be created that could be plugged
together
to build applications. For a long
time this was sort of a search for the holy grail
that had been pursued with more or less
success. Javabeans provides a decidedly
successful
answer to McIlroy's request. The
javabean architecture has provided a large
collection
of interoperable components that
can interconnected by hand-coded java or with
the aid of a java beans development
environment.
"Each pattern describes a
problem
which occurs over and over again in our environment,
and describes the core of the
solution
to that problem, in such a way that you can use this
solution a million times over,
without ever doing it the same way twice"
Christopher Alexander
The designers of the javabeans architecture
were
influenced by the study of patterns
that has been carried out in many academic fields
of study. The study of patterns began
with the release of Christopher Alexander's book
'The Timeless Way of Building'
printed by Oxford Press in 1978. Alexander was
an architect who observed different
problems would arise in buildings that the
builders
would solve in the same way over
and over again. He cataloged these patterns
in his book. Academics in other disciplines
were soon looking for patterns in their fields
of study.
Four computer scientists, Erich Gamma, Richard
Helm, Ralph Johnson and John Vlissides
wrote the highly influential book, 'Design
Patterns-Elements of Reusable Object Oriented
Software' which was published in 1995. The
researchers
document 23 well used software
patterns in this book.
The advantage to recognizing patterns is that
they provide a prescription for a tried and
tested solution to a problem that can be refined
and perfected and doesn't need to be
rediscovered every time a problem arises. The
javabean architecture benefits from the
use of classic patterns in it's design, and also
supply the java programmer with a design
pattern that can be followed in order to create
reusable and interoperable java
components.
Practical Aspects of Using JavaBeans
The Bean Box
JavaBeans are designed to be used in combinations to create applets
and applications.
The architecture allows builder tools to be used to execute this task.
The builder tools
allows beans to be put together visually without the need for the
builder
to do provide
and code. To assist the developer, a reference builder is supplied
called the BeanBox.
The BeanBox is to javabeans what appletviewer is to applets. It
provides
a quick way
for a developer to find out how his or her bean will behave in a visual
environment.
Commercial IDE such as IBM's Visual Age for Java will supply a
sophisticated
version
of the bean box to put together javabeans.
Get & Run the BDK
The Bean Development Kit is a 5 megabyte
download.
The self-extracting archive
can be downloaded from sun at http://java.sun.com/beans/software/bdk_download.html
To start the application you have to change to the \bdk\ beanbox
directory and
execute the batch file, run.bat. John Zukowski one of the early authors
of Java texts
has a useful article written at the sun site if you wish to visit it.
Introduction
to
the
JavaBeans
API
Bean Box Operations
The BeanBox produces three windows. The
windows
allow a user to modify a
bean's state using property sheets. The bean
box allows beans to message each
other using property changes and events. The
bean box also allows saving a bean
with all it's property states via the
serialization
process. The bean box expects the
java bean will be packaged in a jar file when
it loads it into it's work area. The
bean box environment provide these controls along
with other point and click
operations to create programs built from java
beans.
What the BeanBox does alludes to what a
JavaBeans
is. Beans are stateful objects
that can be persisted in memory. Beans are
normally
packaged in jar files. JavaBeans
can communicate with each other through events
and property changes. Beans are
inconnectable components that can be assembled
in a builder's tool.
Jar Files
Java tools such as the BeanBox expect beans to
be packaged in JAR files. The
jar utility is another variant on the zip file
invented by Phil Katz who donated his
compression scheme for public use. Jar files
like their Zip equivalents, provide
an efficient means for packaging and accessing
a set of java classes. Jar files
serve to reduce download time. They also allow
digital signatures to be associated
with the jar allowing for authentication and
security. When a jar file is used to house
a java bean it's manifest file must be used to
indicate which components are Java
beans.
Example
Name:
frame1.gif
// a manifest may have
several
.class files
Name:
Frames.class
// when they are beans they must be followed
Java-Bean:
True
// by the tag line
'Java-Bean'
True
JAR Utility
To create a jar file type jar at the
command
line followed by options and names, first,
what you wish to call the jar file, then the
name of the manifest file, followed by the
names of the files you wish to enter in the
archive.
(If you are not creating a bean than
you may not need the manifest and your command
becomes jar cf and the name
following will be the name of the jar followed
by the classes or pattern for the classes
you want entered into the archive.)
example jar cfm Globe.jar Places.mf *.gif *.class
// places all .class
and
.gif in current directory into a JAR file named Globe.jar
// with an
externally
prepared manifest file Places.mf
Here is a second example straight from the jar utility. // type jar by itself at the command line
example jar cvfm classes.jar myM -C foo/ .
Use an existing manifest file 'myM' and archive all the files in the foo/ directory into 'classes.jar':
Following is a list of jar options. Notice two of the capital
letters
describe negative
actions and the third C doesn't follow the rule.
jar options
c | a new archive is created |
f | makes the first file name listed that of the archive |
m | makes the next name that of the manifest file |
M | no manifest file is created |
t | tabulate archive contents |
x | extract archive files |
v | provide verbose output as utility executes |
C | change to the given directory and include the following file |
O | do not compress |
i | generate index info for the named manifest file |
u | update existing archive |
Creating a Bean
Assuming we have the code that defines a bean
prepared and we will get to that, there
are a number of mechanical steps that have to
be followed in creating a java bean. First
a directory for the bean needs to be created.
Once created switch into that directory.
The java source file is copied into the
directory.
The source code's packaging scheme
must match the hierarchy of the directory
structure.
A manifest file needs to be prepared
as a straight text file and saved with the .mf
or .mft extension. Inside the manifest any
classes that represent java beans must be tagged
with script indicating they are
javabeans.
example
Manifest-Version: 1.0
Name:
SimpleBean.class
Java-Bean:
true
A jar file is created using a statement such as the following.
example jar cfm jar_name manifest.mft *.class
Finally the BeanBox can be started and the
newly
created bean can be loaded into it to
see if it works as expected.
Summary of Steps involved in packaging a
javabean.
1.Create a directory for the bean ( and switch to it ). |
2.Create the java source file matching package hierarchy to that of the directory structure. |
3.Compile the source file* // see box below |
4.Write the
manifest file,
saving with the .mf /.mft extension to the directory, tagging
the bean as below example 1
Name:
new_directory / the_bean.class
example
2 Manifest-Version:
1.0
//
this is the contents of a tested manifest file
The manifest file
consists
of a list of files present within the archive itself. Not all
files
Manifest-Version:
1.0*
|
5. Generate a jar file using jar cfm jar_name manifest.mft *.class |
6. Start the BDK
by
changing to c:\bdk\beanbox
and type run
// The new bean should be in the toolbox window |
7. Test by loading excercising the beans parameters in the BeanBox |
Bean developers can automate some of these
processes
using Makefile utilities. For more
on this topic see JavaBeans
Makefiles
Makefile
utilities
// for reference
Makefile utilities have been developed to simplify the bean building process serving to eliminate the needs for steps 3, 4 and 5 above (compiling the bean, writing the manifest and creating the jar file) For Unix, run gnumake yourBean.gmk For Windows, run nmake -f yourBean.mk This will
compile SimpleBean,
and create a JAR file in the beans/jars directory.
|
Beans Support
Java Bean support comes in the following
packages.
The beans package provides
the general support for visual tools to build
beans. The beancontext package provides
a way to structure beans in hierarchies. The
reflect package is java's generic package
for accessing internal information about java
objects. A typical java bean will include
these packages as imports.
Reflection and Class Class
At runtime a Java Virtual Machine
knows
how to build an object based on information
it has obtained from class bytecode.This same
information regarding the field(s), method(s)
and constructor(s) of a class can be accessed
using the Reflection API in java.lang.reflect.
Though only the JVM can create an instance of
these classes from the bytecode info, you can
get a representative instance of them using the
class
Class.
RTTI or Run Time Type Information
for any object is stored in an object of class Class,
which is full of informational method types like
isPrimitiveArray(
), or
getConstuctor( ).
One the main functions of Class is to
provide auxiliary support for the loading of classes at
runtime. Programs written in other languages
will often load everything before running. (This
is static loading. Java uses dynamic loading.
(Classes
are loaded when they are first
referenced).
When source code is compiled to a .class
file, in addition to translating source code to
bytecode, a Class object, is written into
the bytecode describing the files contents. The
runtime checks any casts that have been made
using this object, before allowing the class
to be loaded into the program.
Three ways of getting a Class object:
1)
Class
c = lemon.getClass(
);
// off an instance
2)
Class q = Class.forName("className");
// via a static method call
3)
className.class
// a class literal
Introspection // a specialized form of reflection for getting information about a bean.
Introspection is a specialized use of the
reflection
process. Introspection is the process
of finding what properties, methods and events
a Bean support. This capability is a
specialized use of the reflection process.
The
Introspector
class
uses reflection (the
more general tool for decomposing any object)
and applies it to Bean instances. The
Introspector class provides a standard way for
vendor tools to acquire the bean info.
Additional information is derived from the way
beans conform to design patterns resulting
in the further exposure of bean information.
Introspector
provides access to the BeanInfo
class via it's getBeanInfo( ) method.
// the method takes an instance of class Class for a given component
From the BeanInfo class returned by
getBeanInfo(
), you can call getXXXDescriptor( )
methods, where XXX can be EventSet,
Property
or
Method providing info for all the
events the bean fires, all the properties and
all the methods the Bean supports.
public class Introspector extends
Object
//
from jdk1.3 documentation, for reference
The Introspector class provides a standard way for tools to learn about the properties, events, and methods supported by a target Java Bean. For each of those
three kinds
of information, the Introspector will separately analyze the
If a class
provides explicit
BeanInfo about itself then we add that to the BeanInfo information
If we don't find
explicit
BeanInfo on a class, we use low-level reflection to study the
|
BeanInfo
The information gathered in Introspection is
poured
into an instance of the BeanInfo class. This
interface has methods which can be used to
control
what information is returned to the user,
allowing more or less detail than is provided
by reflection alone. Normally BeanInfo is used to
provide the bean tool user with less
information
so the Bean is easier to work with. The
bean info class must be named after the bean
it is associated with. For example, a Bean named
Jumping need a bean info class called
JumpingBeanInfo.
SimpleBeanInfo is a convenience
class which allows you to override methods
selectively.
Persistence
The ability to save the state of a java bean
is
achieved through serialization.
Serialization
uses the reflection process to store
information
about the state of an object.When
you Save a bean to a .ser file
via the menu in BDK's BeanBox, persistence is achieved
by writing the java bean to file through ObjectOutputStream.
It
is retrieved through
ObjectInputStream. As a result,
the
bean must be tagged with the Serializable
interface. The Serializable interface is one
of Java interfaces that defines no methods.
The Serialiizable interface is just used to mark
an object as being serializable.
Customization
A builder tool uses reflection to present a
default property sheet for use by the bean
integrator. If the BeanBox's standard GUI for
property or service presentation is inadequate,
the interface Customizer can be
implemented
to improve or customize the presentation.
According to the jdk1.3 documentation, "A
customizer
class provides a complete custom
GUI for customizing a target Java Bean. Each
customizer should inherit from the java.awt.
Component class so it can be instantiated inside
an AWT dialog or panel. Each customizer
should have a null constructor."
// customizer's should inherit from components so it can come up in a dialog or panel
PropertyEditorSupport
To create an alternate presentation for a
single
property, PropertyEditorSupport class can
be extended. Both customization and property
editor support requires the Bean to provide a
support class which implements the BeanInfo
interface.
// PropertyEditorSupport -a
support
class to help build property editors.
Bean Properties
When bean writers adhere to the JavaBean's
naming
conventions, the Introspector can
recognize and abstract information about the
beans properties and events. The Introspector
also scans the class hierarchy and by default
fills the beans property sheet with inherited (and
perhaps unwanted properties). As mentioned
earlier,
this is where BeanInfo interface is used
to filter the information that will be presented
by the Bean.
// the BeanInfo interface limits the information entered into the bean's property sheet.
JavaBean properties are classified in four
categories.
Simple Properties
Simple properties are those that can be
described
by a get/set method pair as in the
following examples:
public void setPropertyName( property_type ) property_type getPropertyName( )
// set takes the simple property, get returns it
For boolean properties the following form is also allowed.
setPropertyName( boolean ) boolean isPropertyName( )
// where isXXX(
)
returns boolean
Indexed Properties
Indexed properties are plural versions of the
simple form methods where instead
of a simple value an array is taken or returned.
setPropertyName( property_type
[]
) property_type
[]
getPropertyName( )
// set takes an
array
, get returns an array
setPropertyName(property_type[], int ) property_type[] getPropertyName(int) *
// indexed array in,
indexed
property returned, *
here int is a specified index value
Bound Properties
A control bean can be defined to bind
it's
property changes to those of other beans.
Firing a change in the source automatically
updates
properties in other targets. An
intermediary class has been designed to take
care of the details of binding properties.
The class, PropertyChangeSupport
supports
a registration model where target bean
properties are added or removed from a
notification
list. The binding Bean uses the list to
call listeners through the method firePropertyChange(
).
The method is called from the
setXXX( ) method of the binding property.
Summary of property binding
1) Instantiate PropertyChangeSupport on
this
(usually),
the source bean instance
2) Override add /removePropertyChangeListener(
), wrapping in them calls to the
same method on the PropertyChangeSupport
instance.
3) getXXX( ) method returns the property
which will be bound in setXXX( )
4) in setXXX( ) method make the property
changes then fire them.
Example demonstrating the above summary
of property
binding
1) PropertyChangeSupport pc_support = new PropertyChangeSupport(this); 2) public void
addPropertyChangeListener(PropertyChangeListener
pc_listener){
|
public interface
PropertyChangeListener extends
EventListener
A "PropertyChange" event gets fired whenever a bean changes a "bound" property. You can register a PropertyChangeListener with a source bean so as to be notified of any bound property updates. void propertyChange(PropertyChangeEvent evt) // This, it's only method is called when a bound property is changed. . |
Target Beans
The target bean only real requirement is to
support
a method that includes the appropriate
EventObject subclass in it's argument.
Target beans do not have to implement listener
interfaces. The bean builder tool will provide
a hookup or adapter class that achieve the
same end. The hookup acts as a proxy for the
target implementing the required interface
on the targets behalf.
public class PropertyChangeSupport extends Object implements Serializable
This is a utility class that can be used by
beans
that support bound properties. You can use
an instance of this class as a member field of
your bean and delegate various work to it.
Constructor
//
from jdk1.2.2 API docs
PropertyChangeSupport(Object sourceBean) | Constructs a PropertyChangeSupport object. |
Methods (5 of 9 methods)
void
addPropertyChangeListener
(PropertyChangeListener listener) |
Add a
PropertyChangeListener
to the listener list.
// 1 of 2 |
void
firePropertyChange
(PropertyChangeEvent evt) |
Fire an existing
PropertyChangeEvent
to any registered
listeners. Overridden to take (String propertyName, type oldValue and type newValue) where type is int boolean or Object in which case the method reports an int boolean or Object , bound property update to any registered listeners. // 1of 4 |
boolean
hasListeners
(String propertyName) |
Check if there are any listeners for a specific property. |
void
removePropertyChangeListener
(PropertyChangeListener listener) |
Remove a PropertyChangeListener from the listener list. |
void
removePropertyChangeListener
(String propertyName, PropertyChangeListener listener) |
Remove a PropertyChangeListener for a specific property. |
Constrained Properties
Constrained properties are similar to bound
properties
but also allow the target beans to
reject updates to their properties.
Rejecton
may occur because a new value is illegal,
invalid or just inappropriate. The Bean normally
maintains a VetoableChangeListener(s)
list in addition to a list of PropertyChangeListener(s).
// If the
target
does not throw exception then the new value is excepted into the target.
// In practice Beans may have
both bound and constrained properties being notified in parallel.
Example of implementing a
vetoable change
//
from
JavaBean
Short course
private VetoableChangeSupport vetoes = new VetoableChangeSupport (this);
public
void addVetoableChangeListener (VetoableChangeListener v) {
public void setSalary
(float salary) throws PropertyVetoException {
public void vetoableChange(PropertyChangeEvent
e)
// notice if the
vetoableChange
method throws it's exception, it cause it to be raised
|
This is a utility class that can be used by
beans
that support constrained properties. You can
use an instance of this class as a member field
of your bean and delegate various work to it.
Constructor //
from the jdk documentation
VetoableChangeSupport(Object sourceBean) | Constructs a VetoableChangeSupport object. |
Methods (5 of 9 methods)
void
addVetoableChangeListener
(VetoableChangeListener listener) |
Add a VetoableListener to the listener list. // 1 of 2 |
void
fireVetoableChange
(PropertyChangeEvent evt) |
Fire an existing
VetoableChangeEvent
to any registered listeners.
Overridden to take (String propertyName, typeoldValue and typenewValue) where type is int, boolean or Object in which case the method reports an int boolean or Object , bound property update to any registered listeners. // 1of 4 |
boolean
hasListeners
(String propertyName) |
Check if there are any listeners for a specific property. |
void
removeVetoableChangeListener
(VetoableChangeListener listener) |
Remove a PropertyChangeListener from the listener list. |
void
removePropertyChangeListener
(String propertyName, VetoableChangeListener listener) |
Remove a PropertyChangeListener for a specific property. |
public interface
VetoableChangeListener extends
EventListener
A "VetoableChange" event gets fired whenever a bean changes a "constrained" property. You can register a VetoablerChangeListener with a source bean so as to be notified of any bound property updates. void vetoableChange(PropertyChangeEvent evt) // This, it's only method is called when a constrained property is changed. . |
The following example is excerpted from the
custom event modeling shown in the
'Java Beans Short Course' in the tutorial
section at the sun site. Here is the url
http://developer.java.sun.com/developer/onlineTraining/
Beans/JBShortCourse/beans.html#beanWriteEvent
Bean Events
When writing a JavaBean you may wish to
provide
it with it's own customized events. If
you are extending stock beans like awt and swing
events you can use the event processing
that have been provided. It is useful to consider
a customized event strategy as it shows
clearly the underlying workings of the Delegation
Event Model used in Java.
An event allows your Beans to communicate when
something interesting happens.
There are three parts to this communication:
EventObject
The java.util.EventObject class is the basis of all Beans events.
public class java.util.EventObject
extends
Object
implements java.io.Serializable {
public
java.util.EventObject
(Object source);
public
Object
getSource();
public
String
toString();
}
Although you can create EventObject instances
directly for your Bean events, design
pattern guidelines require you to subclass
EventObject so you have a specific event type.
For example, to define an event for an employee's
hire date, you could create a HireEvent
class.
public class HireEvent extends EventObject {
//
concrete
implementation of EventObject
private
long
hireDate;
public
HireEvent
(Object source) {
super
(source);
hireDate
=
System.currentTimeMillis();
}
public
HireEvent
(Object source, long hired) {
super
(source);
hireDate
=
hired;
}
public
long
getHireDate () {
return
hireDate;
}
}
EventListener
The EventListener interface is empty,
but
serves
as a tagging interface that all event
listeners must extend in order for Bean
integration
tools can work. A listener is something
that desires notification when an event happens.
The listener receives the specific EventObject
subclass as a parameter. The name of the new
listener interface is EventTypeListener.
For the new HireEvent, the listener name would
be HireListener. The actual method names
within the interface have no specific design
pattern to follow, but, should describe the event
that is happening. // the name
of the method of the method doesn't have to follow a naming pattern
public
interface HireListener
extends
java.util.EventListener
{
public
abstract
void hired (HireEvent e);
// the key is the method has to take the event object
}
Event Source
Without an event source, the HireEvent and
HireListener
are virtually useless. The event
source defines when and where an event will
happen.
Classes register themselves as
interested in the event, and they receive
notification
when the event happens. A series
of methods patterns represents the registration
process:
public synchronized void addListenerType(ListenerType l );
public
synchronized void removeListenerType( ListenerType l );
The event source needs to maintain the list itself, so the entire code to do this is:
private Vector hireListeners = new Vector();
public
synchronized void addHireListener (
HireListener
l)
{
hireListeners.addElement
(l);
}
public synchronized void removeHireListener (HireListener l) {
hireListeners.removeElement
(l);
}
If you are using AWT components, AWT events
already
have this behavior. Maintaining
listeners is only necessary for new event types,
or adding listeners where they previously
were not (ActionEvent within a Canvas). If you
want to permit only one listener (unicast),
you have the addListenerType method throw the
java.util.TooManyListenersException
exception when adding a second listener (and
you do not need a Vector). Also, remember
to synchronize the add/remove methods to avoid
a race condition.
Once the event happens, it is necessary for
the
event source to notify all the listeners. For
the hiring example, this would translate into
a method like the following:
protected void notifyHired ( ) {
Vector
l;
// create Event
HireEvent
h
= new HireEvent (this);
//
Copy listener vector so it won't
//
change
while firing
synchronized
(this)
{
l
=
(Vector)hireListeners.clone( );
}
for
(int
i=0;i<l.size();i++) {
HireListener
hl
= (HireListener)l.elementAt (i);
hl.hired(h);
}
}