 |
Writing JSF Custom Components for Sun IDEs
This page contains information on writing custom JSF components and
delivering them as a component library in a complib file for
NetBeans Visual Web Pack
(VWP) and
Sun Java Studio Creator IDEs. Please email comments to EdwinGoei. Thanks for the
feedback.
As of 2007-03-12, this document needs to be revised with information on
current IDE development which is occuring on the
NetBeans nbusers and nbdev mailing lists.
It is still somewhat useful in that it describes how components can be
assembled into a complib file. The original popup calendar sample code in
this article has been updated and incorporated into the
java.net blueprints project.
This document is directed towards third-party component developers instead
of the typical IDE web application developer user. We assume that the
developer has a deeper understanding of JavaServer Faces (JSF) and
general Java platform development. This information is for Creator version
2 and above including NetBeans 5.5 Visual Web Pack and above.
A set of components can be bundled together into a component library and
delivered to an IDE web application developer as a single complib file. A
complib file is just a package file much like a war or ear file
containing the contents of a component library.
Table of Contents
Overview
This document tries to take a concrete, by-example approach to
documentation. We will develop a simple complib containing both JSF
components and a converter. However, we also present an overview of the
major steps of the process along with links to details. You may want to
skip over these details upon a first reading of this document.
Creator uses the standard JavaBeans component architecture to
associate property editors, customizers, and other metadata with a
component at design-time. A JavaBean component or bean is a reusable
software unit that exposes its features such as properties, methods, and
events, to tools. Creator expects user-manipulatable objects to be beans.
This includes JSF components such as text fields and command buttons as
well as JSF converters and validators. It also applies to non-JSF
components such as DataProviders. Creator obtains metadata on these
beans by using BeanInfo classes and the CreatorDesignTimeApi.
In this document, "design-time" refers to parts of an application that are
only used by a tool or integrated development environment (IDE) such as
Creator. The word "runtime" refers to parts of an application that are
always deployed to a servlet container or application server. Some runtime
parts may also be used in a tool at design-time. The following list
outlines the major steps for adding third-party components to Creator.
- Creating the runtime components themselves taking into consideration design-time aspects since some of the code will also be executed at design-time. Components should also be beans and should meet some guidelines before they will work well in Creator. Adapting existing JSF components for use in Creator should also follow these same guidelines. For details, see CreatorComponentRules.
- Creating the design-time artifacts for the components. The design-time artifacts include JavaBeans BeanInfo classes, property editors, customizers, icons, and other resources as well as classes that are part of the CreatorDesignTimeApi.
- All the parts: the runtime components, the design-time code, icons, javadoc, source, and a
complib manifest need to be assembled into a complib file. For details, see ThresherComplibSpec.
- The user imports the
complib into Creator and uses the components in an application.
Component developers should perform the above steps iteratively until they
are satisfied with the results.
Sample Simple Component Library
We will illustrate the major steps of the process by example. We will
develop a simple complib containing JSF components and a converter. This
new sample is an evolution of the
older sample-date complib
but has been updated. Currently, it contains a date picker component, a
DHTML popup calendar component, and a combined java.sql.Date and
java.util.Date converter along with some design-time classes. Eventually,
the plan is to add other non-date related components so I have also renamed
the complib to make it more generic.
If you are writing your own JSF component or modifying an existing
component to work in Creator, the popup calendar is probably the better
component to use as a template. It is an example of a more complex
component to illustrate how one can add resources such as JavaScript
and CSS files to a complib. One of my product responsibilities was to fix
problems in the current Creator built-in calendar component. I realized
that this component needed to be refactored so I thought I would write a
new version of a popup calendar as a real-world example. So I started by
doing a survey of existing date-picker and popup calendar type of
components on the web and came up with a set of requirements. I think I
have come up with an improved prototype which can be the basis of a new
product-quality popup calendar. Feel free to modify it and extend it to fit
your needs. For screenshots, see
this blog entry.
You can obtain the following files using the Creator auto update center
(Tools->Update, I think):
This file will be pla
The following files are obsolete and will be removed in the near future:
Ideally a tool should support any JSF component and this is also Creator's
long-term goal. However, often times this requirement conflicts with
ease-of-use for the user. Creator was originally targeted for
ease-of-use and so full generic JSF support is being delayed for future
releases. For example, Creator allows the user to drag and drop components
onto a design canvas. In order to support this, the components themselves
must follow a more restrictive set of rules to work well in Creator. See
CreatorComponentRules for more information. This information is also useful
when modifying an existing component to work well in Creator. In this
sample, however, we develop components from scratch.
Parts of a Component Library
Creating a component library involves creating various parts and then
packaging these parts into a single complib file. A complib may contain
several parts:
- Runtime jars contain classes and metadata that are deployed to a server at runtime as part of the application. Runtime jars are also used at design-time.
- Design-time jars contain classes and metadata that are used by the IDE itself and are not deployed at runtime.
- Javadoc zip files that contain javadoc used by Creator users. Not deployed.
- Source files that contain Java code used by Creator users. Not deployed.
- Help information that is used by Creator users. Not deployed. (Not currently implemented 2006-01.)
- A complib manifest which points to a configuration XML file to specify the complib package contents. For example, META-INF/MANIFEST.MF and complib-config.xml.
Creator components do not necessarily have to be JavaServer Faces
(JSF) components but they all have to be beans. For example,
DataProvider-s are beans but they are not JSF components. Going back
to JSF, a "JSF component" is a subclass of UIComponent and is
sometimes referred to as a "visual component" because it typically has code
to render markup for display in a user agent. For visual components,
Creator displays the rendered markup in the designer at design-time. This
leads to the use of the term "non-visual component" for other types of
beans.
Creating a runtime jar is a good place to start. The runtime artifacts are
typically packaged in one or more jar files. In our sample, we have a JSF
converter which is a non-visual component. Typically non-visual components
can be implemented by hand. However, visual components are typically more
complex. A visual component can be further divided into three kinds of Java
classes plus some metadata:
- Metadata: sample-simple.tld (tag library descriptor)
- The UIComponent itself: DatePicker.java, PopupCalendar.java
- Its Renderer: DatePickerRenderer.java, PopupCalendarRenderer.java
- Its tag handler: DatePickerTag.java, PopupCalendarTag.java
Writing a visual JSF component typically involves creating several classes
and a config file many of which are boilerplate and can be derived from the
same set of metadata. Instead of manually trying to keep the different
parts of a component in sync, one approach is to keep a single set of
metadata and provide a code generator to derive the boilerplate classes and
config file. This is the approach we will take here. Our sample has
faces-config.xml and sun-faces-config.xml files containing metadata which
is used as an input into a code generator to produce both runtime and
design-time artifacts. Please note that the code generator is optional, if
you have problems running the code generator for your own components, you
can view the output of the generator for this sample to figure out how
to manually write your own code.
Some of the design-time artifacts are typically packaged in a design-time
jar and can also be further divided:
- Design-time classes: PopupCalendarBeanInfo.java describes the PopupCalendar bean. SqlUtilDateConverterBeanInfo describes the SqlUtilDateConverter bean. PopupCalendarDesignInfo.java supports dynamic design-time behavior.
- Other code: such as PropertyEditor-s: YearAlignPropertyEditor.java
- Other resources: PNG icon for DatePicker
Other optional files such as javadoc and source code may also be packaged
in zip files and allow the IDE to provide javadoc and source during
development. Finally, a catalog containing all of the previously described
parts of a complib allows the IDE to determine its contents.
Creating Runtime Jar(s)
Let's start by creating a runtime jar which contains JSF components. The
converter can be manually implemented as a bean without any properties. For
visual components, there are typically three associated kinds of
runtime classes: the UIComponent class itself, the tag handler class, and
the renderer. Much of the UIComponent code is boilerplate and can be
automatically generated. Similarly, the tag handler follows a boilerplate
pattern and can also be automatically generated. In fact, no special
component code should be in the tag handler since current versions of
Creator do not execute tag handlers at design-time. Of the three, the
renderer is what gives the component its unique character and is thus
typically hand-written. The TLD can also be automatically generated.
Running the Code Generator
A JSF runtime requires a faces-config.xml file to determine the types of
components, converters, validators, and renderers for an application. The
schema for this file is extensible and Creator extends this schema to allow
additional metadata which can be used as input to a code generator.
Typically, this extended metadata file is called sun-faces-config.xml and
follows the SunFacesConfigDtd schema. Since faces-config.xml is required by
a JSF runtime, only the minimal metadata required for runtime is included
in that file. The sun-faces-config.xml contains all other information.
Let's look at the sample faces-config.xml file. Notice it has minimal
information about the date picker component and its renderer. When creating
a new component, add entries to this file first. Next, look at the the
sun-faces-config.xml file. Notice this file contains the component's
description, tag-name, taglib-uri, base-component-type and instance-name as
well as all its properties. Also, each property contains its own
description and other metadata such as its display name, type, JavaBeans
property editor, and category in the property inspector.
The metadata in the sun-faces-config.xml file will be used to generate
- Component base classes containing concrete properties and value bindings. For example: DatePickerBase.java
- Taglib handler classes containing code to collect JSP attributes and set their values on properties in component classes. For example: DatePickerTag.java.
- Taglib TLD file describes to the JSP compiler in a container how to process the tags in a JSP page. For example: sample-simple.tld.
- JavaBeans BeanInfo base classes contain metadata on a component. For example: DatePickerBeanInfoBase.java.
See the "gen-all" target in the ant build.xml file for details on running
the Creator code generator with the sun-faces-config.xml as input.
Warning: the code generator interface is not stable and is likely to
change in the future.
Runtime: Final Steps
After running the code generator, we need to manually write the actual
renderer and component classes themselves. For details, see the code in the
following files:
- Renderer class: DatePickerRenderer.java
- Component class: DatePicker.java which extends DatePickerBase
Finally collect all the runtime classes, resources, and faces-config.xml
file and create the runtime jar. See the "jar-rt" ant target.
Design-time Jar(s)
The next major step is to create a design-time jar. As mentioned
previously, the design-time jar contains BeanInfo classes and other
items that are not deployed to a container but are only used by a tool. The
"gen-beaninfo" target, which the "gen-all" target mentioned in the previous
section runs, also generates BeanInfo base classes. These base classes
need to be extended to create BeanInfo classes. Warning: if you
forget to extend the BeanInfo base class, your component will not work
correctly because the JavaBeans Introspector will return a runtime
synthesized BeanInfo instead. Creator uses BeanInfo classes as
the exclusive source of static metadata about components. See
DatePickerBeanInfo.java for more information. Note how an icon can be
associated with the component by adding an image file and how
PropertyDescriptor-s can be manipulated.
Another kind of item contained in a design-time jar are DesignInfo
classes. DesignInfo classes provide dynamic behavior rather than
static information about a component. In our sample,
DatePickerDesignInfo.java shows how a property can be changed at
design-time when a new instance of a date picker is added to a page. The
sample also shows how a DateUtilConverter can be linked to a
DatePicker using the CreatorDesignTimeApi. This API extends the
standard JavaBeans API and adds a rich set of new features to
manipulate components and properties at design-time and provides the IDE
with context menu items, customizers, and extended property editors.
Other items that are typically placed a design-time jar are
PropertyEditor-s, category descriptors, and localization resource
bundles. For example, in the DatePickerBeanInfo, the DatePicker
"yearAlign" property is associated with the
"com.example.util.YearAlignPropertyEditor" which is also included in the
design-time jar. For details, see the "jar-dt" target in the sample
build.xml file.
Other Items
There are other optional items that are useful to Creator users and may be
included in a complib.
- Javadoc: see the "javadoc" and "complib" ant targets
- Source files: see the "complib" ant target which shows how a zip file of source can be included
- Help: eventually help files will also be supported
Assembling the Complib File
The final steps are to assemble the complib file. First, create a jar
manifest file containing a pointer to an XML config file. In this example,
complib-config.xml contains all metadata about the complib itself. See
ThresherComplibSpec for more information on the schema for this file. In
our example, the conf/complib-config.xml file contains a pointer to a Java
property ResourceBundle for internationalization and among other
things explicity declares component classes within palette categories.
Note: it also uses filter expressions which are replaced by the ant copy
task. Finally, the complib file itself is created with the jar ant task.
See the "complib" target for details.
Test your component library as follows:
- Open the Component Library Manager dialog by selecting Tools->Component Library Manager from the menu bar or selecting "Manage Component Libraries" from a palette category context menu
- Click on the "Import..." button in the dialog which will open another dialog
- Browse to your
complib file and import it
- Add a component from the newly imported
complib to test your component
Importing a complib will cause Creator to expand the package and add new
components into the user palette. You may need to open a palette category
to see the new components. The expanded complib will be copied to a
location under the current user directory, ~/.Creator/complibs/. (This is
not a stable location.) Test a component by adding a component to the
design surface. The first time this happens, the IDE will copy the unpacked
complib into the project. Also a new Library Reference in the project
will be created. You can see the added Library Definitions and References
by selecting the Properties context menu item of the main project's node in
the Project navigator.
Notes and Tips
- Whenever a new version of a component library is imported into Creator, it is stored under the Creator userdir and does not affect any existing projects that may use older versions. This is because each project has its own copy of a complib. A complib is first copied to a project when a component in it is first used in a project. See more details here.
- When developing a component iteratively, it is often useful to change some code, generate a new complib, import it into Creator, and test the new code within an existing project. To force the old project complib to be overwritten, you can add a component from the newly imported complib displayed in the palette to a page in the project and then undo the add. This will cause the new complib to overwrite the older complib in the project even when both complibs have the same identifier (uri, version pair). Creator uses timestamps to implement this behavior.
- To completely remove a complib from a project, as opposed to the userdir, and without replacing it with a different version, see this link. A better user interface for this feature is planned.
- Until a more functional Java standard design-time API is agreed upon (JSR-273), third parties can use the Creator Design-Time API that allows a richer design-time experience. See CreatorDesignTimeApi for more information.
- A command line property can be set to specify the initial complib import browse directory. For example, on Windows you can open a dos prompt and execute:
C:\Sun\Creator\bin\runide.exe -J-Dtoolbox.importStartDir=/cygwin/home/edwingo/tmp.
- New in Creator 2 is the ability to auto-install a
complib by placing it in a special directory $RAVE_INSTALL_HOME/complibs_to_install/. Upon the next restart of the tool, the complibs will automatically be installed into the user's palette.
- If you develop a visual JSF component using a similar procedure as the sample above and adding your component to a project makes it appear in the Application Outline but not in the designer and no exceptions are shown in the log, check that you have a BeanInfo class that extends the generated BeanInfoBase class.
- If you see the following warning message in the IDE log file, it can be safely ignored. The latest version of the sample should not have this problem. Ignore message: "You are trying to access file: complib-config.xml from the default package...".
- Existing generic JSF components such as myfaces and ourfaces that have not been modified to work with Creator will not work without additional effort. However, it may be possible to get some components to work by adding additional design-time code and in other cases by also modifying component runtime code. They can then be packaged into a
complib file. See CreatorComponentRules for guidelines.
Problems with this Sample
The following lists known problems with the sample code. Problems will be
addressed in a future version of the code. Please report any problems to
EdwinGoei.
- Date properties "year", "month", and "day" do not always update properly in the property inspector. Click on the property to see it update. These are also read-only properties and can currently only be accessed using Java code getter methods.
Open Issues
- The code generator command line interface is not stable and is likely to change in the future. This means that basing your component library build on the sample ant file may not work in future versions of Creator. The generated code itself should be more stable, however.
- The sample-simple complib uses non-portable but useful internal PropertyEditor-s. These may not be available in a future release. The workaround is to write your own PropertyEditor-s.
- Some PropertyEditor-s in sample-simple rely on a NetBeans API to alert the user when a String cannot be parsed. This may be a Creator bug. I will try to resolve this issue. The workaround is either to use the NetBeans API as done in the sample or to catch exceptions within the PropertyEditor itself. The latter solution unfortunately does not inform the user of a problem.
References
Troubleshooting
- If you try to add a new visual component from the palette to a page in the IDE and expect something to appear on the page and instead only see the component in the Outline view, then this often means that the associated custom BeanInfo class of the component cannot be instantiated. In reality, a default BeanInfo class is created internally by introspection, but the default is not very useful. One way to check this is to write a unit test that instantiates the BeanInfo classes of your components.
Updates
- 2007-04-12 Added #TroubleshootingAnchor section.
- 2007-03-12 Updated Intro with newer info on VWP.
- 2007-03-03 Moved Updates section to bottom.
- 2006-06-12 Added note that this document needs to be revised.
- 2006-03-23 Added tips on copies of complibs in projects and how to remove them.
- 2006-03-10 Added #DownloadAnchor
- 2006-03-06 Draft 2.4: sample-simple-2.2.1.complib: fixed bugs with popupCalendar when embedded in a table.
- 2006-03-02 Draft 2.3 Added more complex popupCalendar component to complib version 2.2 to illustrate capabilities such as adding JavaScript and CSS.
- 2006-02-08 Draft 2.2.2 Clarified visual vs non-visual components
- 2006-02-06 Draft 2.2 Minor updates
- 2006-02-03 Draft 2.1 in progress
- 2006-02-02 First Creator 2 (Thresher) draft.
- 2006-01-30 Started updating documents for Creator 2 (Thresher). Watch this space for more info. Until this update is completed, see the previous version.
- 2005-07-29 Minor updates.
- 2005-07-28 Fixed sample-date-2.0 example
complib and updated docs.
- 2005-07-26 Draft based on old Reef version of this document at WritingCustomComponents.
- 2005-07-26 Created
|