cornercorner
FeaturesPluginsDocs & SupportCommunityPartners

WoodstockCustomComponents

Developing Woodstock components.

This article will give an overview on how to go about creating JSF components for use in the netbeans Visual Web pallete. You will be using the woodstock components source for building these components. The tutorial consists of the following sections.

[[{TableOfContentsTitle=TableOfContents} | {TableOfContents title='Table of Contents'}]]

Pre-requisites

Before starting off with the tutorial, you would need to have the following installed on your computer.

a) Netbeans 6.x with visual web pack installed. b) Glassfish v2 or tomcat 6.x c) A web browser such as firefox for viewing the output.


It is also assumed that you have a knowledge of

a) Java Server Faces

b) Javascript c) HTML and CSS

We will use staticText component as a reference implementation for demonstrating how to write a simple woodstock component for visual web pack.

__Note: The tutorial uses the latest workspace from the woodstock repository. Hence, the tutorial will be based on the "under-development" woodstock 4.3 workspace and not the 4.2 version that is bundled with the netbeans 6.1 IDE.

Woodstock source code is undergoing private API changes and hence, APIs described over here for widget creation process may differ from the latest version of the source code that you have downloaded.Also, the APIs for widget development are

not publicly supported for now and hence, code which works with a daily snapshot one day may not work after you have updated the 

code at a later stage. Effort will be made to periodically update this note to reflect the latest APIs involved in widget creation to facilitate developers.__

1. Check out the woodstock source code

We will be using the woodstock tree to create our new component,. The first step involved is to check out the woodstock source code from java.net. To do this, you would need to register yourself at java.net and obtain an user name and password.

Open netbeans 6, select Versioning -> CVS -> checkout

In the popup wizard, configure the CVSROOT as:

pserver:<your-java.net-username>@woodstock.dev.java.net:/cvs

Enter your java.net password in the password field and press Next > File:WoodstockCustomComponents/checkout-cvs-root WoodstockCustomComponents.JPG

In the next screen, simply click next > again and the check out will start. After the checkout ends, you should find a message like this: File:WoodstockCustomComponents/completed WoodstockCustomComponents.JPG


Choose "Open Project..." and select webui and themes projects. These are the two projects that we will use for creating custom components.


2. Create JSF components and Renderers

2.1 Create the JSF component

You can shoose to either create an independent library for your component set or you can choose to add on to the existing component set. You would like to do the latter as it is much more easier to integrate it with the woodstock designtime component set.

We will take the example of a static text component for this tutorial.

i) Create the component class for the new component that you are developing. This component class should be placed in the com.sun.webui.jsf.component package in the webui/runtime folder. This class would define the properties that your component wants to expose to the developers.

We use annotations to annotate the component class and its properties.

@Component(type="com.sun.webui.jsf.StaticText", family="com.sun.webui.jsf.StaticText", displayName="Static Text", tagName="staticText", tagRendererType="com.sun.webui.jsf.widget.StaticText",
    helpKey="projrave_ui_elements_palette_wdstk-jsf1.2_static_text",
    propertiesHelpKey="projrave_ui_elements_palette_wdstk-jsf1.2_propsheets_static_text_props")
public class StaticText extends UIOutput {

The above snippet shows the annotation for the staticText component class. The "@Component" annotation identifies this class as a component class and the other attributes define the kind of component it. The helpKey and propertiesHelpKey are something specific to designtime and we can take a look at that later.

The component that you are developing would usually inherit from one of the core JSF base classes like UIInput, UIOuput, UICommand etc. The staticText component class inherits from the UIOutput base class since it deals with displaying some sort of text on the page.

You will need to override some of the super class methods you are inheriting from for your component.

- Set the renderer type for the component you are creating. This will basically identify the renderer class which renders the component on to the browser page. For staticText, we have an html, json and ajax renderer. Since the default renderer for the static text is an json renderer that outputs json attributes on to the page, we will set that renderer as the renderer type in the staticText component's constructor. This class (which we will see later) resides in the com.sun.webui.jsf.renderkit.widget package.

- Override the getFamily method to return the family type of your component. The staticText returns "com.sun.webui.jsf.StaticText"

- We will also need to override the getRendererType method if the component we are developing has more than one renderer associated with it. The staticText component uses an ajax renderer to handle ajax requests and render data. Hence, the getRendererType method will try to determine the kind of request the component receives and delegate the response processing to an appropriate renderer.

