C H A P T E R  3

Developing View Components

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


View Components

For background on Sun ONE Application Framework Views, see the Sun ONE Application Framework Developer's Guide.

View components are also referred to as visual components. The View term comes from the Model-View-Controller design pattern. Most of the types in the com.sun.iplanet library use the term view for this reason. The IDE however, caters to corporate developer expectations by using the term visual component more frequently than view, and page more frequently than ViewBean.

For the purposes of this document:

Broadly speaking, there are two types of view components:

Extensible view components are custom implementations of Sun ONE Application Framework ContainerViews which are intended for further specialization by application developers. For instance, in the Sun ONE Application Framework Component Library, the Basic Container View, Basic Tiled View, and Basic ViewBean are all examples of extensible view components.

Do not read too much into the statement "specialization by application developers" above. Frequently, the only specialization an application developer will make is the addition of child view components (which is done via the IDE), and the logic associated with them.

The most recognizable and easily comprehended non-extensible view components are custom implementations of the DisplayField interface. The Sun ONE Application Framework Component Library contains over a dozen DisplayField components. These fall easily into the classic widget or visual control category, and component developers and application developer alike intuitively relate to these components. As you will see, the Sun ONE Application Framework goes well beyond this minimal component story and offers more potential in the component domain than many component authors and application developers might have seen before.

For instance, a less recognizable non-extensible view component would be ANY concrete ContainerView implementation created by the IDE toolset. Every ContainerView an application developer creates is a non-extensible component. This is a subtlety of the Sun ONE Application Framework approach where nearly everything is a component. Where these various types of components differ is in the way in which they are packaged for distribution and reuse.

ViewComponentInfo

The ViewComponentInfo interface allows component authors to specify additional metadata that is applicable to all view components. This interface is applicable to both extensible and non-extensible view components, and contains metadata, such as, which JSP tags should be associated with the view component.

As indicated above, it is possible, and expected, that multiple ComponentInfo classes can be paired with a single component class to produce a variety of components. For example, the ListBoxComponentInfo, RadioButtonsComponentInfo, and ComboBoxComponentInfo all specify BasicChoiceDisplayField classes as their component class. These form three distinct tuples, and therefore, three distinct logical components. One of the key ways in which these three components differ from each other is that they each implement the ViewComponentInfo's getJspTagDescriptors() method to return a different JspTagDescriptor. In summary, these components are nearly identical to each other, except for the different JSP tags which the IDE toolset generates when an instance of the component is added to the application. The opportunity this presents to component authors is quite liberating. A component author could create a whole new library of JSP tags that generate different markup, and pair them with existing component classes simply by implementing additional ComponentInfo classes.

ContainerViewComponentInfo

The ContainerViewComponentInfo interface allows component authors to additional metadata that is applicable to all ContainerView components. This interface is only applicable to extensible view components.


Develop a Non-Extensible View Component

This section describes how to create a new TextField component that supports a rudimentary input validation feature. In the interest of simplicity, the validation design and implementation are kept to a minimum. This exercise is intended to focus on the mechanics of non-extensible view component design and as such, only scratches the surface of validation support possibilities.



Note - The Sun ONE Application Framework Component Library 2.1.0 already contains a fully productized ValidatingTextField component. This educational exercise results in your creating a validating text field component that approximates the Sun ONE Application Framework Component library's functionality. But the resulting component from this exercise is not equivalent to the one in Sun ONE Application Framework Component library, because this exercise does not attempt to implement all of the features of that productized component.



This example covers several additional Sun ONE Application Framework component model topics, leveraging ViewComponentInfo, developing a new JSP tag, and developing a ConfigurableBean.

The validating text component should support the following design-time functionality:

The validating text component should support the following run-time functionality:

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

A new JSP tag library will also be defined - mycomponents.tld

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

Create the Validator Interface

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

2. Define a very simple validation API.

Design principle hint: In designing the Validator as an interface the stage is being set to leverage the power of the Sun ONE Application Framework component model's ConfigurableBean story. Specifically, a ValidatingTextField property will subsequently be defined to be of type "Validator". And as you will see, the IDE toolset will allow the application developer to choose from a dynamically list of ConfigurableBean types which implement that interface. Furthermore, third party component authors can augment this component story by authoring and distributing additional ConfigurableBean implementations of the same interface.

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

package mycomponents;

 

 

/**

*

* @author component-author

*/

public interface Validator {

 

 

/**

*

*

*/

public abstract boolean validate(Object value);

}


Create at Least One Implementation of the Validator Interface

1. In any Java editor create the class mycomponents.TypeValidator.

2. Add a String property called ValidationRule.

3. Implement the Validator interface.

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

package mycomponents;

import com.iplanet.jato.model.*;

import com.iplanet.jato.util.*;

 

 

public class TypeValidator implements Validator

