This module will teach you how to use the JavaBeansTM API to create independent, reusable, platform-independent, marketable components with JavaTM technology.
A JavaBeans component is an object that conforms to a communication and
configuration protocol, as prescribed by
the JavaBeans
specification. The JavaBeans specification prescribes programming
conventions and dynamic discovery mechanisms that (1) minimize the design and
implementation effort for small software components while (2) fully supporting
the design, implementation, and assembly of sophisticated components. The
three fundamental aspects of the JavaBeans component as defined by the
specification are events, properties, and methods.
The success of the JavaBeans model is due in large part to its "division
of labor" strategy. Consider the design of a common graphical component
such as a progress bar. The fundamental task of displaying a
percent-completion bar is quite easily handled with basic drawing operations.
With the JavaBeans model, you can implement a basic progress bar Bean in 200
lines of code or less. That is, with respect to design and implementation, the
model does not impose any significant up-front overhead. In other words, the
overhead is minimal.
Much of the JavaBeans API is optional. Supplemental tasks such as supporting
configurable foreground and background colors, vertical or horizontal
configuration, and increment size are easily handled with minimal programming
effort due to various API conventions. For tasks such as providing a
sophisticated, custom color editor, the JavaBeans API comes to the rescue with
a variety of supporting interfaces and classesall optional.
The JavaBeans architecture, a modern component architecture, is designed for
building independent software components from which programmers assemble
larger application components. With JavaBeans components, programmers have
total freedom to assemble components using traditional programming strategies
or to use graphical builder tools to connect JavaBeans components, or to
combine both techniques.
The JavaBeans architecture is the standard component architecture for Java
technologies. The complete JavaBeans API is packaged in
java.beans
,
one of the core Java APIs. This package includes interfaces and classes that
support design and/or runtime operations. In developing a JavaBeans component,
it's common to separate the implementation into design-only and runtime
classes, so that the design-oriented classes (which assist programmers during
component assembly) do not have to be shipped with a finished application. The
JavaBeans architecture fully supports this implementation strategy.
There are additional supporting APIs available. The
Glasgow specifications
define the principal new JavaBeans capabilities. Parts of
this specification are incorporated into the Java 2 platform, version 1.2, for
example, the drag and drop subsystem; other facilities are available as a
Standard Extension, for example, the JavaBeans Activation Framework, which
defines standard mechanics for Bean instantiation and activation.
The InfoBus specification
defines a secondary API that provides yet another, possibly alternative,
communication mechanism among Beans. The InfoBus provides programming
conventions and mechanics whereby JavaBeans components can register with
either a default or a named "information bus." Components cooperate
by getting on the same bus and exchanging information following an
asynchronous (event-driven) communication protocol.
The core/standard JavaBeans API is a fairly lightweight component
architecture. The discussion here focuses on standard JavaBeans programming.
You, of course, can track upcoming developments in the Glasgow and InfoBus
APIs via the previously mentioned websites.
Component technology's time has come. The concept of designing applications by
plugging together components has lingered in programmers' minds for 30 years,
since M. D. McIlroy (1968) made a now-historical plea for catalogs of software
components. The practical tools necessary for McIlroy's vision of
libraries of software components now exist, fundamentally with the core Java
programming language, but also in the JavaBeans API for client-level component
assembly, and the Enterprise JavaBeans specification for server-level
component assembly.
For the last 30 years, programmers have swayed to and fro on the component
issue, drawn in one direction by management's continued reliance on outdated
technologies and in the other by a series of programming developments that
include languages such as Smalltalk, Eiffel, and now the Java programming
language. The recent shift in programming paradigms, attributable in part to
Internet developments, has forced component technology out of the shadows.
There is, finally, light at the end of the tunnelit's a great time to be
a programmer.
Component technology in the 1990s incorporates the event-driven methodology of
the late 1980s. Software components support synchronous communication via
method calls. In addition, components communicate asynchronously using an
event and notification model that's often characterized by terms like
subject-observer or source-target.
Principally, Beans are source objects. Typically, a Bean does its work and, at
times, sends notifications of changing state to all registered targets. These
notifications are component-specific; that is, they signal the occurrence of
one or more significant events in the Bean instance. In a drop-down list, for
example, selecting an item would constitute such an event.
Hence, interested parties, for example, a phonebook application, can register
with the source object for event notifications and perform their own
component-specific operations in response to these events, for example,
dialing the telephone number selected in the drop-down list.
As another example, the following worksheet from Sun's demonstration JavaBeans
tool, the BeanBox
, illustrates a Progress
Bean as well as
ProgressTarget
, a primitive target Bean designed simply for testing
Progress
Beans:
A Progress
instance reports each change in its progress-completion
value as an event to all registered targets. A ProgressTarget
instance registers for these events and displays each new progress-completion
value.
Many enterprises are now designing libraries of JavaBeans components because
the event-driven approach to software components forces designers to think in
terms of published, significant events, as opposed to simply implementing a
zillion methods for controlled, synchronous access to an object's current
internal state. The JavaBeans approach works because (1) it reduces the
surface area of the component API and (2) it's conducive to component assembly
and connection with integrated development environments (IDEs), graphical
builder tools, or RapidApp tools, whatever your preferred terminology.
In many cases, a JavaBeans component will function as a source for certain
types of events, yet be capable of registering as a target for events produced
by other components. For example, a heating and cooling unit controller would
typically register with a thermostat component for notification of temperature
changes (temperature-change events) and at the same time function as a source
for heating and cooling unit on-off event notifications to multiple heaters,
boilers, coolers, and/or air conditioners. See the controller registered as
targets[3]
in the following scenario:
The upcoming sections address the issues involved in designing a JavaBeans
component with one or more event notification schemes and basic configuration
support, as well as providing customized configuration controls within an IDE.
The JavaBeans API includes several interfaces and classes in the
java.beans
package. In addition, it employs interfaces and classes from other Java
technology API areas including:
- The Java event model:
java.util.EventObject
, java.awt.event
- Object serialization:
java.io.Serializable
, java.io.Object*
- Reflection:
java.lang.reflect
The Java Development Kit (JDKTM) 1.0
included the AWT (Abstract Window Toolkit) for graphical user interface (GUI)
implementation. This version of AWT used a containment model for
events. That is, GUI events were generated on a component-by-component basis
and any and all events that were not "handled" within their
"home" component propagated upward or outward to the surrounding
container.
At each containment level, unhandled events propagated to the next level until
eventually unhandled events propagated to the outermost container. This
outermost container was, of course, some specialization of Window
,
typically, either a Frame
or Dialog
instance. At this level,
unhandled events simply disappeared into oblivion.
The containment model had several problems:
- By default, all common events were reported but often went unused, hence, a waste of computing cycles
- To process events of interest, programmers had to either:
- Subclass the GUI component class and override the appropriate event-related method
- Capture the events after they propagated outward to the surrounding container using (messy) case analysis
- Events did not exist outside the GUI framework
JDK 1.1 introduced the subject-observer or source-target
event model. JDK 1.1 provides base-level support for this event model
outside the AWT package, specifically, in the
java.util
package. The relevant interface, class, and exception are
java.util.EventListener
,
java.util.EventObject
,
and java.util.TooManyListenersException
.
With this approach, programmers can use event-driven communication strategies
above and beyond either GUI or JavaBeans contexts, for example,
thermostats communicating with heating and cooling unit controllers. Ideally,
however, nongraphical event-driven scenarios should take advantage of the
JavaBeans framework, because the JavaBeans component API is now well known and
supported by many IDEs.
In most cases, however, the JavaBeans API is used for designing graphical
components. The source-target event model is ideally suited for graphical
events such as mouse pointer motion, mouse button clicks, key activity, and
high-level component operations such as pressing a command button and
selecting items from a drop-down list. In fact, all AWT components employ the
JavaBeans framework for asynchronous event communication, as well as for
configuration support; that is, all AWT components are fully compliant
JavaBeans components. While the AWT components include support for the
original JDK 1.0 event model, components created to use the JavaBeans
framework cannot use the older model.
Object serialization is another core Java API area that is indispensable for
basic JavaBeans functionality. When programmers assemble, configure, and
connect Beans using an IDE, the JavaBeans components must be "live,"
dynamically created objects. The IDE must be able to save the worksheet's
state at the end of the day and restore it at the start of a subsequent
session. That is, a Bean's state must persist via external storage.
JavaBeans components, like all other user-defined data types in the Java
environment, support persistence by implementing the
Serializable
interface. Serializable
is a tagging interface; that is, it marks an
object as suitable for serialization by the Java runtime environment.
Before attempting to write an object to disk (or send it over the network) as
a serialized byte stream, the Java interpreter verifies that the object
implements Serializable
. Take for example, a worksheet such as the
one with connected, operational Progress
and ProgressTarget
Beans shown in the previous example. For an IDE to save the state of the
worksheet, it would use selected java.io.Object*
classes to serialize
the subsequently deserialize the worksheet contents (along with other IDE- and
project-related information).
Reflection is the third indispensable API
(java.lang.reflect
)
for the JavaBeans architecture. With reflection, it's straightforward to
examine any object dynamically, to determine (and potentially invoke) its
methods.
By examining a Bean dynamically to determine its methods, an IDE can analyze
design patterns in method names and put together a list of access methods that
retrieve and set instance data, for example, getForeground()
and
setForeground()
for retrieving and setting foreground color. With
the JavaBeans API, an instance/state variable with this type of pairing of
access methods is called a property.
Thus, when a programmer drags and drops a JavaBeans component from an IDE's
palette onto a worksheet, the IDE uses reflection to determine the Bean's
properties and present them for editing in a graphical window, commonly called
a property sheet. The main point, however, is that by using standard naming
conventions a programmer can design a Bean that's configurable in a graphical
builder tool with minimal attention to configuration-related details. This
somewhat-automatic, somewhat-default support for configuration is possible due
to Java technology's reflection mechanism, in particular, and due to Java
technology's dynamic nature, in general, both of which exist outside the
JavaBeans paradigm.
JavaBeans objects are like other user-defined data types, but with the
following additional options that make the objects more useful:
- Providing a public no-argument constructor
- Implementing
java.io.Serializable
- Following JavaBeans design patterns
- Set/get methods for properties
- Add/remove methods for events
- Java event model (as introduced by JDK 1.1)
- Being thread safe/security conscious
- Can run in an applet, application, servlet, ...
For an IDE to instantiate a bean, the class implementation must provide a
no-argument constructor. An example of this would be when a developer drags a
component from the palette and drops it into the design worksheet. After
several bean assembly, connection, and configuration operations, at some
point, the IDE will need to save the bean as part of a project. For this to
take place, the bean must be serializable.
For an IDE to automatically present various state variables for
configuration/editing, there must be access methods that follow prescribed
naming, return value, and signature conventionsthe JavaBeans design
patterns.
This design pattern principle applies to events as well. For an IDE to allow
communication connections between Beans, there must be add and remove methods
that the IDE can invoke to register and unregister targets (Beans that listen
to and respond to event notifications). That is, an IDE must be able to
connect the event notifications from one Bean to the event-handling
functionality of another Bean, typically, using a graphical, rubberband-like
point-and-connect strategy.
Lastly, the Java programming language supports multithreading and the Java
runtime environment is a multithreaded world. Screen-updating threads
asynchronously invoke a Bean's paint()
method, which typically is
dependent of certain state variables, which in turn are often settable through
a property sheet. With the Java environment, all user-defined classes should
be thread-safe; this requirement is especially significant with JavaBeans.
In some case, "bean-ifying" a user-defined data type leads to
additional design considerations. For example, suppose a class implements a
graphical label widget, like AWT's Label
except with multiline
support. The following Bean, FlexLabel
, implements this
functionality:
To allow the user to set the label (property), both programmatically and
within a Bean-aware IDE, you must have a strategy for representing line
breaks. This simple implementation of FlexLabel
uses the HTML
"<br>" tag. This type of text-and-linebreak representation is
mandatory for programmatic invocation of the setLabel()
method, but
only minimally sufficient within the property sheet.
Typically, you would provide a Bean support class that implements a custom
property editor, for example, something similar to the multiline text object
below, solely for use during design-time operations within an IDE:
The point is that in most cases you must support both mechanisms for modifying
such a state variable.
Each JavaBeans component will have its own specific design issues. Thus, going
beyond a standard user-defined data type implementation and implementing a
class as a Bean typically implies a little additional work. In most
programming environments, the extra work is easily justified because of the
added ease of use for programmers of all backgrounds.
Magercises
-
The Beans Development Kit
-
Working with Beans
As mentioned, JavaBeans uses the Java event model to communicate. Events
provide an alternative to (synchronous) method invocations for any type of
communication between components in which "background notifications"
are appropriate. That is, components providing one or more computational
services can acknowledge and handle other services on an event-driven, or
asynchronous, or "logical interrupt" basis.
For example, consider a software component that principally provides
telecommunications data analysis. You can design this component to
"listen" for incoming-data notifications, adding new satellite
data to its model on an asynchronous basis. In this case, the data processing
component is a target of some other software component that announces
the availability of data. With respect to the data processing component,
the data collection and deliver component is a source for telecom
data.
In an event-driven paradigm, the source and target orientation is a matter of
context. A component can be a source for one type of event and a target for
another. With JavaBeans, you're almost always implementing some type of source
functionalityfor significant events such as temperature changes,
progress-bar state changes, and so on.
In general, event-driven designs are ideal for a variety of tasks:
- Handing user interface events:
- Mouse actions
- Keyboard events
- Managing/reporting inter-client connections:
- JDBC Bean that connects to database server
- Notifies a client of specific changes in a database
- Accepts database requests and notifies a client when the data is available
- Other events:
- Property changes in a Bean
- Any general-purpose notification from one object to another
An event occurrence, as defined by the JavaBeans component (as the source), is
typically followed by some type of event notification. The event notification
process passes along pertinent event-related data to each registered target in
a specialized event object.
java.util.EventObject
is the base class for event objects.
New subclasses of EventObject
specialize
java.util.EventObject
for Bean-specific event types:
- Example: "employee-has-been-hired" event
- Example: "smiley-face-mood-changed" event
- Example: "stick-figure-body-parts-changed" event
Event objects carry pertinent/timely data to registered targets; this data is
typically read-only. Therefore, the target object uses the event object's
access methods to retrieve data.
Consider the following stick-figure Bean example:
Its event object could be defined as follows:
public class StickFigureEvent extends EventObject {
private StickFigure source;
private long date;
public StickFigureEvent(Object source) {
super(source);
this.source = (StickFigure)source;
date = System.currentTimeMillis();
}
public long getDate() {
return date;
}
...
}
The implication of this minimal, generic event notification is that
some significant event has occurred and the target object can use
the getDate()
access method to determine exactly when it
occurred. But, for any additional information regarding the source and/or
recent events, the target would have to discover what happened by
using the source's standard access methods. Did the stick figure's mood change
from a smile to a frown? Did the stick figure's waistline change?
In designing a JavaBeans component, it's important to allow sufficient
up-front design time for creating multiple, specialized event objects for
fine-grained communications regarding state changes in the Bean. That is,
how well does the Bean "express itself?"
With component technology such as Beans, there must be a way for a Bean to
say "I'm active; I'm doing various things such as X, Y,
and Z; who's interested?" And, target objects must be able to
say, "I'm interested in Y; let me know every time you do
Y."
With JavaBeans, a source publishes its "monitorable" behaviors using
interfaces called event listeners. The interfaces prescribe one or more
methods that the source will invoke when the appropriate event occurs. The
target implements the interface and the source notifies the target of the
significant event by invoking the target's method.
StickFigureListener
is an interface that uses the rather generic
StickFigureEvent
object:
public interface StickFigureListener
extends java.util.EventListener {
public abstract void
stickFigureChanged(StickFigureEvent e);
}
In designing a JavaBeans component, you must choose the events for which the
Bean will issue notifications, as well as the structure and syntax for the
event object(s) and event notification method(s). You may report different
kinds of events through different listener interfaces, each with a single
method, for example, stickFigureChanged()
, or
stickFigureMoodChanged()
, or stickFigureSunburnChanged()
, or
whatever. If appropriate, however, a single listener interface can bundle
together multiple notifications methods; consider
StickFigureBodyPartsListener
:
public interface StickFigureBodyPartsListener
extends StickFigureListener {
public abstract void
neckChanged(StickFigureEvent e);
public abstract void
trunkChanged(StickFigureEvent e);
public abstract void
leftArmChanged(StickFigureEvent e);
...
}
Ultimately, the level of granularity with respect to event notifications is up
to the Bean designer.
For a Bean to notify a target via the prescribed method call(s), the Bean must
have a reference to the target. Beans support target registration and
unregistration with add/remove methods:
public void
addAnEventListener(AnEventListener x);
public void
removeAnEventListener(AnEventListener x);
For example, StickFigure
could support targets with an interest in
body-part-change notifications via the following methods:
public void addStickFigureBodyPartsListener(
StickFigureListener listeningObject);
public void removeStickFigureBodyPartsListener(
StickFigureListener listeningObject);
Note that any target that registers an interest in StickFigure
events
by passing its object reference via these methods must implement the
appropriate method(s) (the Java programming language is strongly typed).
Another point is that the previous example uses a rather specific listener, in
conjunction with a rather generic event object. This approach may or may not
be appropriate; it simply depends on the design and function of the Bean. With
many Beans, different event classes may be justified for different event types.
Beans must, of course, keep track of the targets that register and unregister,
so they can notify the targets when an event occurs. It's common to store each
target's reference in a vector, for example,
private Vector targets = new Vector();
...
public synchronized void
addStickFigureListener(StickFigureListener l) {
targets.addElement(l);
}
public synchronized void
removeStickFigureListener(StickFigureListener l) {
targets.removeElement(l);
}
...
Thus, whenever the event occurs, the notification process is straightforward.
It simply traverses the vector and invokes the target's listener method:
protected void notifyTargets() {
Vector l;
StickFigureEvent s = new StickFigureEvent(this);
synchronized(this) {
l = (Vector) targets.clone();
}
for (int i = 0; i < l.size(); i++) {
StickFigureListener sl =
(StickFigureListener) l.elementAt(i);
sl.stickFigureChanged(s);
}
}
...
Note that it's important to synchronize access to the vector of target objects
during registration, unregistration, and notification operations so that the
vector doesn't change during direct processing. In the previous example, the
notification method synchronizes on the entire source object, makes a copy of
the target vector, and then operates on the copy. Another option is to utilize
the javax.swing.event.EventListenerList
object that comes with the
JFC/Project Swing classes.
Sometimes, depending on the design of the source, it's appropriate to limit
registration to a single target. If so, the appropriate way to enforce this
restriction is to throw a TooManyListenersException
instance whenever
a target attempts to register and the source is managing an existing target:
public void
addSomeEventListener(SomeEventListener x)
throws java.util.TooManyListenersException;
Lastly, as a convenience to other programmers using the bean, it's common to
provide an adapter class for each listener interface that packages multiple
notification methods. The adapter class simply provides null-body
implementations for each method in the interface, so that programmers
interested in only one method notification in the interface can extend the
adapter class in lieu of implementing the interface. For example, for the
body-parts listener interface, the adapter class would be:
public class StickFigureBodyPartsAdapter implements
StickFigureBodyPartsListener {
public void neckChanged(StickFigureEvent e) {};
public void trunkChanged(StickFigureEvent e) {};
public void leftArmChanged(StickFigureEvent e) {};
...
}
Thus, a programmer implementing a target can simply specialize
StickFigureBodyPartsAdapter
, overriding the null-body implementation
for the method of interest. Depending on the target, the adapter class
mechanism is sometimes not useful, because the target class implementation may
already be extending some other class, in which case the target class must
implement the listener interface to provide the functionality.
Properties are the public attributes of a Bean that affect its appearance or
behavior, for example, background color, foreground color, font, and so on.
For a thermostat Bean, the temperature change notification interval might be
designed as an integer property, say, one degree Celsius or three degrees
Fahrenheit. For a stick-figure Bean, whether or not the Bean instance is
sunburned could be a boolean property.
IDEs typically present properties in a property sheet (dialog box) for editing:
By default, properties are determined from get/set access method combinations
that follow a prescribed naming convention:
public void setXXX(TYPE value);
public TYPE getXXX();
The name of the property is the common part of the get/set method names, that
is, the characters following "get" or "set".
For the StickFigure
Bean, mood
(happy or sad) could be a
property:
public void setMood(int mood) {
this.mood = mood;
repaint();
}
public int getMood() {
return mood;
}
By default, IDEs analyze Beans for all conforming properties, organize them in
a property sheet, and automatically handle the data conversions required to
display their values and process modified values. For example, an integer
property is typically converted to a string and displayed in a text field
widget. When the value is updated via the property sheet, the IDE must convert
the new value to an integer and invoke the appropriate set method. The IDE
facilities for managing these property updates are called property
editors. Most IDEs provide several built-in property editors.
The previous example illustrates a common property type issue. IDEs provide
property editors for common data types. With a property such as mood
,
the best data types for representing its range of values are often strings or
integers. String handling can be quite messy programmatically and, in an IDE,
string data types for discrete-valued properties are subject to incorrectly
typed (misspelled) values. For this reason, programmers often use integers for
properties with discrete values:
public static final int HAPPY = -1;
public static final int SAD = -2;
...
private int mood = HAPPY;
...
For programmatic manipulation of a Bean, this design works well, but for
design-time manipulation in an IDE, having to know that happiness is
represented by -1
compromises the higher level approach to building
applications. For properties such as mood
, the preferred technique is
to design a custom property editor that accompanies the Bean class, which is
discussed subsequently.
For completeness, two secondary issues related to naming conventions should be
mentioned. First, the JavaBeans model does not preclude read-only and
write-only properties, identified by get-only and set-only access methods,
respectively. However, one-way properties are rather uncommon, and,
of course, cannot support the typical display-and-update behavior in a
property sheet. The second issue has to do with a second type of property,
indexed properties.
Besides simple properties, the JavaBeans model supports indexed properties.
Although uncommon, any object state/characteristic that fits well with the
indexed-value model will be recognized as long as you observe the following
naming conventions for access methods:
public void setXXX(int index, type value);
public type getXXX(int index);
public void setXXX(type values[]);
public type[] getXXX();
One example of a property that fits this model is color values:
public void setColorTable(int index, Color value);
public Color getColorTable(int index);
public void setColorTable(Color values[]);
public Color[] getColorTable();
The JavaBeans model supports two variations on standard properties: (1) bound
properties and (2) constrained properties. Bound properties support the
registration and notification of "interested parties" whenever the
value of the property changes. Constrained properties take this notification
model one step further, allowing the notified party to exercise a veto, to
prevent the property change. Unlike with event handling, most of the
functionality required to support bound and constrained properties is handled
by the JavaBeans framework.
Bound properties are useful for Beans that want to allow instances of the same
Bean class or some other Bean class to monitor a property value and change
their values accordingly (to match the "trend setting" Bean). For
example, consider a GUI Bean that wants to allow other GUI Beans to monitor a
change in its background color to update their backgrounds accordingly.
Implementing a bound property is quite easy because of the underlying
framework provided by the JavaBeans architecture. To support a bound property,
the Bean class must instantiate an object in the JavaBeans framework that
provides the bulk of this functionality, and implement registration and
unregistration methods that simply invoke the appropriate methods in the
JavaBeans framework.
private PropertyChangeSupport changes =
new PropertyChangeSupport(this);
public void addPropertyChangeListener(
PropertyChangeListener p) {
changes.addPropertyChangeListener(p);
}
public void removePropertyChangeListener(
PropertyChangeListener p) {
changes.removePropertyChangeListener(p);
}
Then, each bound property must invoke the firePropertyChange()
method
from its set method:
public void setMood(int mood) {
int old = this.mood;
this.mood = mood;
repaint();
changes.firePropertyChange("mood",
new Integer(old), new Integer(mood));
}
At this point, the PropertyChangeSupport
object takes over and
handles the notification of all registered targets. Note that
PropertyChangeSupport
provides general-purpose functionality
following a prescribed protocol. Specifically, the method invocation for
firePropertyChange()
must provide the property name, as well as
old and new values, which are passed along to notified targets.
The listener (target object) must provide a propertyChange()
method
to receive the property-related notifications:
public void propertyChange(PropertyChangeEvent e) {
// ...
}
Constrained properties add the functionality that the notified listener can
object to the property change and execute a veto. To support constrained
properties the Bean class must instantiate the JavaBeans object that provides
this service, namely, VetoableChangeSupport
, and implement the
corresponding registration-related methods:
private VetoableChangeSupport vetoes =
new VetoableChangeSupport(this);
public void addVetoableChangeListener(
VetoableChangeListener v) {
vetoes.addVetoableChangeListener(v);
}
public void removeVetoableChangeListener(
VetoableChangeListener v) {
vetoes.removeVetoableChangeListener(v);
}
Note that a Bean could provide one or more bound and one or more constrained
properties, in which case it must instantiate both
PropertyChangeSupport
and VetoableChangeSupport
and provide
both sets of registration-related methods. In addition, constrained properties
tend to also be bound, but that is not a strict requirement.
The set method for bound-constrained properties is slightly more complicated:
public void setMood(int mood)
throws PropertyVetoException {
vetoes.fireVetoableChange("mood",
new Integer(this.mood), new Integer(mood));
int old = this.mood;
this.mood = mood;
repaint();
changes.firePropertyChange("mood",
new Integer(old), new Integer(mood));
}
Specifically, the set method must accommodate the exception
PropertyVetoException
. Also, the sequence of operations is:
- Fire the vetoable change notification
- Update the appropriate state variables
- Fire the standard property change notification, if bound
A veto-interested target object must implement the
vetoableChange()
method:
public void vetoableChange(PropertyChangeEvent e)
throws PropertyVetoException {
// ...
}
It exercises a veto by (1) including a throws
clause for
PropertyVetoException
and (2) raising the exception
(throw new PropertyVetoException();
), as appropriate.
The JavaBeans framework receives the propagated exception and in turn
propagates it to setMood()
via the fireVetoableChange()
invocation. Note that setMood()
is organized such that in the event
of a veto (1) it does not handle the exception (instead, returning and
propagating the exception), hence, (2) it does not complete the update to
state variables or the property notification for bound-property listeners.
It's clear that the JavaBeans framework supports a lot of property-related
functionality and requires minimal work from the Bean implementation.
Magercises
-
The StickFigure Bean with Properties
-
The StickFigure Bean with Control Methods
The Java programming language is dynamic. A class instance "knows"
its data type, the interfaces it implements, and the data types of its
instance variables. An object can discovery many things about objects for
which it has a reference, for example, an object's methods and the methods'
parameters and return types. With this information, an object can instantiate
an object and formulate a method call on the fly, which provides more
flexibility than source code-level, compiled object instantiations and method
invocations.
In general, this process of discovering an object's characteristics is called
introspection. The JDK provides a collection of classes and
interfaces for introspection and dynamic manipulation of objects, commonly
known as the Reflection API. Reflection is one of the core Java APIs and is
packaged in java.lang.reflect
.
Reflection is covered in a separate MageLang course module.
IDEs use the Reflection API to instantiate an object dynamically when a
programmer drags and drops a Bean's icon from a palette to a worksheet. The
IDE must be able to instantiate the Bean, as well as discover its properties
for inclusion in the property sheet.
The Reflection API supports very general, low-level examination of objects.
The JavaBeans framework provides a higher level class,
Introspector
,
that's used by an IDE when working with Beans. An Introspector
object
assists in discovering a Bean's configurable characteristics. Developers who
use the JavaBeans architecture don't typically directly use
Introspector
, but their IDE environment does use it.
The Introspector
class provides functionality for a container to
discover information about a Bean, either by directly querying the Bean or
from working with a complementary Bean configuration class that optionally
accompanies each Bean. This complementary, support class is called a
bean-info class. The JavaBeans framework provides the interface
BeanInfo
,
which describes the services that bean-info classes implement, for example
publishing the list of configurable properties or defining an alternative way
of specifying accessor methods. An Introspector
object manipulates
and makes available a Bean's configuration services in a general-purpose
manner using the BeanInfo
interface. When there is no bean-info
class, the Introspector
object uses reflection to discover a Bean's
properties.
There are a variety of configuration possibilities with Beans: properties,
property editors, custom configuration dialog boxes, and so on. A Bean
publishes its configuration support via methods in its bean-info class. A Bean
analyzer then instantiates the bean-info class and queries the appropriate
method during the Bean configuration process.
A Bean analyzer searches for a bean-info class by appending
"BeanInfo"
to the Bean's class name, for example,
MyWidgetBeanInfo
TextFieldBeanInfo
StickFigureBeanInfo
Each IDE is free to design its own Bean analyzer class(es), but in all cases
the operation would be similar to:
TextField tf = new TextField();
BeanInfo bi =
Introspector.getBeanInfo(tf.getClass());
Any object that implements the BeanInfo
interface meets the data type
requirement implied in the previous code.
At times, no bean-info class is required; it's sufficient to provide standard,
bound, and constrained properties following the naming conventions outlined
previously. At other times, it's sufficient to provide one or two
configuration specifications, for example, to restrict the number of
properties displayed in the property sheet or provide a custom property
editor. For a StickFigure
Bean, it might be important to provide a
drop-down list for setting the mood
property.
As a convenience for the developers who use the JavaBeans architecture, the
JavaBeans API provides SimpleBeanInfo
, a class that implements
BeanInfo
with empty-body methods. You simply override the appropriate
methods with implementations that build and return the appropriate
configuration data.
For example, consider StickFigureBeanInfo
:
import java.beans.*;
public class StickFigureBeanInfo
extends SimpleBeanInfo {
public PropertyDescriptor[]
getPropertyDescriptors() {
try {
PropertyDescriptor pd1 = new
PropertyDescriptor("mood",
StickFigure.class);
pd1.setBound(true);
pd1.setPropertyEditorClass(MoodEditor.class);
PropertyDescriptor pd2 = new
PropertyDescriptor("sunburned",
StickFigure.class);
pd2.setBound(true);
return new PropertyDescriptor[] {pd1, pd2};
} catch (Exception e) {
return null;
}
}
}
In the previous example, MoodEditor
is a class that implements a
drop-down list for the mood
property.
Magercise
-
The StickFigure Bean with BeanInfo Support
There is nothing about the JavaBeans framework that implies that Beans should
be graphical objects such as command buttons and drop-down lists. A good rule
of thumb is that if it makes sense to drag and drop an object from a palette
to a worksheet as part of the application development process, it makes sense
to implement the object as a Bean.
From the previous sections, it's clear that "Bean-ifying" a class
can be quite simple because of the default Bean functionality. Minimally, you
must provide a no-argument constructor, implement the Serializable
interface, and so on as described previously.
The Bean framework is often overlooked for its value in debugging and testing
new classes. You can add simple properties that turn on debugging mode, or
initiate arbitrary operations when you change a boolean property's value from
false
to true
("off" to "on").
Manipulating application state in this manner is often simpler than writing a
test GUI that allows you to submit test values for processing.
Subsequent sections use a nongraphical Hire
Bean to reinforce this
idea and to illustrate Bean customization. The Magercises apply the
customizations illustrated with Hire
to the graphical
StickFigure
Bean, and others. The remainder of this section
introduces the Hire
Bean.
Hire
is a simple Bean that simulates employee hiring operations. For
clarity, the Hire
implementation is very minimal, but it could easily
be enhanced to track hiring operations in a human resources department.
Hire
uses the following event object to report recent hirings:
package hire;
import java.util.*;
public class HireEvent extends EventObject {
private long hireDate;
public HireEvent(Object source) {
super(source);
hireDate = System.currentTimeMillis();
}
public long getHireDate() {
return hireDate;
}
}
Interested targets (for example, manager objects for each department) must
implement the appropriate interface and register for event notifications:
package hire;
import hire.*;
public interface HireListener
extends java.util.EventListener {
public abstract void hired(HireEvent e);
}
The Hire
class manages targets with the vector hireListeners
:
package hire;
...
public class Hire implements Serializable {
...
private Vector hireListeners = new Vector();
private PropertyChangeSupport changes =
new PropertyChangeSupport(this);
public Hire() {
}
public synchronized void
addHireListener(HireListener l) {
hireListeners.addElement(l);
}
public synchronized void
removeHireListener(HireListener l) {
hireListeners.removeElement(l);
}
It issues hiring notifications with the method notifyHired()
:
protected void notifyHired() {
Vector l;
HireEvent h = new HireEvent(this);
synchronized(this) {
l = (Vector)hireListeners.clone();
}
for (int i = 0; i < l.size(); i++) {
HireListener hl =
(HireListener)l.elementAt(i);
hl.hired(h);
}
}
Hire
supports the bound property salary
:
public void addPropertyChangeListener(
PropertyChangeListener p) {
changes.addPropertyChangeListener(p);
}
public void removePropertyChangeListener(
PropertyChangeListener p) {
changes.removePropertyChangeListener(p);
}
public void setSalary(float salary) {
float old = this.salary;
this.salary = salary;
changes.firePropertyChange("salary",
new Float(old), new Float(salary));
}
public float getSalary() {
return salary;
}
At present, its bean-info class restricts the published properties to salary
:
package hire;
import java.beans.*;
public class HireBeanInfo
extends SimpleBeanInfo {
public PropertyDescriptor[]
getPropertyDescriptors() {
try {
PropertyDescriptor pd1 = new
PropertyDescriptor("salary", Hire.class);
pd1.setBound(true);
return new PropertyDescriptor[] {pd1};
} catch (Exception e) {
return null;
}
}
}
With this base functionality, Hire
provides a good starting point for
customization, as illustrated in subsequent sections.
As mentioned, IDEs provide property sheets for editing a Bean's configurable
state. Property sheets vary from one IDE to another, but typically appear as a
top-level dialog box.
The Beans Development Kit (BDK) provides a Bean testing tool, the
BeanBox
. Consider the BeanBox
's property sheet for a
Progress
Bean with no bean-info class:
Note that several properties are inherited from the superclass.
Next, consider the Hire
Bean:
HireBeanInfo
restricts the displayed properties to whatever the
developer feels is appropriate, in this case, a single property
salary
. These properties are commonly called published
properties.
Next, suppose we would like to display a department
property in the
property-sheet dialog. Further, suppose that department
is a
discrete-valued property, so that we would like to present the range of values
in a drop-down list for easy, error-free property updating:
To provide this functionality we must (1) implement the drop-down list as a
custom property editor and (2) publish its existence with HireBeanInfo
.
Task (1) involves several steps:
- Extend the
PropertyEditorSupport
class
- Implement the
getValue()
and setValue()
methods
- Implement the
getAsText()
and setAsText()
methods
- Implement the
getTags()
method for displaying values in a drop-down list
The JavaBeans framework provides a general-purpose mechanism for supplying
custom property editors of specific designs. To use this mechanism, the
developer who uses the JavaBeans architecture must implement certain
prescribed methods, namely, those listed previously, and the framework handles
the rest of the process. The getValue()
and setValue()
methods are invoked by the framework and provide a way to display and update
values. The getAsText()
and setAsText()
methods map discrete
values to user-friendly strings. Lastly, getTags()
lists the strings
(tags) for the drop-down list.
First, implement the methods for manipulating the property value, as displayed
in the custom property editor:
import java.beans.*;
public class DepartmentEditor
extends PropertyEditorSupport {
protected int dept;
public void setValue(Object o) {
dept = ((Integer)o).intValue();
firePropertyChange();
}
public Object getValue() {
return (new Integer(dept));
}
...
Note that the dept
instance variable is used within
DepartmentEditor
for local operations, and the name does not have to
agree with the state variable for the department
property in
Hire
. Note also that DepartmentEditor
specializes
PropertyEditorSupport
, which provides functionality for broadcasting
a property change; simply invoke the inherited firePropertyChange()
method to signal an updated value.
How does the system work? The JavaBeans framework invokes setValue()
and passes the data, namely, the current value of the property from the
Hire
instance, as an object. This value is stored locally, using
whatever data type is appropriate. The framework invokes getValue()
as necessary to display the property's value. The framework monitors updated
values not by polling, but more efficiently, by registering itself for
property change notifications, hence, the invocation of
firePropertyChange()
. Note that in this simple design you could have
stored the intermediate integer value as an Integer
object in lieu of
the performing the complementary conversions back and forth to int
.
Next, implement getAsText()
:
public String getAsText() {
switch (dept) {
case Dept.MARKETING:
return Dept.MARKETING_STR;
case Dept.TRAINING:
return Dept.TRAINING_STR;
case Dept.SALES:
return Dept.SALES_STR;
case Dept.RESEARCH:
return Dept.RESEARCH_STR;
default:
return Dept.UNASSIGNED_STR;
}
}
...
The framework uses this method to map string representations to the Bean's
data type for internal representation of property values, in this case,
int
. (Dept
is a public class that defines the constants.
These values could be defined in a Dept
interface, or within the
Hire
Bean itself.)
Next, implement setAsText()
, which the framework uses for the
complementary conversion:
public void setAsText(String s)
throws IllegalArgumentException {
if (s.equalsIgnoreCase(Dept.MARKETING_STR))
dept = Dept.MARKETING;
else if (s.equalsIgnoreCase(Dept.TRAINING_STR))
dept = Dept.TRAINING;
else if (s.equalsIgnoreCase(Dept.SALES_STR))
dept = Dept.SALES;
else if (s.equalsIgnoreCase(Dept.RESEARCH_STR))
dept = Dept.RESEARCH;
else
dept = Dept.UNASSIGNED;
firePropertyChange();
}
...
Lastly, provide a getTags()
method that delivers the tags for the
drop-down list as an array of strings:
public String[] getTags() {
return new String[] {
Dept.MARKETING_STR, Dept.TRAINING_STR,
Dept.SALES_STR, Dept.RESEARCH_STR,
Dept.UNASSIGNED_STR
};
}
Note that with this form of declarative specification for a custom property
editor, the developer off-loads much of the work onto the JavaBeans framework.
In many cases, this approach is considerably easier for the programmer than
directly building the actual user interface for a property editor.
Next, you handle task (2), namely, publishing the property editor via the
bean-info class. The updated HireBeanInfo
follows:
public class HireBeanInfo extends SimpleBeanInfo {
public PropertyDescriptor[]
getPropertyDescriptors() {
try {
PropertyDescriptor pd1 = new
PropertyDescriptor("salary", Hire.class);
PropertyDescriptor pd2 = new
PropertyDescriptor("department", Hire.class);
pd2.setPropertyEditorClass(
DepartmentEditor.class);
return new PropertyDescriptor[] {pd1, pd2};
} catch (Exception e) { return null; }
}
}
Thus, when an IDE's Bean analyzer requests the array of published properties,
it queries each PropertyDescriptor
instance for the associated
property editor, if any.
Magercise
-
The StickFigure Bean with a Custom Property Editor
Sometimes the developer who uses the JavaBeans architecture simply needs total
freedom to design a property editor for one or more, possibly specialized,
properties. In this case, the JavaBeans framework allows the developer to
design and register a custom, graphical objecttypically, as a collection
of GUI components in a container (panel). Most IDE's display the panel inside
a dialog box. In some IDEs, the dialog is modal; in others, it resides on the
desktop as a top-level window.
This customizer implementation is free to use any of the classes provided by
the JavaBeans framework, for example, PropertyEditor
. The customizer
must specialize java.awt.Component
and implement
java.beans.Customizer
. A Bean analyzer uses the
BeanInfo
-prescribed method getCustomizerClass()
to retrieve
the customizer.
The BDK ships with several sample JavaBeans, including ExplicitButton
and ExplicitButtonCustomizer
:
In this section, you'll implement a customizer for the Hire
Bean. A
customizer can do anything really; it is not limited to setting property
values. For the Hire
Bean, however, the customizer simply provides
GUI components for configuring two properties:
salary
: using a TextField
object
department
: using a Choice
object
The following outline summarizes the complete process. The customizer must:
- Publish itself via the bean-info class (
HireBeanInfo
)
- Implement the
Customizer
interface:
addPropertyChangeListener()
removePropertyChangeListener()
setObject()
- Provide a no-argument constructor:
- IDEs will use
newInstance()
to instantiate the customizer
- Subclass
Component
:
- IDEs will instantiate the customizer inside a dialog window
First, HireBeanInfo
must specify the customizer by implementing
getBeanDescriptor()
:
public class HireBeanInfo
extends SimpleBeanInfo {
public BeanDescriptor getBeanDescriptor() {
return new BeanDescriptor(
Hire.class, HireCustomizer.class);
}
...
Next, HireCustomizer
implements ActionListener
, to respond
to the <Enter>
key in the text field for salary
, and
implements ItemListener
, to respond to selections in the
Choice
instance for dept
:
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
public class HireCustomizer extends Panel
implements Customizer, ActionListener,
ItemListener {
private Hire hireObject;
private TextField salary;
private Choice dept;
private PropertyChangeSupport support;
...
Next, a no-argument constructor supports dynamic instantiation:
public HireCustomizer() {
support = new PropertyChangeSupport(this);
...
For simplicity, implement the user interface directly in the constructor.
First, create the dialog contents for salary
:
public HireCustomizer() {
...
setLayout(new GridLayout(2, 1));
Panel p1 = new Panel();
p1.setLayout(new BorderLayout());
add(p1);
p1.add(new Label("Salary:"), BorderLayout.WEST);
p1.add(salary = new TextField(20),
BorderLayout.EAST);
salary.addActionListener(this);
...
Next, build the dialog contents for dept
:
public HireCustomizer() {
...
Panel p2 = new Panel();
p2.setLayout(new BorderLayout());
add(p2);
p2.add(new Label("Department:"),
BorderLayout.WEST);
p2.add(dept = new Choice(), BorderLayout.EAST);
dept.addItemListener(this);
dept.add(Dept.MARKETING_STR);
dept.add(Dept.TRAINING_STR);
dept.add(Dept.SALES_STR);
dept.add(Dept.RESEARCH_STR);
dept.add(Dept.UNASSIGNED_STR);
}
...
You must provide the method setObject()
to capture the reference to
the Hire
Bean instance:
public void setObject(Object obj) {
hireObject = (Hire) obj;
salary.setText(
String.valueOf(hireObject.getSalary()));
selectChoiceItem();
}
private void selectChoiceItem() {
if (hireObject.getDepartment() == Dept.MARKETING)
dept.select(Dept.MARKETING_STR);
else if (hireObject.getDepartment() == Dept.TRAINING)
dept.select(Dept.TRAINING_STR);
else if (hireObject.getDepartment() == Dept.SALES)
dept.select(Dept.SALES_STR);
else if (hireObject.getDepartment() == Dept.RESEARCH)
dept.select(Dept.RESEARCH_STR);
else
dept.select(Dept.UNASSIGNED_STR);
}
...
Note that you cannot assume that the Hire
Bean is instantiated prior
to its customizer. Whenever setObject()
is invoked, you'll need to
receive and store the associated Hire
object reference using the
instance variable hireObject
. At this time, you can initialize the
GUI components for each property by querying the Hire
instance.
The customizer must respond to action events for the salary
property's text field:
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == salary) {
String text = salary.getText();
try {
hireObject.setSalary(
(new Float(text)).floatValue());
} catch (NumberFormatException ex) {
salary.setText(String.valueOf(
hireObject.getSalary()));
}
support.firePropertyChange(
"salary", null, null);
}
}
...
Also, it must respond to selections for the department
property's
Choice
object:
public void itemStateChanged(ItemEvent e) {
Choice c = (Choice) e.getSource();
String label = (String) c.getSelectedObjects()[0];
if (label == null)
return;
if (label.equalsIgnoreCase(Dept.MARKETING_STR))
hireObject.setDepartment(Dept.MARKETING);
else if (label.equalsIgnoreCase(Dept.TRAINING_STR))
hireObject.setDepartment(Dept.TRAINING);
else if (label.equalsIgnoreCase(Dept.SALES_STR))
hireObject.setDepartment(Dept.SALES);
else if (label.equalsIgnoreCase(Dept.RESEARCH_STR))
hireObject.setDepartment(Dept.RESEARCH);
else
hireObject.setDepartment(Dept.UNASSIGNED);
support.firePropertyChange("department", null, null);
}
...
Lastly, the customizer must support other objects that listen for property changes:
public void addPropertyChangeListener(
PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(
PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
}
The IDE, for example, can register an interest in property changes, depending
on its design.
Typically, customizers are popped up from a command button or a menu. In the
BeanBox
, for example, customizers are available from a menu:
With some IDEs, for example, SuperCede, customizers have a command button in
the property sheet.
Magercises
-
The StickFigure Bean with a Customizer Dialog
-
Exploring Bean Customization: FlexLabel
The JavaBeans API discussion began with a reminder that M. D. McIlroy (1968)
made a plea for catalogs of software components more than 30 years ago.
JavaBeans, of course, is the standard component architecture for Java
technology.
JavaBeans is particularly well-suited for asynchronous,
intra-application communications among software entities. That is,
JavaBeans is an intra-JVM (Java1
Virtual Machine) framework. Every target, for example, that registers with a
source does so by handing over its object reference, which the source Bean
typically maintains internally in a vector. This framework does not allow (in
any direct way) for inter-application source-target communication because
object references are local to the JVM that houses the running application.
There are several component technologies in the distributed computing arena.
Currently, the CORBA-based frameworks are the most popular, but newer
component technologies such as
Enterprise JavaBeans
and mobile agents, for example,
Aglets, show considerable
promise as well.
The JavaBeans technology has been enhanced in some environments to support
location transparency (almost). For example, the Java application server from
WebLogic/BEA Systems includes a
JavaBeans implementation wherein each Bean is (in effect) wrapped in a network
layer. Thus, the source Bean could be running in the application server and
the target object could be running on a distant client.
It's clear that there is now, finally, a strong movement toward
component-oriented design and implementation. Some enterprises dictate, for
example, that every Java class be designed and implemented as a JavaBeans
component. In these enterprises, programming groups exchange and share their
software developments as Beansthey use each other's work in a
plug-and-play application development setting.
There is, of course, some additional overhead in designing classes as Beans.
In some environments (IDEs), you must provide void
, no-argument
methods to connect Beans together graphically in lieu of traditional source
code-level method invocation in which the method signature has no limits.
Several IDEs, however, support Bean-connecting operations for methods that
have arguments and return data.
The fundamental Bean requirement of a no-argument constructor implies that
some programmers must change their design habits. In general, it's a bad idea
to use constructors for extensive class configuration during instantiation. At
first, it may seem like a good idea to provide 20 or 30 constructors for an
everything-but-the-kitchen-sink class implementationone for every
possible combination of relevant configuration options. Classes with many,
complex constructors, however, almost always lead to
"documentation-itis" during development and result in unreadable
source code once the application moves to the maintenance phase.
The (practical) research on software design has suggested for more than ten
years that certain object-oriented programming styles are effective, and, at
a somewhat more abstract level, certain design patterns tend to
reappear with incredible regularity in most properly designed (large)
applications. These design pattern are summarized and cataloged in
Design Patterns,
by Gamma, et al. (1994).
In the 1980s there was a movement, for a time, toward declarative programming
languages, for example, Prolog. A declarative versus procedural language
debate is beyond the scope of this discussion, but even procedural programming
bigots generally accept that declarative languages have merits, and most
(not all) procedural languages include declarative features, for example,
variable declarations.
A fundamental characteristic of declarative languages is that they allow
programmers to, in a sense, describe the state of an application, whereupon
the application adapts to that description, as opposed to requiring that
programmers intricately manipulate (execute) an application to arrive at a
certain state.
It's clear that the JavaBeans framework has a declarative flavor and
facilitates component design that involves plug-and-play, descriptive,
declarative assembly of an application from cataloged parts. That is, you
build applications by plugging and hooking together smaller components,
each of which takes on the responsibility of adapting itself to the
declarative specifications that appear in an IDE's property sheet, or
customization dialog.
The previous Magercises demonstrate the principal issues that arise in using
the JavaBeans framework. This section concludes with several Magercises that
reinforce the idea that you can (and probably should) design most classes,
even nongraphical classes, as Beans. For most programmers, "designing
with Beans" is simply a commitment to creating classes with reasonable
default values and methods that rigorously adhere to the
"test-its-value-before-using-it" principal.
For these somewhat larger Magercises your solution will surely differ from the
ones included here. The important issue, however, is that you provide a
workable mechanism for inter-component communication. With modern operating
and windowing systems, this approach implies a concerted effort to design all
components to adhere to and work well within an event-driven, asynchronous
paradigm.
Magercises
-
A Client Socket Bean
-
A URL Hex Dump Bean
-
A JDBC Bean Database Manager
-
Exploring an HTML Browser Bean
Further web-resident information on JavaBeans is available from:
Component technologies are prevalent in the distributed computing arena.
Recent books with distributed computing themes and coverage of component
technologies include:
- Farley, J. Java Distributed Computing. Sebastopol, CA: O'Reilly & Associates, 1998, ISBN: 1-56592-206-9.
- Lange, D. B. and Oshima, M. Programming and Deploying Java Mobile Agents with Aglets. Reading, MA: Addison-Wesley, 1998, ISBN: 0-201-32582-9.
- Orfali, R. and Harkey, D. Client/Server Programming with Java and CORBA, 2nd Ed. New York: John Wiley & Sons, 1998, ISBN: 0-471-24578-X.
The home for the ICE Browser Bean is ICEsoft.
The suppliers and parts data is based on similar data from Chris Date's
classic database text:
- Date, C.
An Introduction to Database Systems, 6th Ed. Reading, MA:
Addison-Wesley, 1994, ISBN: 0-201-54329-X.
Recent books on JavaBeans and related topics include:
- Englander, R.
Developing JavaBeans. Sebastopol, CA: O'Reilly & Associates, 1997,
ISBN: 1-56592-289-1.
- Gamma, E., Helm, R.,
Johnson, R., Vlissides, J. Design Patterns: Elements of Reusable
Object-Oriented Software. Reading, MA: Addison-Wesley, 1994,
ISBN: 0-201-63361-2.
- Rodrigues, L. H.
The Awesome Power of JavaBeans. Greenwich, CT: Manning, 1998,
ISBN: 1-884777-56-2.
Historical references for object-oriented and component-based programming
include:
- Cox, B. R.
Object-oriented programming: An Evolutionary Approach. Reading,
MA: Addison-Wesley, 1986, ISBN: 0-201-10393-1.
- McIlroy, M. D.
Mass-produced Software Components. In Software Engineering
Concepts and Techniques (republication of materials from the 1968 NATO
conference on software engineering, eds. Buxton, J. M., Naur, P., and Randell,
B.). New York: Petrocelli/Charter, 1976, ISBN: 0-884-05334-2.
_______
1 As used on this web site,
the terms "Java virtual
machine" or "JVM" mean a virtual machine
for the Java platform.
Copyright © 1998-1999
MageLang Institute.
All Rights Reserved.