C H A P T E R  5

Developing Command Components

This section assumes that you have already read Develop Your First Component found in Chapter 2, Developing Components.


Developing an Extensible Command Component

This section describes how to create a new Command component that adds value on top of the ValidatingDisplayField you created in the Develop a Non-Extensible View Component section found in Chapter 3, Developing View Components.

This is called the ValidatingCommand component, and it encapsulates some reusable logic related to the processing of pages which contain instances of the ValidatingDisplayField.

This exercise is intended to focus on the mechanics of extensible Command component design and, as such, only scratches the surface of extensible Command possibilities.

The mechanics of creating extensible Commands are very straightforward. If you have completed the previous sections, you know the mechanics by now. The reality is that Commands are structurally simple. This section will reinforce your familiarity with the command design pattern, introduce you to its role in the Sun ONE Application Framework, and hopefully encourage you to become creative in leveraging this simple, but powerful pattern.

This example introduces several additional Sun ONE Application Framework component model topics:

Your validating Command component should support the following design-time functionality:

Specifically, subclass developers will focus on implementing two component specific methods handleInvalid and handleValid, instead of the conventional Command execute method. The application command developer will rely on the component base class to determine if the page on which the component has been activated is valid or invalid, and invoke the appropriate handler. Therefore, application command developers can focus on responding to the valid or invalid state, and not need to worry about detecting the state.

Your validating Command component should support the following run-time functionality:

The ValidatingCommand base class implementation of the execute method will perform a deep search on the submitted ViewBean, detect any and all instances of ValidatingTextField, and check to see if any of those fields are invalid.

The choice to implement this Command component as outlined above is purely a matter of style. As pointed out the command pattern is elementary. Therefore, personal OO style will factor largely into component authors designs.

To meet these requirements, you will design and implement the following classes:

Additionally, you will implement a custom Java template which the IDE toolset will use as the basis for application specific sub-types of your ValidatingCommand.

Finally, you will edit the mycomponents complib.xml to add the new component to the Sun ONE Application Framework component library.

This example assumes the co-existence of the mycomponents.ValidatingDisplayField. Before continuing, complete the Develop a Non-Extensible View Component section in Chapter 3, Developing View Components, if you have not already done so.

Create the Sun ONE Application Framework Component Class

1. In any Java editor, create the class mycomponents.ValidatingCommand.

2. Make ValidatingCommand extend com.iplanet.jato.comand.BasicCommand.

3. Implement the appropriate constructor for the component type.

All Command components must implement a no-arg constructor.

4. Implement the remaining methods that are required to fulfill your component specific requirements.

After these steps, mycomponents/ValidatingCommand.java should look as follows:

package mycomponents;

import java.util.*;

import com.iplanet.jato.*;

import com.iplanet.jato.command.*;

import com.iplanet.jato.model.*;

import com.iplanet.jato.view.*;

import com.iplanet.jato.view.event.*;

 

 

public abstract class ValidatingCommand extends Object implements Command {

 

 

public ValidatingCommand() {

super();

}

 

 

public void execute(CommandEvent event) throws CommandException {

Map map=event.getParameters();

 

try {

boolean isValid = true;

ViewBean viewBean = ViewBase.getRootView((View)event.getSource());

List vFields = getValidatingTextChildren(viewBean);

Iterator iter = vFields.iterator();

while(iter.hasNext()) {

ValidatingDisplayField vText = (ValidatingDisplayField)iter.next();

if(! vText.isValid()) {

isValid = false;

break;

}

}

 

if( isValid ) {

handleValid(event);

}

else {

handleInvalid(event, viewBean);

}

}

catch (Exception e) {

if (e instanceof CommandException)

throw (CommandException)e;

else {

throw new CommandException(

"Error executing ValidatingCommand",e);

}

}

}

 

public List getValidatingTextChildren(ContainerView container) {

List result=new LinkedList();

 

String[] childNames=container.getChildNames();

for (int i=0; i<childNames.length; i++) {

Class childType=container.getChildType(childNames[i]);

if (ValidatingDisplayField.class.isAssignableFrom(childType)) {

ValidatingDisplayField child=(ValidatingDisplayField)

container.getChild(childNames[i]);

result.add(child);

}

else if (ContainerView.class.isAssignableFrom(childType)) {

ContainerView child=

(ContainerView) container.getChild(childNames[i]);

result.addAll(getValidatingTextChildren(child));

}

}

return result;

}

 

 

public abstract void handleValid(CommandEvent event) throws CommandException;

 

 

public void handleInvalid(CommandEvent event, ViewBean invalidVB)

throws CommandException {

// default implementation is to just redisplay the invalid page

invalidVB.forwardTo(event.getRequestContext());

}

 

}


