| By William Wright | Article Rating: |
|
| December 1, 1999 12:00 AM EST | Reads: |
10,664 |
One of the great things about the JavaBeans specification is the flexibility it affords component developers in how they package their beans. As a bean developer, all you need is a class with a no-argument constructor that supports serialization and it's a bean. If you follow some simple naming conventions, most integrated development environments (IDEs) can tell enough about your bean to be able to use it for visual application development. While this is sufficient for some simple beans, the beans specification also provides ways for bean developers to explicitly give information that the IDE can use to assist the application developer in using the component. This article explores some of the ways that bean developers can make their components more usable by application developers through the use of custom property editors and customizers.
JavaBeans can be used by software development tools to allow visual programming using the beans as reusable components. This can reduce the amount of code that application developers need to write to make an application out of beans components. Beans are characterized by their properties and the events they generate and receive. Most beans have some properties that the application developer can configure during application construction to set the initial state of the bean. The Swing components are examples of beans with many properties that can be configured during application construction. They have properties like "backgroundColor" and "font" that control how the component looks and behaves. The bean developer needs only to follow some simple naming conventions when naming accessor methods to take advantage of this flexibility. If the bean has a property named "backgroundColor", a development tool will look for a method called "getBackgroundColor" that can be used to read the state of the property from the bean. If the bean has a method called "setBackgroundColor", it will be used to change the state of the property. Each of these methods is optional. That is, a bean can have a read-only property or a write-only property. Most beans are visual user interface components (like the Swing components), but beans can also be classes that are not visible on the user interface.
Development environments use Java introspection to interrogate a bean for its properties by looking for methods called get<something> and set<something> unless the property is a boolean; in that case the read accessor is called is<something>. With this information the IDE can build a graphical user interface called a customizer that the application developer can use to manipulate the bean. Often this is good enough. Simple beans may have properties that are of simple types and whose names are self-explanatory. Most IDEs include GUI components for Java primitive types and common classes like java.awt.Color and java.awt.Font, but if your bean has a property of a type that you defined, or isn't included in the IDE's set of property editors, it will just omit the property from the bean's automatically generated customizer. Also, more complicated beans may have dozens of properties - most of which are not important to the bean user - or have complicated dependencies that aren't visible through introspection. In these cases it would make the bean more useful if you could provide a simpler customizer that guided the user in how to configure the bean and helped prevent errors.
While the JavaBeans specification allows for IDEs to create customizers for beans using introspection, it also allows for bean developers to provide customizers to be used in place of, or in addition to, the default customizer. Bean developers have the flexibility to simply provide an editor for a single property to be used within the default customizer, or to provide a complete GUI customizer that can replace the default customizer altogether.
Property Editors
There are several ways to provide a custom property editor to be used within the default customizer that the IDE generates by introspection. I'll explore the simplest here with an example and then move on to a full-blown customizer.
Let's make a simple bean that's a digital clock. Its only important property controls whether it displays the time as 12-hour time with an AM/PM designation or as 24-hour time. I'll call this boolean property "twentyFourHourFormat" and when the property is true, the clock will use 24-hour time; when it's false, it will use 12-hour AM/PM time. The code for this bean is in Listing 1.
Beans-aware IDEs have a property editor for the boolean primitive type; Figure 1 shows the customizer and bean display that the Sun BeanBox provides for the Clock bean.
The BeanBox is Sun's reference implementation of a bean container. It's a part of the Beans Development Kit (BDK) and is available at http://java.sun.com/beans. The BeanBox is not a full-blown IDE, but it's a good tool for testing beans, property editors and customizers. In this case the BeanBox used introspection to generate the customizer panel that includes not only the twentyFourHourFormat property but also all of the properties from the bean's base classes. Notice that the property editor for the twentyFourHourFormat property is a combo box of the values true and false. The Introspector also found a property called "running" because the bean has methods called isRunning and setRunning. The "running" property should be hidden from the application developer so that the clock runs continuously. I'll show how to hide this property from the application developer later in this article.
The automatically generated customizer isn't bad, but we can make it a little more user-friendly. Let's define a custom property editor that gives the user more information about what the property does.
All property editors must implement the java.beans.PropertyEditor interface. The easiest way to create a simple PropertyEditor is to extend the java.beans.PropertyEditorSupport class that implements java.beans.PropertyEditor and defines all of the interface's methods to reasonable defaults. Then I'll just need to override the methods that are necessary for the features I want to provide.
Instead of "true" and "false," it would be nice if the application developer could choose between "12 hour" and "24 hour" as the time format. I can do that with a custom property editor. If I override the getAsText() and setAsText() methods from PropertyEditorSupport, I can tell the IDE that the property can be set and read as a text string. If I also override the getTags() method, I can tell the IDE what the valid string values are for this property. The code for the property editor is shown in Listing 2.
Now the IDE can build a customizer that uses the strings that are in the property editor rather than true and false. But how does the IDE associate the property editor with the property? I'll need a BeanInfo class to make that association. A BeanInfo class provides information about a bean that isn't available through introspection. It can also be used to override the results of introspection. BeanInfo classes must implement the java.beans.BeanInfo interface. As in the case of the PropertyEditor interface, there is a class called java.beans.SimpleBeanInfo that implements all of the BeanInfo methods with reasonable defaults. I'll extend SimpleBeanInfo to define the ClockBeanInfo.
When examining a bean, an IDE looks for the BeanInfo class by appending "BeanInfo" to the name of the bean class and looking for a class by that name. If my bean is named mybeans.Clock, an IDE will look for the class mybeans.ClockBeanInfo that implements the BeanInfo interface. Ordinarily, a BeanInfo class goes in the same package with the bean it describes, but it can go in another package. IDEs will also look for BeanInfo classes in the packages returned by the static method in java.beans.Introspector called getBeanInfoSearchPath().
You can add your BeanInfo package to the search path with setBeanInfoSearchPath(). Thus, if your BeanInfo classes were all in the package mybeans.beaninfos, you could call this to tell an IDE about it:
String [] path = {"mybeans.beaninfos"}; Introspector.setBeanInfoSearchPath(path);
If the IDE finds a BeanInfo for a bean, either in the same package as the bean or in the BeanInfo search path, it will ask the BeanInfo for information about the bean before using introspection.
An IDE uses the getPropertyDescriptors() method to get information about the bean's properties from the BeanInfo class. getPropertyDescriptors() returns an array of java.beans.PropertyDescriptor objects. PropertyDescriptors define how the property should be displayed and edited. PropertyDescriptor and its superclass FeatureDescriptor also follow the beans naming convention for get and set accessors to properties. Table 1 summarizes the properties of a PropertyDescriptor.
The BeanInfo getAdditionalBeanInfo() method is used by the IDE to get properties from the ancestor classes of the bean. It relieves the BeanInfo class of the responsibility for creating PropertyDescriptors for all of the properties of all of the superclasses of the bean. I'll include getAdditionalBeanInfo() here so the inherited Swing properties are also displayed in the automatically generated customizer. The code for the ClockBeanInfo class is in Listing 3.
The BeanInfo class can also be used to associate a set of icons with the bean. IDEs can use the icons in their palette as a graphical representation of the bean. The getIcon() BeanInfo method takes a request from the IDE for a type (color or monochrome) and size (16 or 32 pixels square) of icon and returns a java.awt.Image object if an icon of the requested type is available. Constants representing the different icon types are defined in the BeanInfo interface. As we'll see, different IDEs use different icon types so it's a good idea to include a variety if possible. The getIcon() method is also listed in Listing 3.
Once the BeanInfo and PropertyEditor classes are written, I can load them into the BeanBox and see the results. Notice how the property editor for the twentyFourHourFormat property now uses the information from the PropertyDescriptor in the BeanInfo to make the editor user interface. The name of the property is now listed as "Time Format" rather than "twentyFourHourTime" and the combo box contains "12 Hour" and "24 Hour" rather than true and false. Because the "running" property descriptor hidden property was set to true in ClockBeanInfo.getPropertyDescriptors(), the property doesn't appear in the customizer that the BeanBox generated (see Figure 2)
Customizers
By adding the BeanInfo and PropertyEditor, we've hopefully made the Clock bean a little easier to use in an application. I can take this one step further by asserting more control over the bean configuration process and define a customizer that can replace the default customizer generated by the IDE. By doing this I can show the user only the properties that are important and present the bean state in any way I want.
Like custom property editors, customizers are also associated with their beans through the BeanInfo class. The BeanInfo method getBeanDescriptor() returns a BeanDescriptor that contains the bean's class and the bean's customizer class if it exists. An IDE can use the bean descriptor to find out whether the bean's author has provided a customizer that the IDE can use instead of or in addition to the customizer it generates using BeanInfo and introspection. SimpleBeanInfo.getBeanDescriptor() asserts that there is no customizer, so to assert that there is one, I'll override getBeanDescriptor() in ClockBeanInfo in Listing 3.
Now I'll need to write the customizer. All beans customizers must implement the java.beans.Customizer interface and extend java.awt.Panel. The code for the simple Clock customizer is in Listing 4.
The IDE gives the customizer a reference to the to-be-configured object by calling the set-Object method of the Customizer interface. The customizer can then synchronize its state with the object being customized, register as an event listener, etc.
Then, as the application developer manipulates the customizer GUI, the bean can be updated immediately. This gives the application developer immediate feedback on what the effects are of changing a property.
To view the customizer in the BeanBox, first select the bean to be customized, then select View->Customizer from the menu bar. Figure 3 shows what the customizer looks like in the BeanBox.
Packaging the Bean
Once the bean, BeanInfo, PropertyEditors and customizer have been written, they need to be packaged for loading into an IDE. The easiest way to load the bean and its associated classes and images into an IDE is to put them into a JAR file using a command like this.
jar cvmf manifest.mf mybeans.jar mybeans\*.class mybeans\*.gif
The manifest file is an annotated list of the files that go into the JAR file. A special tag in the manifest file called "Is JavaBean" identifies the classes that are JavaBeans. The manifest file for this example is:
Manifest-Version: 1.0
Name: mybeans/Clock.class
Java-Bean: True
Every IDE loads beans a little differently, but I can go through a couple of examples here. All of the examples so far have used the BeanBox that comes with the Sun Beans Development Kit. Here's one way to load the JAR file into the BeanBox:
- Under the File menu, select LoadJar.
- Use the file dialog to select the JAR file that contains the beans. The BeanBox loads all of the beans in the Jar file into the BeanBox palette.
A Real IDE
Borland's JBuilder uses a little different method but can load the same JAR file. These are the steps to load the Clock bean into the JBuilder 3 palette:
- Select Tools -> Configure Palette.
- Under the "Pages" tab, select the palette page to hold the component. A blank page called "Other" included in the default palette is a good place for new beans.
- Select the "Add From Archive" tab and select the JAR file containing the bean and related classes. JBuilder opens the JAR file, reads the manifest and displays the beans that it found.
- Select the bean class or classes to be installed.
- Select "Install" to load the component.
Let's take a closer look at how JBuilder uses the Clock bean and its associated classes. If I drop the Clock from the palette onto a panel, JBuilder shows the clock in the panel and the Clock is running. JBuilder also creates a property sheet customizer containing the properties that I defined in the BeanInfo getPropertyDescriptors() method and the Swing properties that it found by using getAdditionalBeanInfo(). The Clock bean property sheet is shown in Figure 6.
IDEs have a lot of flexibility in how they use the BeanInfo information. Notice that JBuilder did use the property editor for the twentyFourHourTime property, but used the property name rather than the display name I set in the BeanInfo. It did pick up the short description text from the BeanInfo and used it as the ToolTip help text (see Figure 7). Cool. JBuilder also added two entries to the property sheet that are not bean properties. The "name" is the name that JBuilder will give the Clock variable in the code that it generates. "Constraints" refers to the layout constraints used when the Clock component is added to its container.
The custom ClockCustomizer looks a lot like it did in the BeanBox. It's displayed by right-clicking on the Clock component and selecting Customizer from the popup menu. JBuilder's version of the ClockCustomizer is shown in Figure 8.
JBuilder also has a design view that shows the user interface component tree. In this view JBuilder shows the Clock bean contained within a JPanel (see Figure 9). Here it used the 16x16 color icon that I specified in ClockBeanInfo.
Debugging a Customizer
This example is a simple customizer, but for a complicated customizer it might be necessary to use a debugger to get it working just right. Once the bean and customizer are loaded into an IDE, it can be pretty difficult to find and fix problems. Sometimes even the time-tested method of adding System.out messages won't work because there is no console to print to. I've found that it's worth taking the time to write code that can be used to test customizers outside of an IDE. That way I can use System.out or a debugger to see what the customizer is doing. This simple code loads a bean and its customizer into two windows so they can be tested and debugged.
Clock c = new Clock();
ClockCustomizer cust = new ClockCustomizer();
JFrame f = new JFrame("Bean");
f.getContentPane().add(c);
f.pack();
f.show();
f = new JFrame("Customizer");
f.getContentPane().add(cust);
f.pack();
f.show();
cust.setObject(c);
Figure 10 shows what the test harness looks like.
This simple test harness can make debugging a customizer much easier. Insert this code into the main() method of the customizer for a built-in test capability. With a little additional code this test driver could also be used to test bean serialization and event handling.
Using custom property editors and customizers are great ways to make your beans more usable by application developers regardless of what development tools they use. Take the time to include these features in your beans and your users will thank you for it.
Published December 1, 1999 Reads 10,664
Copyright © 1999 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By William Wright
William Wright is a senior software engineer with GTE Corporation in Arlington, Virginia. He has 10 years' experience with real-time systems development and object-oriented programming.
- Kindle 2 vs Nook
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- Confessions of a Ulitzer Addict
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- It's the Java vs. C++ Shootout Revisited!
- Cloud Computing Can Revitalize Your Career as Software Developer
- IBM Could "Reinvent" Java: Mills
- Oracle & Cloud Computing: Exclusive Q&A with SVP Richard Sarwal
- A Brief History of Cloud Computing
- Kindle 2 vs Nook
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing Expo: Exclusive Q&A with Yahoo! SVP Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Confessions of a Ulitzer Addict
- My Thoughts on Ulitzer
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- What's New in Eclipse?
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- i-Technology Predictions for 2007: Where's It All Headed?









