{

 

public TypeValidator()

{

super();

}

 

public String getValidationRule()

{

return rule;

}

 

public void setValidationRule(String value)

{

rule=value;

}

 

public boolean validate(Object value)

{

if (getValidationRule()==null)

throw new ValidationException("No validation rule has been set");

 

try

{

value=TypeConverter.asType(getValidationRule(),value);

}

catch (Exception e)

{

return false;

}

 

return true;

}

////////////////////////////////////////////////////////////////////////////

// Instance variables

////////////////////////////////////////////////////////////////////////////

 

private String rule;

}


Design hint: The rudimentary implementation of TypeValidator above exposes the ValidationRule as a simple String property. In the absence of any further work, the IDE toolset will expose this property for editing with the default String editor. This will require application developers to explicitly set the value of the property to "java.lang.String" or "java.lang.Integer" or "java.lang.Float". That is not a very user friendly user interface. Since this ValidationRule falls into the ConfigurableBean category, the component author can make use of the full JavaBean component model to improve the user experience. Ideally, a component author would also design and deploy a custom property editor for this property. In this case, a simple drop down list property editor would be a big improvement over the default String editor. Then the component author can create a TypeValidatorBeanInfo which would specify the custom property editor of his choice. For more on this topic, see Design Actions.

Create the Sun ONE Application Framework Component Class

1. In any Java editor create the class mycomponents.ValidatingDisplayField.

2. Make ValidatingDisplayField extend com.iplanet.jato.view.BasicDisplayField

3. Implement the appropriate constructor for the component type.

All DisplayField components must implement a two-arg constructor that takes a View parent and a String name. The IDE toolset assumes that all DisplayField components will implement this constructor.

4. Add a get and set method for the property Validator

5. Add a get and set method for the property ValidationFailureMessage

6. Implement the remaining methods that are required to fulfill our requirements.

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

package mycomponents;

import com.iplanet.jato.view.*;

import com.iplanet.jato.model.*;

import com.iplanet.jato.util.*;

 

 

public class ValidatingDisplayField extends BasicDisplayField {

 

public ValidatingDisplayField(View parent, String name) {

super(parent, name);

}

 

public Validator getValidator()

{

return validator;

}

 

 

public void setValidator(Validator value)

{

validator=value;

}

 

public String getValidationFailureMessage()

{

return validationFailureMessage;

}

 

public void setValidationFailureMessage(String value)

{

validationFailureMessage=value;

}

 

public boolean isValid()

{

return isValid;

}

 

public void setValid(boolean value)

{

isValid = value;

}

 

////////////////////////////////////////////////////////////////////////////

// Value methods

////////////////////////////////////////////////////////////////////////////

 

public Object getValue()

{

if (!isValid())

return getInvalidValue();

else

return super.getValue();

}

 

public Object getInvalidValue()

{

if (invalidValue !=null)

return invalidValue;

else

return null;

}

 

public void setValue(Object value)

{

if (value!=null && getValidator()!=null)

{

if (getValidator().validate(value))

{

try

{

super.setValue(value);

setValid(true);

}

catch (ValidationException e)

{

setValid(false);

invalidValue=value;

setValidationFailureMessage("Exception setting value \""+

"on model: "+ e.getMessage());

}

}

else

{

setValid(false);

invalidValue=value;

}

}

else

super.setValue(value);

}

 

 

////////////////////////////////////////////////////////////////////////////

// Instance variables

////////////////////////////////////////////////////////////////////////////

 

private Validator validator;

private String validationFailureMessage;

private boolean isValid = true;

 

private Object invalidValue;

}


Create a Custom JSP TagHandler Class

Requirements call for the ValidatingComponent class to display its validation error message. One way to achieve this, and the approach pursued here, is to pair the new component with a custom JSP TagHandler class. This will allow you to fully control the rendering of the component.

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

2. Extend this class from com.iplanet.jato.taglib.html.TextFieldTag.

3. Override the doEndTag method to conditionally append the validation error message whenever the component is not valid.

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

package mycomponents;

import com.iplanet.jato.util.*;

import javax.servlet.jsp.*;

import com.iplanet.jato.taglib.html.*;

import com.iplanet.jato.util.*;

import com.iplanet.jato.view.*;

public class ValidatingTextFieldTag extends TextFieldTag

{

 

public ValidatingTextFieldTag()

{

super();

}

 

 

public int doEndTag()

throws JspException

{

int result=super.doEndTag();

 

ContainerView parentContainer=getParentContainerView();

View child=parentContainer.getChild(getName());

checkChildType(child,ValidatingDisplayField.class);

 

ValidatingDisplayField field=(ValidatingDisplayField)child;

// If the field is valid, do nothing.

if (field.isValid())

return result;

 

// Append the validation error message in Red

NonSyncStringBuffer buffer=new NonSyncStringBuffer(

" <font color=\"#FF0000\">");

buffer.append(field.getValidationFailureMessage());

buffer.append("</font>");

writeOutput(buffer);

return result;

}

}




