Long-Term Persistence for JavaBeansBy
Philip Milne & Kathy Walrath
(Note: After you've read this article, please see the update.) At JavaOne '99 we gave a preliminary talk on a new persistence model for Swing that would allow Swing user interfaces to be "serialized" as XML documents. As we found out from the BOF sessions after the talks, there was a great deal of interest in this topic, much of it on the more general problem of archiving graphs of JavaBeans as XML documents.
Since then we've been working with the IDE vendors to generalize and refine these techniques to deal with the practical issues that arise in saving real designs as constructed by commercial tools. The recent surge of interest in Swing IDE's has brought the question of interoperability to the fore. At the heart of this issue is the question of persistence and how a design can be saved in a format that is not tied to the tool that created it. Now it's time to solicit feedback from developers working outside of the IDE space. We invite you to:
Please send any comments, criticisms, or ideas you have on this
work to java-beans@java.sun.com.
Design and Implementation of the Persistence ModelThe proposed persistence model is implemented in a downloadable package namedarchiver. The archiver package
works only with 1.3 versions of the Java 2 SDK.
This section describes the following aspects of the design and implementation of the persistence model:
GoalsThe new persistence model is designed to handle the process of converting a graph of JavaBeans to and from a persistent form. We had the following goals in creating a long term persistence scheme for user interfaces made of JavaBeans:
The Archiver Package The This formal separation allows the majority of the internal architecture and any special-case code that changes the way the state of a particular class is archived to be written in a form that is independent of the syntax of the output format. Given both the proliferation of new XML standards and their rapid evolution, this accommodating rather than defining role seems to be the best way to provide Beans with a long-term persistence strategy that can coexist with this evolutionary process. To test this approach we have implemented two very different formats as examples: a declarative XML format and a procedural Java-like scripting language. We've also implemented a third, output-only format that produces compilable Java files. The formal separation of evaluation semantics has also proven useful in the internal implementation of our redundancy elimination mechanism, which requires the write-time evaluation of the statements being written to the output. The
archiver
package are listed in the Archiver API
Documentation.
How to Use the New Output StreamsThe code for using the new output stream classes is almost identical to the code for using the binary serialization output stream class,ObjectOutputStream.
For example, if the code for using ObjectOutputStream
looks like this:
Then the code for writing an XML document requires just one small change: The code for using the new input streams is likewise similar to the code for using ObjectInputStream.
The Persistence ModelTypically the new streams provided in thearchiver package
reduce the serialization problem for JavaBeans to the problem of providing
an ordered list of properties that define the state of the JavaBean.
All values of the properties of a JavaBean are assumed to be JavaBeans.
To make this recursive definition work, we have to widen the notion
of what is considered a JavaBean slightly so as to include all possible
values that properties can take. In our implementation, Color
objects are considered to be JavaBeans, as are LayoutManagers,
Vectors, Hashtables, Numbers,
and Boolean values. To handle the "wiring" part of the
user interface it has also proven convenient to provide built-in support
for some other key classes in the JDK including Method,
Class, array classes, and proxy classes.
To handle all these extra classes the first requirement is that
we are able to create instances of them. In all the special cases,
this requires extra information that describes how a new instance
should be created. For most classes this extra information simply
associates the arguments of a chosen constructor with names of the
properties they represent. So, for example, the In other cases, such as IdentityAs the object graph is traversed a hashtable (actually a special kind of hashtable that uses "==" instead of "equals")
is used to detect when a node is revisited. When it is, the archiver
gives this instance a name so that it can be referred to multiple
times in the archive. That way the identity of objects in the
graph is preserved by the archival process.
SizeEven though an XML encoding, term for term, takes significantly more space than a binary encoding, the archives produced by the new streams are typically between 10x and 100x smaller than their serialized counterparts. This is due to a comprehensive system for excluding default information from the archives. For details, read Redundancy Elimination.ListenersA crucial part of a user interface, beyond the way it will appear, is the way it will be connected to the logic of an application. In the past this has only been possible by generating Java source files that implement event listener interfaces and compiling them at design time. With the introduction of thejava.lang.reflect.Proxy
API's in SDK 1.3 it is now possible to consider the "wiring" of the
user interface as part of the state of the design and saved as an
integral part of archive that represents it.
Even with the much improved footprint of inner classes in 1.2 the
generation of classes is still a potentially costly solution if
an inner class is generated for each action in a user interface.
Our example builder, Bean Builder,
demonstrates how instances of the Most importantly, the "trampoline" class that we have implemented
exposes all of its state using the Beans conventions. It can therefore
be archived in the same way that any other bean is archived
as a textual representation of its public properties. SummaryThe new input and output streams complement the binary serialization support that was introduced in JDK 1.1 with support for some new formats. The new streams have been implemented to a set of design goals that make them more suitable than binary serialization as a persistence mechanism for user interfaces. Committing the archives to the public APIs of the classes to which they refer makes the archives inherently more robust than those that contain private state. The redundancy elimination system used in the new streams makes the new formats attractive in that they are both human-readable and, in most cases, one or two orders of magnitude smaller than their binary equivalents.Downloading the Archiver PackageTo try out the new streams, download archiver.zip
(~100 K), unzip it, and follow the instructions in README.txt.
Information on using the new streams is in The
Archiver Package and How to Use the New Output
Streams Downloading the Bean Builder
To show how the new streams can be used in an IDE environment to save the designs that a user creates, we have built a simple BeanBox-style PropertyEditor/GUI builder and included support for persisting designs, including event handling, as XML documents. Like the original BeanBox, the builder is not a commercial product and is intended to serve only as an example of how these techniques would be used in a real IDE. This builder takes the original BeanBox concept forward a little by showing not just how the properties of a single Bean can be manipulated but how a group of Beans can be "wired up" to make the user interface part of an application. To try out the builder, download
| ||||||||||||
|
| ||||||||||||