      if (ComponentUtilities.isAjaxRequest(getFacesContext(), this)) {
            return "com.sun.webui.jsf.ajax.StaticText";
        }
        return super.getRendererType();
    }

If the request is an ajax request then the renderer type is set to the renderer present in the com.sun.webui.jsf.ajax package.

-Next define the rest of the properties that you would want to expose to the developers for your component. Annotate each property with the "@Property" annotation and define the setter and getter properties for it. Some of the common properties that you would want to expose would be the mouse event handlers (onMouseXXX properties), key event handlers (onKeyXXX properties). The woodstock project also has a few convenience base classes that you can extend from such as WebuiInput, WebuiOuput etc. Here is the code snippet for the escape property.

    /**
     * <p>Escape the html text so it won't be interpreted by the browser as HTML</p>
     */
    @Property(name="escape", displayName="Escape", category="Data")
    private boolean escape = false;
    private boolean escape_set = false;

    /**
     * <p>Escape the html text so it won't be interpreted by the browser as HTML</p>
     */
    public boolean isEscape() {
        if (this.escape_set) {
            return this.escape;
        }
        ValueExpression _vb = getValueExpression("escape");
        if (_vb != null) {
            Object _result = _vb.getValue(getFacesContext().getELContext());
            if (_result == null) {
                return false;
            } else {
                return ((Boolean) _result).booleanValue();
            }
        }
        return true;
    }

    /**
     * <p>Escape the html text so it won't be interpreted by the browser as HTML</p>
     * @see #isEscape()
     */
    public void setEscape(boolean escape) {
        this.escape = escape;
        this.escape_set = true;
    }

The "category" attribute of the @Property annotation specifies under which section in the "properties" section of the component palette would that particular property be shown. For the above snippet, the property will be shown under the "data" category.

- We would also need to expose an attribute called "htmlTemplate" which will allow the developer to specify an alternate template (discussed later) for his jsf component.

- Make sure you have your attributes specified in the saveState and restoreState methods.

2.2 Create the JSF Renderer for the component

The next step would be to create a corresponding renderer for the component you have just developed. You can create an html renderer if you want to render plain html on to the page. Note that in this case though, you would not be able to provide any "out of the box" ajax capabilities for your custom component. If you want your component to have ajax capabilities, you would like to have a widget renderer for the component that renders json properties to the client. We would need to then develop a client side widget (described later) for this component to be rendered on the page. This widget will be able to have"out of the box" ajax capabilities.

You would also need an "ajax" renderer to take care of the ajax requests. On the other hand, if you do not want your component to treat ajax requests differently, you can use the same widget renderer for handling your ajax requests too.

The staticText component has an

- html renderer - that is used mainly for designtime rendering. - widget renderer - Used to render JSON properties to the cleint - ajax renderer - Handle ajax requests from the client.

2.2.1 The widget renderer

We will take a look at the widget renderer and ajax renderer in this section. Since the html renderer is mostly used for the designtime purposes, we will take a look at the html renderer in the designtime section.

The woodstock component widget renderers inherit from the com.sun.webui.jsf.renderkit.widget.RendererBase class. This encapsulates most of the common rendering behavior for the woodstock widgets. The renderers are annotated with the @Renderer annotation (defined in the com.sun.faces.annotation.Renderer package). This annotation describes the component that this particular renderer will render.

The two methods that you would need to define in your widget renderer would be the