Note - The Sun ONE Application Framework component model allows component authors to specify multiple JSP TagHandlers for a given component. For more on that subject see the JspTagDescriptor API.



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 class and in true OO style, simply augment it. You could, of course, implement the ComponentInfo interface from scratch, but that would be unproductive in this case.

This example takes you beyond the functionality revealed in the first component example. Below, you will take advantage of the key metadata opportunity provided by the ViewComponentInfo interface, the ability to describe JSP tag(s) for a given component.

1. Create the class mycomponents.ValidatingTextFieldComponentInfo.

2. Make the ValidatingTextFieldComponentInfo class extend com.iplanet.jato.view.html2.TextFieldComponentInfo.

3. Implement the no-arg constructor.

4. Implement the getComponentDescriptor() method to provide the basic design-time description of the component.

5. Implement the getConfigPropertyDescriptors() method to identify which properties you wish to expose in the IDE.

6. Implement the getJspTagDescriptors() method to specify the JSP tag which you want the IDE toolset to automatically add to associated JSP(s) whenever an instance of this component is added to a ViewBeans/ContainerViews.

After these steps, mycomponents/ValidatingTextFieldComponentInfo.java should look like the code that 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.beans.*;

import java.util.*;

import com.iplanet.jato.component.*;

import com.iplanet.jato.taglib.*;

import com.iplanet.jato.view.*;

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

 

 

public class ValidatingTextFieldComponentInfo extends TextFieldComponentInfo {

 

public ValidatingTextFieldComponentInfo() {

super();

}

 

public ComponentDescriptor getComponentDescriptor()

{

// identify the component class

ComponentDescriptor result=new ComponentDescriptor(

"mycomponents.ValidatingDisplayField");

 

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

result.setName("ValidatingTextField");

 

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

result.setDisplayName("ValidatingTextField Component");

 

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

result.setShortDescription("A simple validating text field component");

 

return result;

}

 

public ConfigPropertyDescriptor[] getConfigPropertyDescriptors()

{

if (configPropertyDescriptors!=null)

return configPropertyDescriptors;

 

// get any properties defined in the super class

configPropertyDescriptors=super.getConfigPropertyDescriptors();

List descriptors=new LinkedList(Arrays.asList(configPropertyDescriptors));

 

ConfigPropertyDescriptor descriptor = null;

descriptor=new ConfigPropertyDescriptor(

"validator",Validator.class);

descriptor.setDisplayName("Validator");

descriptor.setHidden(false);

descriptor.setExpert(false);

descriptors.add(descriptor);

 

descriptor=new ConfigPropertyDescriptor(

"validationFailureMessage",String.class);

descriptor.setDisplayName("Validation Failure Message");

descriptor.setHidden(false);

descriptor.setExpert(false);

descriptors.add(descriptor);

 

// Create/return the array

configPropertyDescriptors = (ConfigPropertyDescriptor[])

descriptors.toArray(

new ConfigPropertyDescriptor[descriptors.size()]);

return configPropertyDescriptors;

}

 

public JspTagDescriptor[] getJspTagDescriptors()

{

JspTagAttributeDescriptor[] attrs=new JspTagAttributeDescriptor[1];

attrs[0]=new JspTagAttributeDescriptor(

TagBase.ATTR_NAME,JspTagDescriptor.ASSUMED_PROPERTY_NAME,null);

JspTagDescriptor htmlTagDescriptor=new JspTagDescriptor(

JspTagDescriptor.ENCODING_HTML,"validatingTextField",

"/WEB-INF/mycomplib.tld",attrs);

 

return new JspTagDescriptor[] {htmlTagDescriptor};

}

private ConfigPropertyDescriptor[] configPropertyDescriptors;

}


Create a New Tag Library TLD File

Since a new JSP TagHandler has been defined, a JSP library TLD file must be created for the component library.

There is a soft restriction on your custom JSP library. During IDE operations, a logical object model is created for the working JSP files. This JSP object model is used by the page and pagelet view component mechanisms to manage the placement of tags in the JSP while the views are mutated. While parsing the JSP file to create the JSP object model, tags for Sun ONE Application Framework component tag libraries have special treatment. If your custom JSP tag library has additional tags which are not related to a Sun ONE Application Framework view component, these tags might be categorized incorrectly in the JSP object model. You should isolate your Sun ONE Application Framework related tags in their own tag library. Internally in the JSP object model, tags from tag libraries specified in the component library manifest will be categorized as "JATO" tags, while all other tags in the JSP file are categorized as "OTHER" tags. The reason why this is a soft restriction is that there is only an edge case where a non Sun ONE Application Framework tag would interfere with view component tag management. If a non Sun ONE Application Framework tag remains in your component tag library, and if that tag has an attribute "name" who's value collides with a "name" attribute of a true Sun ONE Application Framework tag, the JSP object model might not operate properly. In other words, if you have non Sun ONE Application Framework tags which have a "name" attribute, you should try and isolate these tags in a separate tag library to avoid edge case problems.