Create the Extensible Component's Java Template

Extensible components serve as base classes for application defined entities. Therefore, the Sun ONE Application Framework component model provides extensible component authors the opportunity to provide a custom Java template.

The IDE toolset will, subsequently, use the component supplied template to create the application specific sub-type. Component authors can utilize the custom template to enhance the application developer's experience. Component authors may prepare the component specific Java template with a set of template tokens defined in com.iplanet.jato.component.ExtensibleComponentInfo. For token details, see ExtensibleComponent API.

Component authors can also utilize any arbitrary Java constructs within the Java template (for example, import statements, methods, variables, interface declarations, and so on). Minimally, the custom template will ensure that the new Java class extends from the extensible component class.

In this example you will use the template to assist the developer in the implementation of methods which are declared abstract in the base class.

In any text editor, create the template mycomponents.resources.ValidatingCommand_java.template.

The template contents should look as follows:



Note - The tokens follow a __TOKEN__ pattern.



package __PACKAGE__;

 

import com.iplanet.jato.*;

import com.iplanet.jato.command.*;

import com.iplanet.jato.model.*;

import com.iplanet.jato.view.*;

import com.iplanet.jato.view.event.*;

import mycomponents.*;

 

/**

*

*

*/

public class __CLASS_NAME__ extends ValidatingCommand

{

/**

* Default constructor

*

*/

public __CLASS_NAME__()

{

super();

}

 

/**

*

*

*/

public void handleValid(CommandEvent event) throws CommandException

{

// TODO - Developers must implement this method.

}

 

}

 


Create the ComponentInfo Class

The ComponentInfo class defines the design-time metadata that the IDE toolset requires to incorporate the component. In this example, you will extend an existing ComponentInfo, and in true OO style, simply augment it. You could, of course, choose to implement the ComponentInfo interface from scratch, but that would be unproductive in this case.

In this example, you are not going beyond the functionality revealed in the earlier component examples.

1. Create the class mycomponents.ValidatingCommandComponentInfo.

2. Make ValidatingCommandComponentInfo extend com.iplanet.jato.command.BasicCommandComponentInfo.

3. Implement the no-arg constructor.

There is no need to Implement the getComponentDescriptor() method since you do not need to define any new properties.

4. Implement the getPrimaryTemplateAsStream() method to return a Java template file which you want the IDE toolset to use as the starting point for new classes derived from this extensible component.

After these steps, mycomponents/ValidatingCommandComponentInfo.java should look as follows:



Note - In this sample code, String values have been embedded directly for ease of demonstration. Utilize resource bundles if you anticipate the need to localize your display strings.



package mycomponents;

import java.util.*;

import java.awt.Image;

import java.io.*;

import com.iplanet.jato.component.*;

import com.iplanet.jato.command.*;

 

 

