All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jsftoolkit.doc.CodeGen.wiki.txt Maven / Gradle / Ivy

Go to download

The core classes for the JSF Toolkit Component Framework. Includes all framework base and utility classes as well as component kick-start/code-generation and registration tools. Also includes some classes for testing that are reused in other projects. They cannot be factored out into a separate project because they are referenced by the tests and they reference this code (circular dependence).

The newest version!
== Introduction ==

If you've never written a JSF component before, you should read [http://www-128.ibm.com/developerworks/java/library/j-jsf4/ this article] on JSF component development. After you have finished all 30 pages, sit back and take a deep breath.  Seems a little tedious right?  It's a good article, but it is impossible to make most of what goes into a JSF component seem like anything but drudgery.

Does it have to be that way? Absolutely not.  Most of the code necessary for a JSF component to work can be generated automatically, and doing so reduces the likelihood of introducing new bugs and keeps components behaving in a predictable way.

== Your First Component ==

=== The Renderer Template ===

At this point, all you need is a sufficient knowledge of (D)HTML to write an example of how your component should render. For example, suppose you want to render a box that can be shown/hidden with the click of a button. Your HTML might look like this:{{{
<script type="text/javascript">
function toggle(id) {
   var box = document.getElementById(id);
   if(box.style.display == 'none') {
      box.style.display = 'block';
   } else {   
      box.style.display = 'none';
   }
}
function toggleValue(elem,value1,value2) {
   if(elem.value == value1) {
      elem.value = value2;
   } else {
      elem.value = value1;
   }
}
</script>
<input type="button" value="Hide"
   onclick="toggle('exploder');toggleValue(this,'Show','Hide');" />
<div id="exploder">
   Some example content.
</div>
}}}

The first step in turning this HTML into a component is to identify what parts of the HTML are variable.  In this example, the ids and the contents of the box are what will vary.

To let the template compiler know that it should substitute in a component attribute, we replace the id with ${id}.  To tell where any child components of this tag should be rendered, we use the <children/> tag.{{{
<input type="button" value="Hide"
   onclick="toggle('${id}');toggleValue(this,'Show','Hide');" />
<div id="${id}">
   <children/>
</div>
}}}

If you are concerned about developers being able to  internationalize your component, you could also allow the 'Show' and 'Hide' text to be set on your component.  In this case, you also want to provide a default value in case the developer doesn't specify those attributes.  This is accomplished with the syntax ${attributeName|Default Value}, e.g. {{{
<input type="button" value="Hide"  
  onclick="toggle('${id}');toggleValue(this,'${showText|Show}','${hideText|Hide}');" />
<div id="${id}">
   <children/>
</div>
}}}

There is one last thing we should do before moving on.  If your page contains multiple toggle buttons, then the Javascript functions will be written out more than once. This normally wont cause a problem, but it is a waste of bandwidth to include them multiple times, so we should put them in their own file.  We're going to put both files into a package so they'll be easy to find. We chose com.jsftoolkit.example.

Contents of src/main/resources/com/jsftoolkit/example/ToggleBox.xhtml:{{{
<input type="button" value="Hide"
   onclick="toggle('${id}');toggleValue(this,'${showText|Show}','${hideText|Hide}');" />
<div id="${id}">
   <children/>
</div>
}}}

Contents of src/main/resources/com/jsftoolkit/example/toggle.js:{{{
function toggle(id) {
   var box = document.getElementById(id);
   if(box.style.display == 'none') {
      box.style.display = 'block';
   } else {   
      box.style.display = 'none';
   }
}
function toggleValue(elem,value1,value2) {
   if(elem.value == value1) {
      elem.value = value2;
   } else {
      elem.value = value1;
   }
}
}}}

Now that you've specified the rendering for your component, it's time to generate the component code.

=== Filling in the component information ===

There are two ways to describe the rest of your component. One is to create an XML file and the other is to create a class of constants.  The XML file will be described [[ComponentInfo|later]], for now we will use the constants approach because it is easier to refactor.

