This tutorial covers the basic notion of a hierarchical wizard, and how to use these concepts to implement a set of hierarchical wizards. It is assumed that you have a basic understanding of Web Start Wizards, and their purpose. It is also assumed that you have read and understood the Hierarchical specification from the SDK Userguide.
The Web Start Wizard API can be used to create singular wizards that are designed to do a specific task, such as doing disk defragmentation. The user steps through a sequence of screens and performs the task, then exits. This is the standard usage for the wizard model.
Another possibility is that someone wants to perform many tasks, each of which has an associated wizard already designed to perform the task. In this case, rather than having the user run each separate wizard, they can be combined, or aggregated to form a single wizard, with each wizard part of a hierarchy, with one wizard at the top of the hierarchy in a tree-like fasion. This is what is known as hierarchical wizards, where all wizards work together to perform many tasks, while providing a single user interface experience.
A wizard hierarchy allows multiple wizards to work together. For example, one application of wizards is software installation. Generally, an install wizard would be designed to install a single product. It would be nice if multiple products could be installed from the same wizard. A CD-ROM with four products on it could be installed from a single wizard in the root directory of the CD-ROM that connects four single product install wizards into one user experience. The wizard hierarchy preserves the user's ability to use the single product install wizards, as well as provide a multi-product installation solution described above.
Every wizard has a sequence of components, some of which are actual graphical panels, others of which are groups, or containers of panels, called Nodes that it steps through to provide the user experience. A set of components can grouped by adding them all to a Node, which is a container for components. This subtree of components will then be visted in left-to-right order. In an upper-level wizard, custom nodes can be created and placed into the tree that do more than just group components together. One special type that we have provided in the Web Start Wizards SDK is the HierarchyNode.
The HierarchyNode is a special type of grouper (container). The HierarchyNode does dynamic, or run-time, grouping of components. It collects it's components at runtime from other wizards that exist in the same VM. When the user clicks next and advances onto one of these HierarchyNodes, the node goes off and retrieves the components that it should contain at that time, then automatically adds the group of components to itself, causing the user to proceed automatically onto the sequence of components that the HierarchyNode gathered.
These HierarchyNodes are used in a top-level wizard, or "connector" wizard as they are sometimes called, to group similar sets of components from lower-level wizards, and to display all of the similar groups of components at the same time. They will, at runtime, collect groups of components from other wizards and add them into a single set of panels, grouped by a HierarchyNode.
How does a HierarchyNode know which components to aggregate?
First and foremost, HierarchyNodes only exist in connector wizards. The lowest-level wizards have no knowledge of what might be in the upper-level wizards.
In a lower-level wizard, the only components that are candidates for later aggregation into an upper-level wizard are those components that exist directly below the root node in the client panel tree. When building the lower-level wizards, care must be taken to give a name to the WizardComponents that might be later aggregated into a HierarchyNode in a top-level wizard (using the
setName()andgetName()API, or the API that might exist in the constructor).Then, when building an upper-level, or connector wizard, care must be taken to name the HierarchyNode with the same exact name as the components that this HierarchyNode is supposed to aggregate. This is how the HierarchyNode knows which components to gather. These topics are discussed below.
3. Step-by-Step Install Wizards Example
Suppose you have many products, each of which you want to be able to install separately. You can create separate wizards for each of your products and ship them separately. You can also create a top-level wizard, and combine all of your product-level wizards into a hierarchy, and have them all install in a single user experience. This is the goal of the hierarchical wizards paradigm.An example of this is Sun Microsystems' Solaris Easy Access Server product. This product contains several other products, each of which might be installed separately. In addition, you can install the entire product by using a single wizard, which pulls pieces of each lower-level, or product-level, wizard, into a single wizard. The pieces pulled are the pieces that are responsible for customizing the install, such as install location, which components to install, etc.
Creating the lower-level install wizards
Creating the lower-level install wizards entails using the
InstallArchiveWritersuperclass to create your wizard. This class has pre-defined containers into which you can put custom components (such as panels). The pre-defined places are:
POST_WELCOME: Components placed into this container appear immediately after the user presses next on the WelcomePanel.PRE_VERIFY: Components placed into this container appear immediately before the user sees theVerifyPanel, which is shown just before Installation. PREINSTALL: Components placed into this container appear immediately after the user presses next on the VerifyPanel. This is a good place to put pre-install components that might pre-configure the product to install, or to ask a last-minute question of the user.POSTINSTALL: Components placed into this container appear immediately after the product is done installing. This is a good place for post-install configuration screens which might require the user to answer some questions before continuing.These containers are represented in any upper-level wizards as well, by using a HierarchyNode to visit each container from each subwizard, in the order the wizards were added with
addWizard().
An example of a product-level installer
Here is an example of a wizard builder that will build wizards that will later be used in a top-level wizard. You can cut and paste this code into your favorite editor, compile it, and run it.
This source code can also be found here.
It will create a wizardthat installs a sample product:
import com.sun.install.products.*; import com.sun.wizards.core.Msg; import java.util.*; import java.io.*; public class LowerLevelHierarchyBuilder extends InstallArchiveWriter { private String productName = null; private String componentName = null; private String componentPath = null; public static String usage = "Usage: LowerLevelHierarchyBuilder "+ "{product name} {component name} "+ "{component path}"; public LowerLevelHierarchyBuilder(String productName, String componentName, String componentPath) { this.productName = productName; this.componentName = componentName; this.componentPath = componentPath; } private void configureWizard() { if ((this.productName == null) || (this.componentPath == null) || (this.componentName == null)) { System.out.println(usage); System.exit(-1); } super.createClientTree(); setProductName(productName); setArchiveName(productName); /* * Create Unit to hold files to install */ FileUnit files = new FileUnit("Files for "+componentName); File file = new File(componentPath); if ((file == null) || (!file.exists())) { System.out.println(usage); System.exit(-1); } String filename = file.getName(); String parent = file.getParent(); /* * Add the files to the Unit */ files.addFile(parent, filename, null); /* * Add the Unit's collection to the archive */ addCollection(files.getCollection()); /* * Create the selectable component for the user to click on */ SoftwareComponent program = new SoftwareComponent( new Msg(componentName)); /* * Add the files to the selectable component */ program.addComponent(files); /* * Add the selectable component to the overall install */ addComponent(program); } public static void main(String[] args) { if (args.length != 3) { System.out.println(usage); System.exit(-1); } else { LowerLevelHierarchyBuilder s = new LowerLevelHierarchyBuilder(args[0], args[1], args[2]); /* * Configure my wizard */ s.configureWizard(); /* * Write out the archive and exit with a 0 code, indicating * success */ s.writeArchive(); System.exit(0); } } }
To compile and run this wizard, save the file asLowerLevelHierarchyBuilder.javaand issue the following commands, making sure the Wizard SDK classes are in yourCLASSPATH: (note that the "$" is a shell prompt, and should not be typed)$ javac LowerLevelHierarchyBuilder.java $ java LowerLevelHierarchyBuilder MyProduct1 MyProductComponent1 /home/jhf/tmp/files $ java LowerLevelHierarchyBuilder MyProduct2 MyProductComponent2 /home/jhf/tmp/filesYou should substitute your own names and paths to some sample files that exist on your system. This will create two files in the same directory calledMyProduct1.classandMyProduct2.classthat you can then run by typing:$ java MyProduct1or$ java MyProduct2This will cause the wizard(s) to run, and you can install the files if you so desire.The next section shows you how to include the wizards you created into a hierarchical set of wizards.
Creating the upper-level "connector" wizard
To create an upper-level wizard, you simply declare that theInstallArchiveWritersuperclass make an upper-level, or connector wizard rather than a lower-level wizard. You do this by using thesetConnector()API ofInstallArchiveWriter. In addition, you must declare which wizards the connector wizard will look for and read from at install-time. You do this by using theaddWizard()API ofInstallArchiveWriter.
An example of an upper-level "connector" installer
An example wizard builder is shown below, which takes the names of the child wizards on the command line, and builds and upper-level wizard that uses these lower-level wizards at runtime.This source code can also be found here.
import com.sun.install.products.*; import com.sun.wizards.core.Msg; import java.util.*; import java.io.*; public class UpperLevelHierarchyBuilder extends InstallArchiveWriter { /* * How to use this class */ public static String usage = "Usage: UpperLevelHierarchyBuilder "+ "{wizard1, wizard2, ..., wizardN}"; /* * Names of wizards we will control */ private String[] wizards = null; public UpperLevelHierarchyBuilder(String[] wizards) { this.wizards = wizards; } private void configureWizard() { /* * Simple error checking */ if ((this.wizards == null) || (wizards.length <= 0)) { System.out.println(usage); System.exit(-1); } else { /* * Set name of resulting .class file */ setArchiveName("connector"); /* * Tell superclass to write connector wizard archive */ setConnector(true); /* * Add each wizard specified on command line to upper-level wizard */ for (int index=0; index < wizards.length; index++) { String wizardToAdd = wizards[index]; System.out.println("Adding wizard: "+wizardToAdd); addWizard(wizardToAdd, DEFAULT_INSTALL); } } } public static void main(String[] args) { UpperLevelHierarchyBuilder s = new UpperLevelHierarchyBuilder(args); /* * Configure my wizard */ s.configureWizard(); /* * Write out the archive and exit with a 0 code, indicating * success */ s.writeArchive(); System.exit(0); } }
Creating the upper-level "connector" wizard
To compile and use this class, run the following commands (assuming you named your previous archivesMyProduct1.classandMyProduct2.class):% javac UpperLevelHierarchyBuilder.java % java UpperLevelHierarchyBuilder MyProduct1.class MyProduct2.classThis will create a connector wizard calledconnector.class. You can run this wizard by typing:% java connectorThis will cause the wizard to run, and you can install each of the subwizard's products by stepping through the customization for each product, by selecting "Custom Install" on theProductPanel.4. Conclusion
This tutorial has shown how to use the Install API to create wizards that can later be aggregated by upper-level wizards, using the HierarchyNode concept. Generic non-install Wizards can also be created, and, using a HierarchyNode, components can be referenced from upper-level wizards and be stepped through.The wizard hierarchy is not limited to one or two levels. Any number of upper-level wizards can be created. In this example, we have shown a product level install wizard and an upper level install wizard put together in a wizard hierarchy. A level above that upper level could be created to allow one installer to install products from multiple connector installers.
This concludes this Hierarchy Tutorial. For more information on creating wizards for Solaris, please visit our website at www.sun.com/solaris/webstart/wizards/.