public class ValidatingCommandComponentInfo extends BasicCommandComponentInfo {

 

 

public ValidatingCommandComponentInfo()

{

super();

}

 

 

public ComponentDescriptor getComponentDescriptor()

{

// identify the component class

ComponentDescriptor result=new ComponentDescriptor(

"mycomponents.ValidatingCommand");

 

// The name will be used to determine a name for the component instance

result.setName("ValidatingCommand");

 

// The display name will be used to show the component in a chooser

result.setDisplayName("Validating Command");

 

// The description will be the tool tip text for the component

result.setShortDescription("A validating command component");

 

return result;

}

 

 

public String getPrimaryTemplateEncoding()

{

/* Production version would be resource bundle driven, like this:

return getResourceString(getClass(),

"PROP_ValidatingCommand_SOURCE_TEMPLATE_ENCODING", "ascii");

*/

 

return "ascii"; // NOI18N

}

 

 

public InputStream getPrimaryTemplateAsStream()

{

/* Production version would be resource bundle driven, like this:

 

return ValidatingCommandComponentInfo.class.getClassLoader().

getResourceAsStream(

getResourceString(getClass(),

"RES_ValidatingCommandComponentInfo_SOURCE_TEMPLATE",""));

*/

 

return mycomponents.ValidatingCommandComponentInfo.class.getResourceAsStream(

"/mycomponents/resources/ValidatingCommand_java.template"); // NOI18N

}

 

}


Augment the Component Library Manifest

The component manifest has already been created in the earlier example. Now you will add additional information.

Note that you will add additional types of information not seen in the prior example.

The Sun ONE Application Framework library manifest must be named complib.xml. Within the JAR file, the Sun ONE Application Framework library manifest must be placed in the /COMP-INF directory.

1. Create/Open the file COMP-INF/complib.xml.

2. Add an extensible component element to declare the ValidatingCommand component.

After these steps, the COMP-INF/complib.xml file should look as follows:



Note - For clarity, only the significant delta to the prior version of this file shown earlier is shown here.



<?xml version="1.0" encoding="UTF-8"?>

<component-library>

<tool-info>

<tool-version>2.1.0</tool-version>

</tool-info>

<library-name>mycomponents</library-name>

<display-name>My First Component Library</display-name>

 

...

 

<extensible-component>

<component-class>mycomponents.ValidatingCommand</component-class>

<component-info-class>mycomponents.ValidatingCommandComponentInfo</component-info-class>

</extensible-component>

 

...

</component-library>


Recreate the Component Library JAR File

JAR up the component classes as you did in the first example, so they can be ready for distribution as a library.

1. The name of the JAR file is arbitrary.

In this case, name it "mycomponents.jar".

2. You can omit the Java source files from the JAR.

3. You should include in the JAR any necessary ancillary resources, like icon images, or resource bundles.

In this case there are none.

In this case you are now including several new classes and a Java template file.

4. The mycomponents.jar internal structure should look as follows:

mycomponents/resources/SecureViewBean_java.template

mycomponents/resources/ValidatingCommand_java.template

mycomponents/resources/XMLDocumentModel_java.template

mycomponents/MissingTokensEvent.class

mycomponents/MyTextField.class

mycomponents/MyTextFieldComponentInfo.class

mycomponents/SecureViewBean.class

mycomponents/SecureViewBeanComponentInfo.class

mycomponents/TypeValidator.class

mycomponents/ValidatingCommand.class

mycomponents/ValidatingCommandComponentInfo.class

mycomponents/ValidatingDisplayField.class

mycomponents/ValidatingTextFieldComponentInfo.class

mycomponents/ValidatingTextFieldTag.class

mycomponents/Validator.class

mycomponents/XMLDocumentModel.class

mycomponents/XMLDocumentModelComponentInfo.class

mycomponents/XMLDocumentModelFieldDescriptor.class

mycomponents/mycomplib.tld

COMP-INF/complib.xml


Test the New Component

Additional Sun ONE Application Framework IDE toolset features introduced in this section:

1. Deploy the new version of the library into your previously created test application

Important Sun ONE Studio note: The Sun ONE Studio will not let you delete or copy over a JAR file that is currently mounted.

You should shut down the Sun ONE Studio whenever you need to replace one of the JAR files that is currently mounted. If you are trying to test the new version of component library in a project that is already open inside the Sun ONE Studio, you should first shut down the Sun ONE Studio.

Once the Sun ONE Studio has released its hold on the old copy of the library JAR file, you can copy the new version of the JAR file over the old version.

After successfully deploying the new version of the library, you can reopen the application in the Sun ONE Studio.