Regardless of approach, any component requires the following information:
; PACKAGE : The Java package your component belongs to. e.g. 'com.jsftoolkit.example'
; CLASS_NAME : Typically just the name of your component. e.g. 'ToggleBox'
; COMPONENT_TYPE : See the [http://jcp.org/aboutJava/communityprocess/mrel/jsr252/index.html JSF 1.2 Spec] for more details. In our case, we chose 'com.jsftoolkit.Box'. The important part is that it be qualified well enough to not collide with other libraries.
; COMPONENT_FAMILY : See the spec. We chose 'com.jsftoolkit.Toggle'. Technically this may be null for self rendering components, but most of our components will have a separate renderer.
; DEFAULT_RENDERER_TYPE : Which registered renderer should be used?  We will be generating and registering the renderer in a moment, so just pick a unique but descriptive identified, e.g. 'com.jsftoolkit.HtmlToggleBoxRenderer'
; TEMPLATE : This is the location of the template we just created as a class path resource, e.g. '/com/jsftoolkit/example/ToggleBox.xhtml'

Lastly, we need to include information about 'toggle.js', so that it can be loaded when our component is rendered.  The constant for includes is named 'INCLUDES' and is an array of com.jsftoolkit.base.ResourceInfo.  ResourceInfos constructor is: {{{
ResourceInfo(String id, String defaultResource, Type type, String encoding, String filter)
}}}
Where id is a unique identifier for your resource, generally a variation on it's location in the class path, defaultResource is the location in the class path of the resource, type is an enumerated type indicating whether the resource is CSS or Javascript, and encoding is the character encoding of the resource.  filter will be important when we start including stylesheets, but may be left null for now.

The resulting class looks like:{{{
package com.jsftoolkit.example;

import com.jsftoolkit.base.ResourceInfo;
import com.jsftoolkit.base.ResourceInfo.Type;

public class ToggleBoxSpec {
	public static final String COMPONENT_FAMILY = "com.jsftoolkit.Toggle";

	public static final String DEFAULT_RENDERER_TYPE = "com.jsftoolkit.HtmlToggleBoxRenderer";

	public static final String COMPONENT_TYPE = "com.jsftoolkit.Box";

	public static final String CLASS_NAME = "ToggleBox";

	public static final String PACKAGE = "com.jsftoolkit.example";

	public static final String TEMPLATE = "/com/jsftoolkit/example/ToggleBox.xhtml";

	public static final ResourceInfo[] INCLUDES = { new ResourceInfo(
			"com.jsftoolkit.example.TOGGLE_JS",
			"/com/jsftoolkit/example/toggle.js", Type.SCRIPT, "UTF-8", null) };

}
}}}


With that basic information, we have enough to generate the component, renderer, and tag handler.

=== Generating the code ===

Programmatically, the generation code operates on an instance of com.jsftoolkit.gen.info.ComponentInfo, so to invoke the code generator, you need to create such an instance. Fortunately, the class of constants we just created can be parsed by com.jsftoolkit.gen.ConstantsComponentInfoFactory. The following code will write the generated code to target/generated-sources/com/jsftoolkit/example/:{{{
package com.jsftoolkit.example;

import com.jsftoolkit.gen.ConstantsComponentInfoFactory;
import com.jsftoolkit.gen.TemplateComponentGenerator;
import com.jsftoolkit.gen.info.ComponentInfo;

public class ToggleBoxGenerator {
	public static void main(String[] args) throws Exception {
		ComponentInfo info = ConstantsComponentInfoFactory
				.parse(ToggleBoxSpec.class);
		new TemplateComponentGenerator().generate(info);
	}
}
}}}

=== Updating the configuration files ===

We can also update faces-config.xml, a TLD file, and even a Facelets taglib.xml file to register your component.  The constants that specify the location of these files are FACES_CONFIG, TLD, and TAGLIB_XML respectively.  We added the following lines to ToggleBoxSpec:{{{
	public static final String TLD = "src/main/resources/META-INF/example.tld";

	public static final String FACES_CONFIG = "src/main/resources/META-INF/example-config.xml";

	public static final String TAGLIB_XML = "src/main/resources/META-INF/example.taglib.xml";
}}}
And one line to the end of main in ToggleGenerator:{{{
		ConfigurationUpdater.getInstance().updateAll(info);
}}}
(you will need to import com.jsftoolkit.gen.ConfigurationUpdater)

If the above files do not exist, they will be created. If you do not specify the constants NAMESPACE, SHORT_NAME, and TAG_NAME, these values will be guessed (NAMESPACE and SHORT_NAME will only be used if the files do not already exist, or do not have short-name or namespace/uri elements).  If these files already exist, all existing contents will be preserved.  tag elements matching TAG_NAME will have their class/type/renderer information updated, but any extra elements, e.g. description, will be preserved.

==== One line Configuration ====

The above lines demonstrate just what is going on behind the scenes.  For any components that you want to generate everything and register, you can just write:
{{{
   ConstantsComponentInfoFactory.genAndUpdateAll(ComponentSpec1.class,ComponentSpec2.class,...);
}}}
Which will create/update configuration files in the default locations.  If you want to specify different locations, pass an instance of [javadoc/com/jsftoolkit/gen/info/ConfigInfo.html ConfigInfo] as the first argument, e.g.
{{{
   ConstantsComponentInfoFactory.genAndUpdateAll(
           new ConfigInfo( "META-INF/faces-config.xml", "META-INF/myComponents.taglib.xml", 
                   "META-INF/myComponents.tld"), ComponentSpec1.class, ComponentSpec2.class,...);
}}}

=== Using your components ===

There are two things you will need for your component library to work in a web application (presuming you have packaged your component nicely into a jar file):

 # the [http://shale.apache.org/index.html#download shale-remoting jar]
 # the JSF Toolkit base jar

Lastly, in any page where your component is going to be used, the jsfTk:htmlScripts component needs to be in the head element. e.g.
{{{
<html xmlns="http://www.w3.org/1999/xhtml"
   ...
   xmlns:jsfTk="http://jsftoolkit.com/base">
  <head> 
    <jsfTk:htmlScripts />
 ...
}}}
(The above is Facelets, the JSP is similar, but with <%@ taglib uri="http://www.jsftoolkit.com/base" prefix="jsfTk" %>)

htmlScripts is what loads resources (scripts and CSS) for components generated by the JSF Toolkit. It only needs to be included once per page, and doesn't need to be configured.




© 2015 - 2024 Weber Informatics LLC | Privacy Policy