- getProperties - This method returns back the widget properties for the particular component as a org.json.JSONObject. The method signature will be

   protected JSONObject getProperties(FacesContext context,
            UIComponent component) throws IOException, JSONException {

            ..............
            ............

  JSONObject json = new JSONObject(); /** Create a new JSONObject**/
     
      /** Populate the JSONObject with the component properties**/
        json.put("value", message)
            .put("title", staticText.getToolTip());
    ...........
    ...........

    return json;.

}

- getWidgetType - Return the type of widget to be rendered. This is useful in the client side for choosing the appropriate javascript widget on the client side.

  protected String getWidgetType(FacesContext context, UIComponent component) {
        return JavaScriptUtilities.getModuleName("widget.staticText");
    }

The JavaScriptUtilities class contained in the com.sun.webui.jsf.util.JavascriptUtilities.package returns a string that is used for creating the widget on the client side.


2.2.2 The ajax renderer

The woodstock component ajax renderer is used to server ajax requests. They are placed in the com.sun.webui.jsf.renderkit.ajax package. The ajax renderers inherit from their widget renderers. So, we only basically need to override the parts of the widget renderer where the rendering differs for ajax requests.

For example, the staticText renderer prefers only to encode its children during ajax requests. Hence, we override the encodeBegin and encodeEnd methods so that it does not render anything for ajax requests.

@Renderer(@Renderer.Renders(
    rendererType="com.sun.webui.jsf.ajax.StaticText",
    componentFamily="com.sun.webui.jsf.StaticText"))
public class StaticTextRenderer
        extends com.sun.webui.jsf.renderkit.widget.StaticTextRenderer {

....
....
    public void encodeBegin(FacesContext context, UIComponent component) {
        // Do nothing...
    }

....
....

}

3 Creating client side widgets

The client side widgets are created using the dojo infrastructure.All the files created pertaining to client side widgets are put inside the themes module.

3.1 Create the html template

The html template is used for constructing the javascript widget. The html template defines the html structure for the widget that we are going to create. The files are to be placed in the themes/src/suntheme/templates directory. The naming convention for the html template file is <widget-name>.html Each html element has attach points which serve in getting hold of the element in the javascript code.

<span dojoAttachPoint="_domNode"></span>

Note that for complex components, the html template may not completely describe the structure of the widget. For such elements, we define these elements dynamically in the javascript code.

We also need to create a reference to the html template that we just created in the themes/src/suntheme/properties/templates.properties.

3.2 Create the css and images for the widget.

The woodstock widgets have their css styles presented in the themes/src/suntheme/css directory. The css files are divided in three parts for easy manageability. namely layout.css, colorAndMedia.css and typography.css. Additionally, there are browser specific css files that allow you to specify browser specific styles. There are ie.css (for IE 6 and others) ie7.css (for IE7) and safari.css (for safari browsers)

The styles that you create in these files have public identifiers in themes/src/suntheme/properties/styles.properties

The staticText widget does not have any css related to it by default and hence we will not be touching any of these css files. Note that, the styles specified on the widget by the developer using the statictext widget will still be rendered on the page.

If the widget that you create has to show any images from the theme, it can be placed in the themes/src/suntheme/images/<widget-name> directory. Note that these image names have a public identifier key in the themes/src/suntheme/properties/images.properties file

Whenever we want to use these styles or images in the javascript code while creating the widget, we will refer to these entities using the public identifiers mentioned in the properties file.

3.3 Write the javascript widget code.

The javascript widget code creates the widget and renders it on to the page. Like mentioned earlier, we use the dojo's widget rendering framework to create the widgets. Here is a simplified lifecycle for widget creation.

File:WoodstockCustomComponents/moz-screenshot WoodstockCustomComponents.jpg

The javascript that we create for widgets are placed in the themes/src/suntheme/javascript/widget/ package. Most of the common functions required for the widget creation process are handled by the woodstock base javascript classes like eventBase.js and widgetBase.js The common functions that we use in the javascript widget code are placed in the common.js file. Most of the widgets inherit from the widgetBase.js There are widgets like hyperlink and anchor which inherit from a base anchorBase.js which in turn inherits from widgetBase.js