2. Create a new Command object.



Note - If you have not done this before, complete the Sun ONE Application Framework Tutorial).



The "New Command" wizard should now look as follows:

This figure shows the New Command wizard. 

3. Select "Validating Command" from the component list, and complete the wizard.

4. Take the default settings and let the wizard create ValidatingCommand1 for you.

After the wizard completes, you can see that the IDE toolset has created a new class based on the component supplied template.

You application should now contain a ValidatingCommand1 object.

This figure shows the ValidatingCommand1 object in Explorer. 

The remaining steps assume that your test application already contains two pages that you can leverage in testing your new ValidatingCommand.

The two pages you will need are as follows:

You can see those ViewBeans in the test application explorer graphic just above.

First, you need to complete the coding of ValidatingCommand1.

As designed, the superclass will handle validation state detection, while the application specific class (for example, ValidatingCommand1) is responsible for determining what to do when the submitted page is valid.

This is a very application specific determination. In the interest of simply testing the component, you will code its handleValid method to just display SecurePage1.

a. Open the java source for ValidatingCommand1.

b. Implement its handleValid method.

public void handleValid(CommandEvent event) throws CommandException

{

ViewBean next = event.getRequestContext().getViewBeanManager().getViewBean(

SecurePage1.class);

next.forwardTo(event.getRequestContext());

}


To test a ValidatingCommand component, in particular, you need a ViewBean that contains some ValidatingDisplayFields.

You created just such a ViewBean earlier, Page1

5. Select the Page1 node.

To test any Command component, you need to set up a uses relationship between a command client (for example, a CommandField like a Button or HREF) and your command object.

At run-time, the CommandField will use (activate) the command object. At design-time one establishes this uses relationship by configuring a CommandDescriptor (to declaratively describe a Command instance) and associating this CommandDescriptor with a CommandField.

The IDE toolset actually makes this relatively easy by allowing developers to initiate this multi-step configuration process by selecting the CommandField first, and it will automatically walk you through the configuration of the CommandDescriptor as part of the CommandField configuration.

Learning is doing, so follow the steps below and see.

a. Select its Visual Components sub-node.

b. Select the node for button1 (below).

This figure shows the Properties of button1 in Explorer. 

6. Edit button1's Request Handler property.

a. Click the property's ellipsis to bring up the full blown Command Descriptor Property Editor.

This is a very sophisticated editor and takes some effort to get familiar with it, but the payoff is substantial, as it offers exciting additional opportunities to component authors, which is discussed later.

First you must become comfortable using the editor. The Command Descriptor Property Editor contains a dynamic list of available CommandDescriptor types and it also contains an embedded property sheet (at the bottom of the editor) which will dynamically display the properties for the type of CommandDescriptor that is selected (this will become more clear in subsequent steps).

This figure shows the Command Descriptor Property Editor. 

7. Select the Create new shared instance radio button.



Note - The meaning of "shared instance" is explained a few steps later.



8. Select the "User-Defined Command (Default)" item from the list of available descriptor types (see graphic above).

As you select a given command descriptor type, the bottom section of the editor displays the properties which are particular to the type of descriptor you selected.

Your property editor should look as follows:

This figure shows the properties in the Command Descriptor Property Editor particular to the type of descriptor you selected. 

The embedded property sheet contains three tabs, as follows:

9. Select the Component Properties tab.

10. Within the Component Properties tab, select the Command Class Name property (shown below).

This figure shows the Command Class Name property selected within the Component Properties tab. 

The Command Class Name property is of type java.lang.String.

11. Instead of directly typing into the exposed property field, select the ellipsis to bring up the full blown editor.

You should now see that the Command Class Name property editor is actually the same non-extensible Component browser that you have seen in several other contexts.

In this context, however, it intentionally filters the list of components to those which are appropriate for the context (for example, Command components).

12. Fully expand the Current Application Components node, and you should see "ValidatingCommand1" available (shown below).

This figure shows the ValidatingCommand1 node. 

13. Select the "ValidatingCommand1" node from the property editor, and click OK (shown above).