The library TLD file name is arbitrary. Its location within the library is also arbitrary. In a later step, the new TLD file will be declared in your component manifest. A full discussion of JSP tld files is beyond the scope of this document. Suffice to say, for this example, only a new library (mycomlib) containing a single tag element (validatingTextField) needs to be declared. All of the tag attributes can be copied verbatim from the declaration of the TextField tag in the Sun ONE Application Framework Component Library's jato.tld file. You can find the jato.tld file located in the WEB-INF\tld\com_iplanet_jato directory of any Sun ONE Application Framework application created by the Sun ONE Studio.

1. Create the file mycomponents/mycomplib.tld.

2. Add the basic tld information to declare a new tag library.

3. Add a tag element for the new tag validatingTextField and its corresponding tag-class mycomponents.ValidatingTextFieldTag.

4. Complete the tag element declaration by adding all desired tag attributes. Copy those already defined in jato.tld for the Sun ONE Application Framework Component Library's TextField tag.

After these steps, the mycomponents/mycomplib.tld file should look as follows:

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

 

<!DOCTYPE taglib

PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"

"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<!-- template test -->

 

<taglib>

<tlib-version>1.0</tlib-version>

<jsp-version>1.2</jsp-version>

<short-name>mycomponents.mycomplib</short-name>

<display-name>mycomponents.mycomplib</display-name>

<tag>

<name>validatingTextField</name>

<tag-class>mycomponents.ValidatingTextFieldTag</tag-class>

<body-content>empty</body-content>

<display-name>Validating Text Field</display-name>

<description></description>

<attribute>

<name>name</name>

<required>true</required>

<rtexprvalue>false</rtexprvalue>

<type>String</type>

</attribute>

...

<!-- more attribute definitions follow -->

...

 

</tag>

</taglib>


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 a component element to declare the ValidatingTextField component.

3. Add a ConfigurableBean element to declare the mycomponents.TypeValidator.

4. Add a taglib element to declare the mycomplib.tld.

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



Note - Make sure that the tld is a well formed XML document. Even something as minor as inappropriate leading spaces before the first XML tag can create a malformed document. If your tld file is not well formed XML, certain servlet containers will fail to load your entire Web application. Such errors might be difficult to track down.



<?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>

<!-- Your icon here

<icon>

<small-icon>/com/iplanet/jato/resources/complib.gif</small-icon>

</icon>

-->

<interface-version>1.0.0</interface-version>

<implementation-version>20030221</implementation-version>

 

<component>

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

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

</component>

<component>

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

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

</component>

<configurable-bean>

<bean-class>mycomponents.TypeValidator</bean-class>

</configurable-bean>

<taglib>

<taglib-uri>/WEB-INF/mycomplib.tld</taglib-uri>

<taglib-resource>/mycomponents/mycomplib.tld</taglib-resource>

<taglib-default-prefix>mycomp</taglib-default-prefix>

</taglib>

</component-library>


Recreate the Component Library JAR File

Jar up the component classes as you did in the first example so that 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 new JSP tag library.

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

mycomponents/MyTextField.class

mycomponents/MyTextFieldComponentInfo.class

mycomponents/TypeValidator.class

mycomponents/ValidatingDisplayField.class

mycomponents/ValidatingTextFieldComponentInfo.class

mycomponents/ValidatingTextFieldTag.class

mycomponents/Validator.class

mycomponents/mycomplib.tld

COMP-INF/complib.xml


Test the New Component

Your library is now ready for testing and distribution. You should test it in a sample project. This stage requires the use of the Sun ONE Studio with the Sun ONE Application Framework module installed and enabled. If you have never built a Sun ONE Application Framework application in the Sun ONE Studio, before continuing, you should first complete the Sun ONE Application Framework Tutorial that is included with the Sun ONE Application Framework document set.

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. This presents a bit of a challenge when iteratively developing a component library and testing that library in a test application.

For repeatedly testing new versions of the same library JAR file within a test application, perform the following steps:

a. Unmount the test application.

b. After the unmount is complete, go to your operating system file system and copy the new library JAR file over the old library JAR file in the unmounted test application's WEB-INF/lib directory.

c. Remount the test application.

The test application should now pick up the new library version.

Normally, those steps work fine. If you encounter a spurious failure that either prevents you from copying the new JAR over the old JAR, or failure to remount the test application properly, the fallback strategy is to restart the Sun ONE Studio.

2. Select the previously created Page1 object.

3. Add an instance of the ValidatingTextFieldComponent to Page1.

You can either select the component from the Component Palette, or select the Page1's Visual Components sub-node, right-click, and select the Add Visual Component... action from the pop-up menu.

This figure shows the Component Palette.      This figure shows the Component Browser (alternative to Component Palette). 

This figure shows the Component Palette.      This figure shows the Component Browser (alternative to Component Palette). 

 

4. Select the "ValidatingTextField Component" from the list.

Observe how a child view named "validatingTextField1" is added to the page.

5. Select the validatingTextField1 visual component node.

