There's nothing really magic about JavaBeans, but you can grow some interesting things from them.
Introduction
Code reuse is a lot like the weather. Everybody talks about it, but not too many people are actually doing anything about it. Componentware finally made code reuse popular and convenient. JavaBeans extends the concept of component architecture to Java.
Componentware made its debut about ten years ago with a little-known product at the time called Visual Basic. There were two things about Visual Basic that contributed greatly to its success: it was easy to use, and it had a large selection of ready-made components that you could drag from the toolbox onto a form. You could then readily customize the component through property sheets.
Visual Basic implemented components in a novel way as well. It reused compiled code, not source code. This immediately solved two large problems that had plagued code reuse. The first problem was that in order to reuse the component, you had to understand the source code. The second problem was that in order to customize the behavior of the component, you had to modify the source code. Trust me on this, code reuse isn't really code reuse if you have to modify the code each time you want to use it. Code reuse isn't of much value either if you can't find the code to reuse it. Putting a component in a toolbox makes it easier to find than searching through a code library with thousands of entries.
JavaBeans is a component architecture for Java that solves the problem of real code reuse in much the same manner as the old VBX components. JavaBeans, however, solves the problem in Java's typical platform-independent fashion.
Defining a JavaBean
Following are a few typical questions about JavaBeans:
What exactly is a JavaBean?
A JavaBean is piece of compiled code that can be customized through properties, can be acted upon with methods, and can generate events to let others know that things have happened. A JavaBean is written in Java. It does not use any extensions or features outside the language, but conforms to certain specifications, such as naming conventions for functions that get and set properties.
When is a Bean a Bean?
Technically, Java code can be considered a bean even if it has only one property that can be customized. The bean in Figure 1 is about as simple a JavaBean as you can get. Using the bean is no different than using other Java code. As Figure 2 shows, it isn't any harder either.
What is a property and why isn't it data?
The bean shown in Figure 1 has a property called Count. The property is called Count, not counter, because a property is defined by method names, not data names. If you were to add this bean to an IDE (Integrated Development Environment), the development tool would perform a process called Introspection on the methods of the bean, looking for a set of methods that match a get/set naming convention. From the names of the methods, the tool infers the name of the property.
In this particular case, there is a method called getCount. The IDE assumes, at this point, that there is an integer property called Count, and that it is a read property. When the IDE continues its analysis of the bean, it finds the setCount method, and notes that the Count part of the name matches the previously-found property called Count, and that the parameter type of int matches the return type of the getCount method. Therefore, the IDE determines that there is an int property called Count that is both a read and write property. The fact that both methods use a piece of data called counter is inconsequential to the name and data type of the property. This decoupling allows the mechanism for storing the data internally to change without affecting users of the bean.
Additionally, properties may be arrays, referenced by an index, bound, and constrained. A full discussion of these array characteristics is beyond the scope of this article. Suffice it to say that a bound property is one that notifies other code that the data has been changed, similar in nature to a typical data-bound control in other languages. A property's value can also be constrained by other outside code to fall within a certain range or meet other criteria.
What about methods?
JavaBeans can contain methods, exactly like methods in any other Java class. They are simply there to make the bean do things. Beyond the methods used to access properties, there are no naming conventions or other restrictions on methods inside a bean.
What is an event?
An event is something that happens. The bean generates an event object describing the event to let anyone who cares know about it. Events occur all the time, for example, a war breaks out somewhere. Consider the next issue of Time magazine. The war is the event, and the article in Time is an event object that describes the event. The magazine article just describes the event, but is not the actual event itself. After the article is written, a copy of the magazine is mailed to its subscriber list. When you pull the magazine out of the mailbox, you read all about it, and know that an event occurred.
A Bean Example
One basic need of many GUI programs is a timer. What would Solitaire be without an occasionally smiling sun? When would a browser know when to give up waiting for a site to respond? When would my word processor know to autosave this article? All these tasks require a timer. In this section I show how to build a JavaBean to act as a general-purpose timer. Along the way, I also cover a few of the concepts of multithread programming in Java, just because that's how a timer works. There's no requirement that a JavaBean include a separate thread.
The timer bean can be used in any Java program that needs to do something on a regularly scheduled basis. For example, I recently wrote a Java program for my own use to receive stock quotes over the Internet every five minutes, and display a running total of the value of the portfolio. A timer bean drives the code to call the stock quote provider.
The timer bean needs a property called Interval, that controls how often the timer fires (see Figure 3). Although the name of the property is Interval, the data belonging to the property is actually stored in an integer called theInterval.
Figure 3 also shows another boolean property called Suspended. Naming conventions allow for a slightly different naming pattern for boolean properties. Instead of a getSuspended method, the pattern calls for an isSuspended method. Other than that, a boolean property is exactly the same as a property of any other data type.
I reserve the discussion of synchronized and notifyAll until I have delved more deeply into the thread portion of the timer.
The Publisher/Subscriber Event Model
JavaBeans use a publisher/subscriber mechanism for notifying the user of a bean that events have happened. The mechanism is analogous the process described earlier concerning magazines. If you want to be notified of the events that a bean produces, you must become a subscriber to the bean's events. The scenario for a magazine works like this. You are standing around the grocery store looking at the magazine rack and happen to see a magazine (this one, perhaps) that looks interesting. You fill out the subscription card and send in some money. About six weeks later, copies of magazine start appearing in your mailbox.
What seems like a simple process is actually a bit more complicated. Not everyone can subscribe to a magazine. At the very minimum, two things are usually necessary: you must have an address capable of receiving mail, and you must send in some money. In the case of a JavaBean, the subscriber also must meet some qualifications before the bean will agree to start sending events to the subscriber. Since the notification mechanism uses callbacks, the bean must be guaranteed that the subscriber actually has the correct method for the callback. JavaBeans use interfaces to guarantee subscriber qualifications.
The Role of Interfaces
Interfaces are used extensively by Java to guarantee that a class contains the methods other code expects to be there. Since Java does not support multiple inheritance, it must use another mechanism to guarantee that specific method signatures are present in the target code. In many ways, an interface is a lot like an abstract class that contains only abstract methods. Just as a concrete class inherits an abstract method signature from a superclass, the class that implements an interface must contain a concrete method that implements the signature defined by that interface, otherwise the program will not compile.
To enable user code to subscribe to the events generated by the bean, the bean provides an addxxxListener method with a single parameter of type xxxListener, where xxxListener is the name of the interface. The bean doesn't really care what kind of object subscribes to its events, just that the object has the appropriate callback methods. Defining the parameter as an object that implements an interface fits the bill perfectly (See Figure 4).
In our case, the TimerBean is expecting that the events will be sent to an object implementing the TimerListener interface (Figure 5), thus ensuring that the callback method, timerFired, exists.
The Mailing List
The references to the subscribing users are stored in a Vector, allowing the bean to cycle sequentially through the list (see Figure 6). First, an event object is created containing all the information relevant to event (see Figure 7). In this case, the event object contains a reference to the timer creating the event, the number assigned to this timer, and a counter representing the event number.
When you subscribe to a magazine, it usually takes four to six weeks to get the first issue. Why does it take that long? Why can't you get the very next issue? Part of the answer lies with the mailing list. Imagine a scenario where the current mailing list is spun off to a tape and mailed to a company that prints mailing labels. Even though you may have subscribed before the current issue is printed and mailed, you may not have been a subscriber at the time the tape was cut. Hence the delay. It doesn't work well to print mailing labels from a live list. There's too much potential for error while names are continually added and deleted from the list. It's easier to freeze the list, make a copy, and work from the copy. Since your name will be in the next list, you get the next issue. The code in Figure 6 works the same way. Make a copy of the list stored in the vector, then cycle through the list, calling the timerNotified method on each listener. The synchronized keyword prevents any change to the subscriber list during the time it takes to make the copy. I cover the synchronized keyword later.
Customizing an Existing Bean
One other nifty feature of JavaBeans is the ability to customize the properties of a JavaBean before you ship it to your customer. It's one thing to be able to customize the properties of a component during the development stage of a program, but it's quite another to be able to deliver the customized version of the bean to a customer. The new values for the customized properties must be remembered somewhere so that when the standard bean is loaded into memory, the customized values for the properties overwrite the standard values. With JavaBeans, the values for the properties are serialized to a file with a ser extension, essentially the same as writing the bean object to disk. If you want to load a customized instance of the bean, your code looks to the disk to find the .ser file. When the file is read in, the object is created and the standard data is overwritten with the customized data in the .ser file. This provides instant customization of the bean. In the code shown, if the appropriate .ser file cannot be found, the default constructor for the bean is called to instantiate the bean.
Figure 8 modifies the simple client used earlier in Figure 2. The new code writes the simple bean object to disk after changing the value of the Count property. When the bean is loaded back into memory in Figure 9, Count still has a value of 33. Also note that the mechanism for creating the bean has been changed. Instead of using the normal, default constructor, the Beans.instantiate method reads the bean in from disk, along with its customized values.
Delivering the Goods
JavaBeans are typically delivered to a user via a jar (Java archive) file, which includes the bean, any support classes used by the bean, and customized values contained in a .ser file. The jar file uses exactly the same compression technology as zip files, and thus can be read by any tool that reads standard zip files.
The jar file includes one additional file, a manifest, which describes to an IDE tool using the bean which classes in the jar file are the actual beans. A manifest for the timer bean would look something like this:
Manifest-Version: 1.0 Name: TimerBean.class Java-Bean: TrueCreating the jar file is done using a simple command-line utility, as shown here:
jar cvfm timer.jar manifest.txt *.class *.serThe flags cvfm indicate that we want to create a jar file, to generate verbose output, to specify the file name of the archive, and to include a manifest. The name of the resulting output is timer.jar. The name of the manifest is manifest.txt. That last two tokens on the command line tell the utility to include in the jar file everything in the current directory that ends with class or ser.
I have now covered all the basics for creating, customizing, and delivering simple JavaBeans. The rest of this article quickly covers the aspects of threading used by the timer bean.
Threads, Briefly
Simply stated, a thread in Java is an object that extends the Thread class and overrides the run method. Once started, the thread continues to exist until the code falls out the bottom of the run method. The code in Figure 10 shows a more common variation, in which the timer bean contains an object of class Thread and the bean implements the Runnable interface. When the Thread object is instantiated, a reference to the bean object is passed to the constructor, which directs the thread to use the delegated run method of the bean, rather than an overridden run method. Note also the setDaemon method, which designates the thread as a worker-bee thread. If for some reason the main thread, such as the user interface thread, goes away and only worker-bee threads are left running, the program will still terminate. It doesn't do much good to have a timer running forever with no one listening to timer events.
The user calls the bean's init method (Figure 11) to start the timer. When the runner.start method is called, all the threading housekeeping is performed and control is passed to the delegated run method.
The heart of the thread is the run method shown in Figure 11. The code stays in the while loop until the running flag is set to false by calling the bean's destroy method.
The method goes to sleep for the time specified by theInterval. When the timer awakes, it executes the notifyFired method (Figure 6) to inform all subscribers that the designated time has passed.
Stop and Go
This timer bean has been enhanced to allow a user to temporarily suspend and resume the timer by calling the setSuspended method (Figure 3) with a true parameter to suspend and false to resume. Note an important aspect of this architectural design: the bean receives a request to suspend itself; it is not arbitrarily suspended by an outside force. The bean is in control of when the request is executed in this case, at the top of the while loop. The bean suspends itself only when it is in a quiescent state, rather than, say, in the middle of notifying users of events. When the bean user requests that the bean suspend itself, the wait method inside checkIfSuspended executes (see Figure 12). All activity in the bean is suspended until notifyAll is called (see Figure 3). The notifyAll method wakes up all suspended threads that are waiting on the monitor.
Synchronization
If you ever had two people with two separate checkbooks writing checks on a single account, you will have an instinctive understanding for the need for a monitor and the synchronized keyword. Trust me on this one too. The voice of experience is speaking. It is virtually impossible to balance such an account or to know at any time exactly how much money is in the account. To control the account, there must be just one checkbook, and the only person who can be allowed to write checks is the one who has physical possession of the checkbook. The checkbook is analogous to a monitor, and the timer thread is analogous to the checking account.
To affect the state of the timer, client code must have the monitor in its possession. The keyword synchronized guarantees that once a client has access to the timer object, no other user will be able to affect the timer until the current user has completed execution of the synchronized method.
To ask the timer to suspend itself via the wait call (Figure 3), the client code must have exclusive control over the timer. To make the timer resume operation, the client code must again have exclusive control while calling the notifyAll method.
Conclusion
There's really nothing difficult about writing a JavaBean. The JavaBeans specification defines a standard set of coding conventions for properties, methods, and handling events. The specification also defines a simple method of delivering beans to the customer along with customized values for properties. There is also a well-defined procedure for creating property sheets for use by IDEs, but that's another entire article. On the CUJ ftp site I provide a simple application to use the JavaBean created in this article. (See p. 2 for downloading instructions.)
Reference
Robert Englander. Developing Java Beans (O'Reilly, 1997).
Don Weiss is the president and founder of Step1, Inc., a company specializing in client/server programming training. With 27 years in the industry, Don has been teaching and developing Java courseware for the last three-and-a-half years. You can email Don at dweiss@step1inc.com.