Notice that the Command Class Name property in the CommandDescriptor's embedded property sheet now contains the fully qualified class name for ValidatingCommand1 (shown below).

This figure shows the fully qualified class name for ValidatingCommand1. 


Note - You could have directly typed in the fully qualified name of the Command class (for example, <yourTestApplication>.main.ValidatingTest1) into the Command Class Name property. However, direct editing of the Command Class Name property should only be done in special cases (for example, where you need to refer to a Command Class that exists only as a .class file, and is therefore not visible for direct selection in the Command Component browser as shown above.



14. Click OK to complete the configuration of the CommandDescriptor.

Before further configuration, spend a moment to fully understand the impact of the previous configuration on the Page1.

Note that the value of button1's Request Handler property now reads "commandDescriptor1" (shown below).

This figure shows the value of button 1's Request Handler property that now reads commandDescriptor1. 

You might be asking, "where is commandDescriptor1?".

You might also be wondering about the "Create new shared instance" radio button in the CommandDescriptor property editor that you selected (shown above).

The following step provides the answers.

15. Expand the Page1's Non-Visual Components node (shown below).

There you will find a new non-visual component node "commandDescriptor1". It is the CommandDescriptor object you configured just moments ago. It is a CommandDescriptor that you configured as a "shared instance".

To visually express its "shared" nature, the IDE toolset provides the "Non-visual Components" node to house all of these shared instances.

This figure shows the Non-Visual Components node. 

The "Non-Visual Components" node is an Sun ONE Application Framework component model construct. It provides a ContainerView scoped space for the configuration of JavaBean objects (for example, Configured Beans) which are referenceable by properties elsewhere in the current ContainerView.

In this example, "commandDescriptor1" is a configured CommandDescriptor (which is a JavaBean), which is referenced by button1's Request Handler property. The key to this component model feature is that the same configured non-visual component can be referenced by more than one property within the current ContainerView scope (for example, more than one button or HREF could have its CommandDescriptor property also set to refer to commandDescriptor1).

A quick glance at the IDE toolset generated Java code for Page1 would reveal how this is expressed in Java terms. The benefits to both component authors and application developers are substantial. As a further clarification, not only can multiple CommandFields (for example, Buttons, HREFs, and so on) share commandDescriptor1 by each referring to it in their specific "Command Descriptor" property, but any property within the current ContainerView whose type is assignable from com.iplanet.jato.command.CommandDescriptor may be set to refer to commandDescriptor1. In essence, the non-visual components are class scoped, IDE configurable JavaBean objects which might be referenced in a type safe manner by any number of other visual components within the class.

At this time, the IDE toolset does not support the ability to have one non-visual component refer to another non-visual component.

Application developers must understand and respect the shared nature of the non-visual components. Modifications to the configuration of an existing non-visual component will indirectly affect all circumstances in which that instance of the non-visual component is referenced at run-time.This is why the CommandDescriptor editor (or more generally, the non-visual component editor) always allows one to "Create a new shared instance" of a non-visual component. More often than not, multiple CommandFields within a given ContainerView will not share the same instance of CommandDescriptor, but rather, refer to different and distinctively configured instances of CommandDescriptor.

Now you have configured Page1 to instantiate ValidatingCommand1 and invoke its execute method whenever button1 is indicated in a form submit.

16. Test run the page flow from Page1 to SecurePage1 which is now controlled by ValidatingCommand1.

Test run Page1

The contents of Page1.jsp should show up in the browser.

This figure shows the contents of Page1.jsp in the browser. 

17. Enter an invalid value (any non-integer) in the ValidatingTextField text input, and submit the page.

The page should immediately redisplay with the text of the "Validation Error Message" property immediately following the ValidatingTextField.

This figure shows the Validation Error Message property. 

18. Enter a valid value (for example, 55, or any other valid Integer), and submit the page.

Now, instead of Page1 redisplaying as it did earlier in this guide, the logic within ValidatingCommand1 will display SecurePage1.

This figure shows SecurePage1.