The theme name in the path is replaced with an "@THEME_JS@" EL expression in the javascript files. Here is a code snippet for the staticText widget's constructor.

You can find more information regarding the dojo.provide, dojo.require and dojo.declare statements in dojo's documentation.

@JS_NS@._dojo.provide("@JS_NS@.widget.staticText");

@JS_NS@._dojo.require("@JS_NS@.widget._base.refreshBase");
@JS_NS@._dojo.require("@JS_NS@.widget._base.stateBase");
@JS_NS@._dojo.require("@JS_NS@.widget._base.widgetBase");

@JS_NS@._dojo.declare("@JS_NS@.widget.staticText", [
        @JS_NS@.widget._base.widgetBase,
        @JS_NS@.widget._base.refreshBase, 
        @JS_NS@.widget._base.stateBase], {
    // Set defaults.
    constructor: function() {
        this.escape = true;
    },
    _widgetType: "staticText" // Required for theme properties.
});

You can define the defaults for the particular widget in the widget's constructor. In the above case, the escape property defaults to true. It also defines the widgetName for the widget. This widgetName is helpful while obtaining theme properties.

Each widget defines its own set of events that the widget can publish for other custom javascript code or other widgets to listen/subscribe to. The staicText widget publishes the "refresh" and "submit" events. The refresh event helps in refreshing the widget asynchronously whereas the submit event helps in maintaining the state of the widget.

There are two public functions that each widget has to expose for developers to access the widget properties and set properties on it. The getProps and setProps functions are exposed from the widget for this purpose. There is also a _setProps function which is used by the widget rendering framework to instantiate the widget.

Any code that you would like to be executed (such as providing ids for different attach points in the template) can be provided in the postCreate function.

Here is how the _setProps function looks like for the staticText widget.

@JS_NS@.widget.staticText.prototype._setProps = function(props) {
    if (props == null) {
        return false;
    }
      
    // Set text value -- an empty string is valid.
    if (props.value != null) {
        // NOTE: If you set this value manually, text must be HTML escaped.
        this._widget._addFragment(this._domNode, props.value, null, this.escape);
    }

    // Set more properties.
    this._setCommonProps(this._domNode, props);
    this._setEventProps(this._domNode, props);

    // Set remaining properties.
    return this._inherited("_setProps", arguments);
};


Note that you have not specified any setProps function in the widget code. This is defined in the widget's base class. You would assign ids to all subelements and any other instantiation stuff that you would want to do in the postCreate function. Iimlpement setProps and getProps. Important to note that both functions are tightly dependant, as setProps will always first get the existing properties from getProps. Thus BOTH getProps and setProps are to be implemented. Further, there is a need for private _setProps. Public setProps is used by the user, and private _setProps() is used directly by the constructor, and by default setProps delegates to _setProps() under the covers. Thus minimally, only _setProps needs to be implemented.The following diagram illustrates some idiosyncrasies of setProps/getProps:

File:WoodstockCustomComponents/moz-screenshot-1 WoodstockCustomComponents.jpg

3.4 Create the ajax handler for the widget

The next step is to create an ajax handler for the widget's events. The files should go into the themes/src/suntheme/javascript/widget/_jsfx folder.We use dynamicFaces for handling ajax events. Most of the code for handling default widget events such as refresh and submit are handled in the common.js base class from which the staticText widget's ajax handler inherits. So, you shouldnt have much of code over there.

@JS_NS@._dojo.subscribe(@JS_NS@.widget.calendar.event.refresh.beginTopic,
    @JS_NS@.widget._jsfx.common, "_processRefreshEvent");

The above code subscribes to the static text widget's refresh event's topic. The _processRefreshEvent is defined in the common.js file.


After all this is done, do a build of your webui/themes project and check whether your project compiles successfully.


