Bean Extender contains several utilities. These utilities help JavaBean programmers and assemblers. The utilities are:
Java programmers and assemblers can dip their dippable bean instance using the dip tool. The dip tool interacts with JavaBeans visually in a graphical user interface. This tool can be invoked from either the command line or a builder tool such as the Assembly Surface.
The Dipping Interface relies on a programming interface of the Dipping Framework for dipping a dippable bean with a dip bean. When the Dipping Interface is invoked, it presents a dialog with:
You can use this panel to remove an applied dip.
When the dip tool is invoked from the command line, it presents a dialog with the following additional information:
The following graphic is the graphical interface presented by the dip tool.
You can invoke the Dipping interface utility by typing the following command:
java com.ibm.beans.tools.dip.dip
The previous invocation is shortened to:
java dip
The default to a basic invocation reads the current directory and loads all the JAR files. However, you can:
The command line syntax for invoking the Dipping Interface, including input bean and output repository selections and operation mode is as follows:
java dip [-?] [-dir dir] [-withDir dir] [-toDir] [-batch] [-jar jarFile] [-withJar jarFile] [-toJar] [-jarDir dir] [-withJarDir dir] [-toOriginal] [-class moniker classFile] [-withClass classFile] [-classInJar moniker jarFile] [-withClassInJar moniker jarFile] [-ser moniker serFile] [-withSer moniker serFile] [-serInJar moniker jarFile] [-withSerInJar moniker jarFile]
For example:
java dip -classInJar sunw/demo/jelly/Jelly \bdk\bars\jelly.jar -withClassInJar com/ibm/beans/samples/dips/print/PrintDip \dip\jars\printdip.jar -toOriginal
For additional information on this command line syntax, see the following sections.
You can pre-specify input bean selections by using one of the following optional arguments:
java dip -dir dir -jar jarFile -jarDir dir -class moniker classFile -classInJar moniker jarFile -ser moniker serFile -serInJar moniker jarFile
You can pre-specify dip Bean selections. To pre-specify the dip Bean selections, use one of the following optional arguments:
java dip -withDir dir -withJar jarFile -withJarDir dir -withClass classFile -withClassInJar moniker jarFile -withSer moniker serFile -withSerInJar moniker jarFile
You can pre-specify output repository selections for the generated .class files. To pre-specify the output repository selections, use one of the following optional arguments:
java dip -toDir dir -toJar jarFile -toOriginal
You can optionally specify the Dipping Interface to perform operations in batch mode. The default is interactive.
java dip -batch
If you specify batch, the interface is displayed, but it does not pause even if it encounters an error.
Dipping processing occurs when you click on the Add Dip button or the Remove Dip button. Each of these processes is explained in the following sections.
On the Select Dip panel, click on the beans to dip and then click on the Add Dip button. For each dippable bean selected in the Select Bean(s) panel, the following processing occurs:
When you have selected the beans to remove from the Dips Applied panel, click on the Remove Dip button. However, not all dips can be removed, for example, the SecurityDip.
Any Java builder application can invoke the Dipping Interface by constructing a new DipDialog object. The DipDialog class constructor takes a dippable instance as an argument. This same instance is the output of the dipping processes. In this case, the Select Bean(s) panel and the Select Location panel are not necessary.
For additional information on class DipDialog, see the Bean Extender API Reference.
The JarJava class define the utility that easily adds repositories to the CLASSPATH environment statement and prevents a ClassNotFound exception from being thrown. In many cases, the packaging and deployment functions of the Bean Extender technology handle beans directly from the JAR, zip, and other repositories. You generally do not have to install these repositories on your CLASSPATH environment statement. However, if the repository has a dependency on another repository, which is unknown to Bean Extender, a ClassNotFound exception can be thrown. Use the JarJava utility to resolve this problem.
The JarJava utility invokes the Java interpreter to execute the specified Java class name. The JarJava invocation takes the same arguments as the java command, but it handles the setting of the CLASSPATH environment statement. To use any JAR file or zip file in the directory, add that directory path to the CLASSPATH statement. The JarJava utility scans all directories in the CLASSPATH statement and automatically adds any JAR or zip file found in those directories to the CLASSPATH statement used by the Java interpreter.
The syntax for the JarJava application is:
java com.ibm.beans.tools.JarJava classnameThese are the same arguments accepted on a java command.
For example, if the initial value of the CLASSPATH environment variable is:
u/classes:/u/lib/classes.zip:/u/jarsand the directory /u/jars contains a file named mybeans.jar; when the JarJava application is invoked with the command:
java com.ibm.beans.tools.JarJava MyJavaApp arg1 arg2 ... argNthe JarJava utility starts the child process with the command:
java -classpath /u/classes:/u/lib/classes.zip:/u/jars:/u/jars/mybeans.jar MyJavaApp
The functions of the LogStream class prefaces each message with a header that includes the date, the name of the log, and the name of the thread:
date:log name:thread name:log messageWhen a new LogStream object is created, it defaults to sending messages to System.err. Subsequently, the LogStream can be reset to use any OutputStream. The LogStream can write to System.err, a FileOutputStream, or the OutputStream of the socket.
There are two approaches to writing to logs:
However, there are problems with the java.rmi.server.LogStream class that are solved by using com.ibm.beans.util.LogStream class. These problems are that thejava.rmi.server.LogStream class:
The APIs and logic of com.ibm.beans.util.LogStream mirror those of java.rmi.server.LogStream but with the following changes:
New LogStreams can be created as children of the LogStream.
The default is to flush or close the current OutputStream. However, you can change or query the OutputStream using property change methods.
The default is to not write an identifier. However, you can change or query the identifier using property change methods.
SILENT specifies that nothing is written, and the messages are sent to the bit bucket. BRIEF specifies that the prefix is not added to the message. VERBOSE specifies that prefix and message are printed to the OutputStream. VERBOSE is the default.
import com.ibm.beans.util.LogStream; import com.ibm.beans.util.MessageFormatter; ... MessageFormatter mf = new MessageFormatter ("com.ibm.beans.myPkg.myBndl"); LogStream errorLog = LogStream.log ("MyErrorLog"); LogStream debugLog = LogStream.log ("MyDebugLog"); // messages in the debug log do not have to be NLS enabled. debugLog.println ("First debug line"); // but here is one that is NLS enabled. debugLog.println (mf, "bundleMsgHandle1"); // all error messages need to be NLS enabled. errorLog.println (mf, "bundleMsgHandle2", arg1, arg2); try { ... } catch (Exception ex) { errorLog.println(mf, "bundleMsgHandle3"); // you can print the stack to the error log ex.printStackTrace(errorLog); } ...
Bean Extender uses three implementations of LogStream: an output log, an error log, and a debug log. The output log is logically System.out. The error log is logically System.err. The debug log is also logically System.out, but is separate from the output log so that it can be turned off. The output log and error log should always be output, but the debug log is only turned on to debug a problem. When you use the Configuration tool, the Logging tab can be used to adjust the settings of the Bean Extender logs.
Each of the three logs allow the following configuration changes:
For additional information on how the LogStream names are used, see the description of the com.ibm.beans.util.LogStream.log() method in the Bean Extender API Reference.
For additional information on class LogStream constants, see the Bean Extender API Reference.
Also, a button is provided to allow the user to reset to installed settings. When this button is pressed, the three logs configuration values are reset to their initial installed settings. This button only effects the log configuration. It does not effect any thing else configured by the Configure tool.
The MergedCustomizer implements a java.beans.Customizer and provides a way for a bean to nest multiple customizers in a single customizer. This single customizer is a notebook. The customizer is used by the Dipping Framework to merge dip customizers with the customizer of the original bean. The original customizer is placed on the first page of the notebook.
The following graphic is an example of a displayed merged customizer:
The MergedCustomizer is a customizer that implements the java.beans.Customizer interface. The Customizer interface is comprised of the following methods:
When the setObject() method is invoked on the MergedCustomizer:
The MergedCustomizer enforces all objects passed to the setObject() method to be MergeCustomizable objects. The MergeCustomizable interface is comprised of the following methods:
Bean Extender provides Java programmers and assemblers the following ways to morph classes:
The BeanMorpher is a command line tool that is described completely in "Creating a Dippable Bean" and the Bean Extender API Reference.
The Morphing Interface is a GUI than can be invoked from the command line or from a builder tool such as the Assembly Surface. The Morphing Interface relies on the BeanMorpher of the Dipping Framework.
You can invoke the Morphing Interface by typing the following command:
java com.ibm.beans.tools.dip.morph
The previous invocation is shortened to:
java morph
The defaults to a basic invocation are:
However, you can:
The command line syntax for invoking the Morphing Interface, including input source and output repository selections and operation mode is as follows:
Usage: java com.ibm.beans.tools.dip.morph -options -? Print this help message. -jarDir <dir> Load a directory of jar files. -jar <jarFile> Load a jarFile. -repos <dir> Load a repository directory. A repository directory is a directory containing an un-jared jar file including it's manifest file. -class <moniker classFile> Load a specific class file, giving it the specified moniker. -classInJar <moniker jarFile> Load a specific class from a jar file, giving it the specified moniker. -toOriginal Save the morphed class with the original class. -toJar <jarFile> Save the morphed class in the specified jar file. -toRepos <dir> Save the morphed class in the specified repository directory. -hideOriginal The original class should not be saved with the newly morphed class. -modeless The view of the morphing UI should not be modal.
For example:
java morph -classInJar sunw/demo/jelly/Jelly \bdk\jars\jelly.jar -toOriginal
For additional information on this command line syntax, see the following sections.
You can pre-specify the input source selections by using one of the following optional arguments. These arguments are mutually exclusive, only one is allowed.
java morph -repos dir -jar jarFile -jarDir dir -class moniker classFile -classInJar moniker jarFile
You can pre-specify output repository selections for the generated .class files. To pre-specify the output repository selections, use one of the following optional arguments. These arguments are mutually exclusive, only one is allowed.
java morph -toRepos dir -toJar jarfile -toOriginal
When you invoke the Morphing Interface, it presents a dialog with three tabs:
The Main tab in the Morphing Interface presents the essentials of what is needed to morph a class.
The following graphic depicts the Main tab in the Morphing Interface.
If you select inheritance, the new dippable class is created as a child of the original class. The new dippable class is a "kind of" the original class. Anywhere the original class could be used, the new dippable class can be used.
If you select aggregation, the new dippable class is created implementing an interface, so you must also select an interface. You must first choose a class to be used to implement the function of the new dippable class, then you must choose an interface to the dippable class that is to be implemented. The new dippable class is a "kind of" the interface, and the original class is a "part of" the new dippable class that is used to implement the interface.
If the new dippable class is implemented using aggregation, the interface selected does not have to be the only interface of the class selected. If you would like to have the new dippable class implement more than one interface of the class, the additional interfaces can be entered using the Additional API option under the Advanced Customization tab.
Choosing inheritance or aggregation depends on what you want the new dippable bean to be. If you want it to be a dippable version of an existing class, you should use inheritance. If you want it to be a dippable version of an existing interface, you should use aggregation. Of course, restrictions on inheritance may force you to use aggregation. For example, if the original class is a final class then no child can be created, so you would consider aggregation. Similarly, restrictions on aggregation may force you to use inheritance. For example, if the interface you want to implement doesn't introduce the methods necessary to implement the interface, like java.lang.Cloneable or java.io.Serializable, you would consider inheritance.
If you choose to store these files in a new JAR file or in a new repository directory, you must supply the name or locations in the corresponding text field.
A repository directory is a directory that contains an unjared JAR file, complete with a manifest.
The General Customization tab in the Morphing Interface present the general options for customization of the new dippable class.
The following graphic depicts the General Customization tab in the Morphing Interface.
If your are morphing an ordinary class, not a bean, you will not want a corresponding BeanInfo class created.
If you do not provide a new name, then the new dippable class will be in the same package as the original class. The name for the new dippable class will be a combination of the original interface name (if you chose aggregation), the original class name, and the string "Dippable".
Having dips applied to the class definition of a dippable bean does not prevent additional dips from being applied to a single instance of the dippable class. Additional dips can still be applied to an instance of the new dippable class, but the class based dips listed in this option during morphing will be applied to all instantiations of the new dippable class.
The Advanced Customization tab in the Morphing Interface presents the advanced options for customization of the dippable class. These options assume a higher level of experience with the morphing process, and should not be used by beginner morphers.
The following graphic depicts the Advanced Customization tab in the Morphing Interface.
This option is only valid if the new dippable class was created using inheritance. It is ignored if the new dippable class is created through aggregation.
Using this option could mean dips applied subsequently to an instance of the dippable class will reject being applied because a method needed by the dip is not available in the dippable class.
This option remains disabled until one or more dips have been chosen in the Dip(s) applied option under the General Customization tab.
Using this option could mean dips applied subsequently to an instance of the dippable class will reject being applied because a method needed by the dip is not available in the dippable class.
Choose this option with care. If the new dippable class is not a final class, then a child could be created of a dippable class which could subvert the behavior of a dip applied to the dippable class.
Choose this option with care. Adding additional API could subvert the desired behavior of the original class. If the original class implemented an interface like Cloneable, it's possible the dippable class with the additional APIs no longer correctly implements Cloneable. After all, the dippable class just uses the original implementation for the clone() method.
Also, it is possible the adding of additional APIs to the dippable class may mean the new dippable class isn't thread-safe or isn't immutable, when the original class was thread-safe or immutable.
Once you have made all your choices, click on the Morph Class button. This causes the following process to occur:
The morphing UI is designed using the model-view-controller approach:
As seen in the diagram, the MorphController interfaces with the model and the view through the interface methods defined in the Model, MorphModelManipulator, and MorphView interfaces. The Model sends information to the MorphController class by firing a ModelEvent. The view sends information to the MorphController class by firing a MorphViewEvent. The model and the view have no direct connection. All the logic of what needs to be done is handled by the MorphController.
The MorphController is instantiated using one or two Model+MorphModelManipulator pairings and a single view. One Model+MorphModelManipulator pair represents the collection of classes the MorphController considers for morphing. The second (optional) Model+MorphModelManipulator pair represents the collection of dips the MorphController considers for pre-dipping. If the second Model+MorphModelManipulator pair is not provided, an empty model is created for the dips, and optionally filled with the contents of the first Model+MorphModelManipulator pair. Only one graphical user interface is given to the user to make the decisions necessary for the class to morph.
Anyone wishing to put a different view on the morphing UI only needs to come up with a new implementation of the MorphView interface. Anyone wishing to use a different Model of the classes or dips only needs to come up with a new implementation of the Model interface and it's corresponding MorphModelManipulator interface.
Any Java builder application can invoke the Morphing Interface by constructing a new MorphController instance by providing Model and MorphModelManipulator instances for the list of classes and the list of dips, and by also providing a MorphView instance to display to the user.
Frame myFrame = new Frame("command line tag"); // Create the empty model BeanBagModel myClassesBeanBagModel = new BeanBagModel(); // Create the view MorphView myView = new MorphDialog(myFrame, true); // Create the controller MorphController myController = new MorphController(myView, new MorphBeanBagModelManipulator( myClassesBeanBagModel ) ); // Set any options in the Controller or the View different from the defaults // have the MorphController run a System.exit() when it's through; myController.setDoRunExit(true); // Add some data the model myClassesBeanBagModel.loadFromRepository(null,jelly.jar); // Show the view myView.setDoDisplay(true);
For additional information on MorphDialog objects, see the Bean Extender API Reference.
public static boolean checkCompatibility (String oldClassFile, String newClassFile, String className)