Observe how the IDE's property sheet now displays the custom Validator and Validation Failure Message properties, in addition to the inherited TextField component properties.

This figure shows the validatingTextField1 visual component node. 

6. Edit the Validation Failure Message property.

Set it to "This is a test failure message" (or anything you like).

7. Edit the Validator property.

This should bring up the following dedicated ConfigurableBean editor. For test purposes, set the validationRule property to "java.lang.Integer".

Make sure you specify the fully qualified class name for the validationRule property. Just "Integer" will not evaluate properly at run-time. It must be fully qualified, as in "java.lang.Integer".

This figure shows the dedicated ConfigurableBean editor. 

8. Observe the code generation inside the page's java file.

You should see a block of code inside the createChildReserved method that looks like the following (the indenting in your code might differ):

 

...

else if (name.equals(CHILD_VALIDATING_TEXT_FIELD1)) {

mycomponents.ValidatingDisplayField child =

new mycomponents.ValidatingDisplayField(this, CHILD_VALIDATING_TEXT_FIELD1);

mycomponents.TypeValidator validatorVar =

new mycomponents.TypeValidator();

{ // begin local variable scope

validatorVar.setValidationRule("java.lang.Integer");

} // end local variable scope

child.setValidator(validatorVar);

child.setValidationFailureMessage( "This is a test failure message");

return child;

}

...


9. Open the associated JSP file to observe the inclusion of the validatingTextField tag.