4) Including the components in the Visual Web Palette.

Including the component in the visual web palette involves the following steps


4.1 Create HTML Renderer for the components.

The Visual Web designer cannot interpret javascript and hence you need to write an equivalent html renderer for the component that you are creating. Usually, you would put this into the webui/src/designtime/com/sun/webui/jsf/renderkit/html package. Since the earlier version of woodstock components rendered html, they shared the same html renderers for runtime and designtime.

The staticText component has its html renderer in webui/src/runtime/com/sun/webui/jsf/renderkit/html package.


The staticTextRenderer inherits from AbstractRenderer which acts as a base class for html renderers.

You can either use that and override the
- renderStart, renderEnd and renderChildren methods for rendering your html

- standard JSF methods such as encodeBegin and encodeEnd and extend from UIOutput class.

The designtime renderer for the component present at webui/src/designtime/com/sun/webui/jsf/renderkit/html package provides a default message to be shown when the staticText is first dropped on to the screen. All other rendering is taken care of the runtime's html renderer that we discussed above.

4.2 Create the BeanInfo and DesignInfo classes

You should use these classes to customize the designtime behavior for the staticText component. The constructor of the StaticTextBeanInfo class makes the staticText rendered on the designer page inline editable.

    public StaticTextBeanInfo() {
        BeanDescriptor beanDescriptor = super.getBeanDescriptor();
        beanDescriptor.setValue(
            Constants.BeanDescriptor.INLINE_EDITABLE_PROPERTIES,
            new String[] { "*text://span" }); // NOI18N
    }

4.3 Modifying configuration files

- Edit the webui/src/designtime/conf/faces-config.xml and include

   <renderer>
      <component-family>com.sun.webui.jsf.StaticText</component-family>
      <renderer-type>com.sun.webui.jsf.widget.StaticTextDesignTime</renderer-type>
      <renderer-class>com.sun.webui.jsf.renderkit.html.StaticTextDesignTimeRenderer</renderer-class>
    </renderer>

- Go to the woodstock\ide-plugins\netbeans\woodstock-suite\components\src\org\netbeans\modules\visualweb\woodstock\webui\jsf\designtime\resources directory and create a WoodstockStaticText.comp_palette_item file.

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

<palette_item version="1.0">
  <component classname="com.sun.webui.jsf.component.StaticText" type="visual" />
  <icon16 urlvalue="nbres:/com/sun/webui/jsf/component/StaticText_C16.png" />
  <icon32 urlvalue="nbres:/com/sun/webui/jsf/component/StaticText_C32.png" />
  <description localizing-bundle="org.netbeans.modules.visualweb.woodstock.webui.jsf.designtime.resources.Bundle"
               display-name-key="NAME_com-sun-webui-jsf-component-StaticText"
               tooltip-key="HINT_com-sun-webui-jsf-component-StaticText"
               help-key="projrave_ui_elements_palette_wdstk-jsf1.2_static_text"/>
</palette_item>

If you are creating a new component, you typically wont have the icons with you and so you can comment out the lines containing the icon elements.

- Edit the Bundle.properties file and add the following lines

#Static Text
NAME_com-sun-webui-jsf-component-StaticText=Static Text
HINT_com-sun-webui-jsf-component-StaticText=Outputs text, including HTML markup

The tooltip-key and help-key values present in the WoodstockStaticText.comp_palette_item file must match the strings in this file as shown above.

- Go to woodstock\ide-plugins\netbeans\woodstock-suite\components\src\org\netbeans\modules\visualweb\woodstock\webui\jsf\designtime directory and edit layer.xml

           <file name="WoodstockStaticText.comp_palette_item" url="resources/WoodstockStaticText.comp_palette_item">
                <attr name="position" intvalue="200"/>
            </file>

- Now follow the process described at:http://wiki.java.net/bin/view/Javatools/InstallingWoodstockComponentsInNetBeansto insert the components into the palette