Note also the automatic inclusion of a mycomplib.tld directive. (The overall look of your test application's JSP may differ from the one below, depending upon whether you have added other child views you to your test page in addition to validatingTextField1)

<%@page contentType="text/html; charset=ISO-8859-1" info="Page1" language="java"%>

<%@taglib uri="/WEB-INF/jato.tld" prefix="jato"%>

<%@taglib uri="/WEB-INF/mycomplib.tld" prefix="mycomp"%>

 

<jato:useViewBean className="testmycomplib.main.Page1">

 

<html>

<head>

<title>Page1</title>

</head>

<body>

<jato:form name="Page1" method="post">

<jato:textField name="myTextField1"/>

<mycomp:validatingTextField name="validatingTextField1"/>

</jato:form>

</body>

</html>

 

</jato:useViewBean>


10. Before you can effectively test run Page1, you need to add a button and request handling code.

For your test purposes, you should take the following steps to add a button and some request handling code, which will redisplay the page following a submit. This allows you to see if the ValidatingTextComponent is behaving as designed. If you have not done so already, add a button and some request handling code, follow the steps below:



Note - The steps below represent conventional Sun ONE Application Framework application development practice, the details of which are beyond the scope of this document. These steps, or similar ones, are required to create an effective test page.



a. Add an instance of the Sun ONE Application Framework Library's Basic Button to Page1.

You may either select the component from the Component Palette or select the Page1's Visual Components sub-node, right-click, and select the Add Visual Component... action from the pop-up menu.

This adds a "button1" child to your test ViewBean.

b. Select the button1 visual component node.

c. Right-click, and select the pop up menu's Events->handleRequest action.

This adds an event handler method named handleButton1Request to your ViewBean's Java file.

For this test, you do not need to modify the body of handleButton1Request since it is auto-generated to redisplay the current page, which is precisely the test you are looking for.

Make sure your request handler looks as follows:

public void handleButton1Request(RequestInvocationEvent event) throws Exception {

getParentViewBean().forwardTo(getRequestContext());

}


11. Test run Page1.

See the Sun ONE Application Framework Tutorial if you do not already know how to test run a Sun ONE Application Framework ViewBean.

The Page1 output should appear in a browser looking as follows (it now contains two text fields, one instance of MyTextField, and one instance of ValidatingTextField):

This figure shows the Page 1 output in a browser. 

12. Enter an invalid value (for example, any value other than an integer) in the ValidatingTextField's text input, and submit the page.

The page should immediately be redisplayed with the text of the Validation Error Message property immediately following the ValidatingTextField.

This figure shows the page redisplayed with the text of the Validation Error Message property. 

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

The page should be redisplayed without the Validation Error Message text.

If you continue to get the Validation Error Message, go back and verify that you set the value of validatingTextField1's Validator->ValidationRule property to "java.lang.Integer", and not just "Integer".

This figure shows the redisplayed page without the Validation Error Message text. 

Ship It!

Now that your component is functioning properly, you can ship it. However, you might also go back and enhance it. For instance, you might decide that requiring the end user to type "java.lang.Integer" into the Validator's ValidationRule property is unacceptably error prone. If so, you should spend a little time and develop a custom property editor. The details of that are beyond the scope of this document, but can be found in any basic JavaBean reference.


Develop an Extensible View Component

This section describes how to create a new ViewBean component that supports a rudimentary page level security feature. In the interest of simplicity, the security model and implementation will be kept to a minimum. This exercise is intended to focus on the mechanics of extensible View component design and, as such, only scratches the surface of security model possibilities. Upon completion of this section, you should have a good understanding of the role of extensible components within the Sun ONE Application Framework. Bear in mind that this example will implement several optional features, and goes beyond the bare minimum required to author an extensible View component.

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

As a basic design principle, the Sun ONE Application Framework prefers to be enabling rather than prescriptive when it comes to application and page level security, since developer preferences in this domain vary widely. This example demonstrates that the Sun ONE Application Framework can easily enable an arbitrary page level security model. It is not meant to suggest that this example is the ultimate or recommended implementation.

Your secure ViewBean component should support the following design-time functionality:

Your secure ViewBean component should support the following run-time functionality:

This run-time model assumes that both the grant tokens and the required tokens will be specified on a per secure ViewBean basis by the application developers.

The implementation of the secure ViewBean component is responsible for tracking the accumulated tokens at run-time, and enforcing the security model described above. The implementation shall store the accumulated tokens per user in a special HttpSession attribute.

The choice to implement the base class version of handleMissingTokens to throw a SecurityCheckException is purely arbitrary. Alternatively, you could implement that method to transfer control to a more user friendly error page, or anything else that the component author prefers. Strictly in the interest of brevity and simplicity, the choice is to throw a SecurityCheckException.

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 SecureViewBean.

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

Create the MissingTokensEvent Class

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

2. Define a very simple event API that will allow the event handler to discover which tokens were missing.

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

package mycomponents;

import java.util.*;

public class MissingTokensEvent extends Object {

public MissingTokensEvent(List tokens) {

missingTokens = new ArrayList(tokens);

}

public String toString() {

Iterator iter = missingTokens.iterator();

StringBuffer buff = new StringBuffer();

buff.append("MissingToken count[" + missingTokens.size() + "] ");

while(iter.hasNext()) {

buff.append("Token[" + (String)iter.next() + "] ");

}

return buff.toString();

}

 

public ArrayList getMissingTokens() {

return missingTokens;

}

 

private ArrayList missingTokens = null;

 

}


Create the Sun ONE Application Framework Component Class

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

2. Make SecureViewBean extend com.iplanet.jato.view.BasicViewBean.

3. Implement the appropriate constructor for the component type.

All ViewBean components must implement a no-arg constructor.

4. Add a get and set method for the property named "RequiredTokens".

5. Add a get and set method for the property named "GrantTokens".

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

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

package mycomponents;

import java.util.*;

import com.iplanet.jato.view.*;

import com.iplanet.jato.*;

 

public class SecureViewBean extends BasicViewBean {

 

public SecureViewBean()

{

super();

}

 

public String[] getRequiredTokens()

{

return requiredTokens;

}

 

public void setRequiredTokens(String[] value)

{

requiredTokens = value;

}

 

public String[] getGrantTokens()

{

return grantTokens;

}

 

public void setGrantTokens(String[] value)

{

grantTokens = value;

}

 

public void securityCheck() throws SecurityCheckException

{

super.securityCheck();

// Get the accumulated tokens from session.

HashSet accumulated = (HashSet)

getSession().getAttribute("AccumulatedTokens");

// Defensively prepare the accumulated collection

if(accumulated == null)

accumulated = new HashSet();

// Check to see if required tokens are present

if(requiredTokens.length > 0) {

// Check for presence of required tokens

List missingTokens = new ArrayList();

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

{

if(! accumulated.contains(requiredTokens[i]))

missingTokens.add(requiredTokens[i]);

}

 

if(missingTokens.size() > 0)

handleMissingTokens(new MissingTokensEvent(missingTokens));

}

 

// Now add the current grant tokens to the accumulated.

// Note, as expected, we will not reach this point if the

// handleMissingTokens throws an Exception.

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

{

accumulated.add(grantTokens[i]);

}

getSession().setAttribute("AccumulatedTokens", accumulated);

}

 

public void handleMissingTokens(MissingTokensEvent e)

throws SecurityCheckException

{

// This default implementation will just trigger conventional

// Sun ONE Application Framework SecurityCheckException handling

throw new SecurityCheckException(e.toString());

}

 

private String[] requiredTokens = new String[0];

private String[] grantTokens = new String[0];

 

}


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 might 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 might 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, the template will be kept minimal.

1. Create a new directory mycomponents.resources.

2. In any text editor, create the template mycomponents.resources.SecureViewBean_java.template.

The template contents should look as follows:



Note - The tokens follow a __TOKEN__ pattern.



package __PACKAGE__;

 

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

import com.iplanet.jato.*;

import com.iplanet.jato.view.*;

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

import com.iplanet.jato.model.*;

import mycomponents.*;

 

 

/**

*

*

*/

public class __CLASS_NAME__ extends SecureViewBean

{

/**

* Default constructor

*

*/

public __CLASS_NAME__()

{

super();

}

 

 

}


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 going beyond the functionality revealed in the earlier component examples. Below, you are going to take advantage of two new metadata opportunities provided by the ExtensibleComponentInfo interface, the opportunity to specify a Java template, and the opportunity to describe event handler methods for the extensible component.

1. Create the class mycomponents.SecureViewBeanComponentInfo.

2. Make SecureViewBeanComponentInfo extend com.iplanet.jato.view.BasicViewBeanComponentInfo.

3. Implement the no-arg constructor.

4. Implement the getComponentDescriptor() method to provide the basic design-time description of the component.

5. Implement the getConfigPropertyDescriptors() method to identify which properties you want to expose in the IDE.

a. Add an IndexedConfigPropertyDescriptor for the RequiredTokens property.

b. Add an IndexedConfigPropertyDescriptor for the GrantTokens property.

6. 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.

7. Implement the getEventHandlerDescriptors() method to provide a design-time description of any event handler methods which you want the IDE toolset to expose for automated insertion into new classes derived from this extensible component.

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

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.io.*;

import com.iplanet.jato.*;

import com.iplanet.jato.component.*;

import com.iplanet.jato.view.*;

 

 

public class SecureViewBeanComponentInfo extends BasicViewBeanComponentInfo

{

 

public SecureViewBeanComponentInfo()

{

super();

}

 

public ComponentDescriptor getComponentDescriptor()

{

final String CLASS_NAME="mycomponents.SecureViewBean";

 

ComponentDescriptor descriptor=new ComponentDescriptor(

CLASS_NAME);

descriptor.setName("SecurePage");

descriptor.setDisplayName("Secure ViewBean");

descriptor.setShortDescription(

"A Page with a token based security model");

return descriptor;

}

 

public ConfigPropertyDescriptor[] getConfigPropertyDescriptors()

{

if (configPropertyDescriptors!=null)

return configPropertyDescriptors;

 

configPropertyDescriptors=super.getConfigPropertyDescriptors();

List descriptors=new LinkedList(Arrays.asList(configPropertyDescriptors));

 

ConfigPropertyDescriptor descriptor = null;

descriptor=new IndexedConfigPropertyDescriptor(

"grantTokens",String.class); // NOI18N

descriptor.setDisplayName("Grant Tokens"); // NOI18N

descriptor.setHidden(false);

descriptor.setExpert(false);

descriptors.add(descriptor);

 

descriptor=new IndexedConfigPropertyDescriptor(

"requiredTokens",String.class); // NOI18N

descriptor.setDisplayName("Required Tokens"); // NOI18N

descriptor.setHidden(false);

descriptor.setExpert(false);

descriptors.add(descriptor);

// Create/return the array

configPropertyDescriptors = (ConfigPropertyDescriptor[])

descriptors.toArray(

new ConfigPropertyDescriptor[descriptors.size()]);

return configPropertyDescriptors;

}

 

public String getPrimaryTemplateEncoding()

{

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

return getResourceString(getClass(),

"PROP_SecureViewBean_SOURCE_TEMPLATE_ENCODING", "ascii");

*/

 

return "ascii";

}

 

public InputStream getPrimaryTemplateAsStream()

{

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

 

return SecureViewBeanComponentInfo.class.getClassLoader().

getResourceAsStream(

getResourceString(getClass(),

"RES_SecureViewBeanComponentInfo_SOURCE_TEMPLATE",""));

*/

 

return SecureViewBeanComponentInfo.class.getResourceAsStream(

"/mycomponents/resources/SecureViewBean_java.template");

}

 

public EventHandlerDescriptor[] getEventHandlerDescriptors()

{

if (eventHandlerDescriptors!=null)

return eventHandlerDescriptors;

 

eventHandlerDescriptors=super.getEventHandlerDescriptors();

List descriptors=new LinkedList(

Arrays.asList(eventHandlerDescriptors));

 

EventHandlerDescriptor descriptor =new EventHandlerDescriptor(

"handleMissingTokens",

"handleMissingTokens",

"public void handleMissingTokens(MissingTokensEvent e)" +

"throws SecurityCheckException",

"throw new SecurityCheckException(e.toString());",

"");

 

descriptors.add(descriptor);

 

// Create/return the array

eventHandlerDescriptors = (EventHandlerDescriptor[])

descriptors.toArray(

new EventHandlerDescriptor[descriptors.size()]);

return eventHandlerDescriptors;

}

 

private ConfigPropertyDescriptor[] configPropertyDescriptors;

private EventHandlerDescriptor[] eventHandlerDescriptors;

}


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 SecureViewField 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.SecureViewBean</component-class>

<component-info-class>mycomponents.SecureViewBeanComponentInfo</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 that 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".

You can omit the Java source files from the JAR.

2. Include in the JAR any necessary ancillary resources, like icon images, or resource bundles.

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

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

mycomponents/resources/SecureViewBean_java.template

mycomponents/MissingTokensEvent.class

mycomponents/MyTextField.class

mycomponents/MyTextFieldComponentInfo.class

mycomponents/SecureViewBean.class

mycomponents/SecureViewBeanComponentInfo.class

mycomponents/TypeValidator.class

mycomponents/ValidatingDisplayField.class

mycomponents/ValidatingTextFieldComponentInfo.class

mycomponents/ValidatingTextFieldTag.class

mycomponents/Validator.class

mycomponents/mycomplib.tld

COMP-INF/complib.xml


Test the New Component

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. This presents a bit of a challenge when iteratively developing a component library and testing that library in a test application. For repeatedly testing new versions of the same library JAR file within a test application, do the following:

a. Unmount the test application.

b. After the unmount is complete, go to your operating system file system and copy the new library JAR file over the old library JAR file in the unmounted test application's WEB-INF/lib directory.

c. Remount the test application.

The test application should now pick up the new library version.

Normally, those steps work fine. If you encounter a spurious failure that either prevents you from copying the new JAR over the old JAR, or failure to remount the test application properly, the fallback strategy is to restart the Sun ONE Studio.

2. Create a new ViewBean object.

The new ViewBean wizard should now look as follows:

This figure shows the Select View Type panel. 

3. Select the "Secure ViewBean" from the component list and complete the wizard.

Take the default settings and let the wizard create SecurePage1 for you. (You can select Finish in the wizard stage shown above.)

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

5. To test your security model, create a second SecureViewBean.

You application should now contain two SecureViewBeans (SecurePage1 and SecurePage2).

The new SecureViewBeans contain the Grant Tokens and Required Tokens properties.

This figure shows two SecureViewBeans (SecurePage1 and SecurePage2). 

6. Test the security model by introducing some values into the token properties.

Select SecurePage1's Grant Tokens property.

If you select the ellipsis in the property sheet, it will bring up the indexed String property editor.

7. Add the value "login" to that property.

You can add additional tokens.

This figure shows the indexed String property editor. 

8. Select the other Secure ViewBean, SecurePage2.

Select its Required Tokens property, and add the value "login".

You have now established a page security rule in your application.

SecurePage2 requires the "login" token, and SecurePage1 grants the "login" token. Therefore, an end user who does not visit SecurePage1 BEFORE SecurePage2 should trigger a security exception.

9. Add some static content to the SecurePage2's associated JSP, SecurePage2.jsp, since this is currently a blank page.

For example, put the text "Welcome to Secure2" into SecurePage2.jsp so you will recognize it in the browser

10. Test run SecurePage2.

Instead of seeing SecurePage2.jsp's content, you should see the following message in the browser:



Note - If you see this message, it means that the SecureViewBean security model has worked as intended. At least the access prevention has worked.



This figure shows the message indicating that the SecureViewBean security model has worked as intended. 

11. Create a link between SecurePage1 and SecurePage2 so that you can test the positive path.

There are several ways to do this.

You can implement your own link. The instructions that follow are just one approach.

a. Add an instance of the Sun ONE Application Framework Library's Basic Button to SecurePage1.

You can either select the component from the Component Palette, or select the SecurePage1's Visual Components sub-node, right-click, and select the Add Visual Component... action from the pop-up menu.

This will add a "button1" child to your test ViewBean

b. Select the button1 visual component node.

c. Right-click, and select the pop up menu's Events->handleRequest action.

This will add an event handler method named handleButton1Request to your SecurePage1's Java file.

d. Rework the body of the handleButton1Request to look as follows:

public void handleButton1Request(RequestInvocationEvent event) throws Exception {

getViewBean(SecurePage2.class).forwardTo(getRequestContext());

}


12. Test run the page flow from SecurePage1 to SecurePage2.

a. Test run SecurePage1.

b. Secure1 should appear in the browser as a blank page with a single button labeled "Submit".

The user should now have been granted the "login" token.

c. Press the Submit button.

This will trigger the handleButton1Request logic which will forward the request to SecurePage2.

The contents of SecurePage2.jsp should show up in the browser (because the user had accumulated the required tokens).

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

Ship It?

Not yet. First test the EventHandlerDescriptor feature (handleMissingTokens).

Recall that the SecureViewBeanComponentInfo declares an EventHandlerDescriptor which described an event handler called handleMissingTokens. Now you need to test this feature.

1. Select the SecurePage2 node.

2. Right-click, and select the pop up menu's Events->handleMissingTokens option.

This should insert the handleMissingTokens method skeleton into SecurePage2.java and automatically position the Java editor at that method.

3. Edit that method to automatically route users back to SecurePage1 when this event is triggered.

This is just an arbitrary means of testing the event handler. Application developers can implement this handler any way they want.

public void handleMissingTokens(MissingTokensEvent e)throws SecurityCheckException {

// Route invalid access users to SecurePage1

appMessage("You need to go to Secure1 before Secure2");

getViewBean(SecurePage1.class).forwardTo(getRequestContext());

// Stop further processing of the original request.

throw new CompleteRequestException();

}


4. Test run SecurePage2 again.

This time, the browser should return SecurePage1, because the event handler took control.

This figure shows SecurePage1 in the browser.