Differences between revisions 4 and 5
Revision 4 as of 2007-09-26 19:48:59
Size: 33233
Editor: c-24-9-183-90
Comment:
Revision 5 as of 2009-09-20 23:01:34
Size: 33233
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 6: Line 6:
[[TableOfContents]] <<TableOfContents>>
Line 862: Line 862:
  * ["Facelets with Trinidad"]: Setting up Trinidad with Facelets   * [[Facelets_with_Trinidad]]: Setting up Trinidad with Facelets

This page provides a simple step-by-step tutorial on how to use the Trinidad maven-faces-plugin. It is specific to those using maven, MyFaces 1.1.x, Facelets 1.1.x and Trinidad 1.0.x.

TOC

Overview

The Trinidad project is currently (2007-09-26) working on the 1.2.3 version of the unified maven-faces-plugin. This plugin builds components, JSP tag classes and builds configuration files for use with JSF. It supports Trinidad style components or "normal" JSF style components.

This WIKI page will show you how to get up and running using this plugin. The current version of this page demonstrates using JSF 1.1 with Trinidad style components and facelets. It also shows how to use the jsf-comp facelets annotation deployment to make registration of renderers easier.

Requirements

The following libraries have been used in this example, you may change them, but this may require changes to the settings. It is assumed that you are using Maven2.

Project dependencies

groupId

artifactId

version

org.apache.myfaces.core

myfaces-api

1.1.5

org.apache.myfaces.core

myfaces-impl

1.1.5

com.sun.facelets

jsf-facelets

1.1.12

org.apache.myfaces.trinidad

trinidad-api

1.0.3-SNAPSHOT

org.apache.myfaces.trinidad

trinidad-impl

1.0.3-SNAPSHOT

net.sf.jsf-comp

jsf-comp-facelets-deployment

0.2.1

Note: this is not a comprehensive list

Maven plugins

groupId

artifactId

version

org.apache.maven.plugins

maven-jar-plugin

maven-surefire-plugin

maven-compiler-plugin

org.apache.myfaces.trinidadbuild

maven-faces-plugin

1.2.3

Additional Maven repositories

ID

URL

apache-snapshots

http://people.apache.org/repo/m2-snapshot-repository

jsf-comp.sf.net

http://jsf-comp.sf.net/maven2/repository

Project Architecture

The maven-faces-plugin forces some architectural decisions. The reason for this is that it is generating Java source files. As a result, your code cannot depend on any of these generated files from within the project that is generating them. The best thing to do is mirror the Trinidad project layout. For this example, here is the projects we will create:

faces-generated-config
Contains the XML files to define the Java objects that the Trinidad plugin should be building
faces-api
This project will contain the generated classes and has any classes that are shared by the api, impl and web projects
faces-impl
This will contain the generated config files and any faces classes that use the API (like renderers, tag handlers, etc.)
web
This contains your WAR project with XHTML files, business classes, web.xml and other files

The faces-generated-config is required to be a stand alone project and you will not want to have any java files in it. The faces-api project will use these generated files to build the components, events and other java files that the plugin generates. Note, that in the Trinidad plugin layout, this is called trinidad-build.

There is no requirement to have the faces-impl in its own project, but I felt that it was a better separation of Faces classes and the business classes in the WAR project.

Project directories

faces-generated-config/
  src/
    main/
      resources/
        META-INF/
          maven-faces-plugin/
            (XML files)
  pom.xml
faces-api/
  src/
    main/
      java-templates/
        (Java package folders)/
          (Component name)Template.java
      java/
        (Java package folders)/
          (Java files)
      resources/
        META-INF/
          MANIFEST.MF
  pom.xml
faces-impl/
  src/
    main/
      conf/
        META-INF/
          (prefix)-base.taglib.xml
          faces-config-base.xml
      java/
        (Java package folders)/
          (Java files)
      resources/
        META-INF/
          MANIFEST.MF
web/
  src/
    main/
      java/
        (Java package folders)/
          (Java files)
      resources/
        META-INF/
          MANIFEST.MF
    webapp/
      WEB-INF/
        faces-config.xml
        trinidad-config.xml
        trinidad-skins.xml
        web.xml
      skins/
        (Trinidad custom skin structure)
      (xhtml files)
pom.xml

Step By Step Project Instructions

You may want to use the maven2 setup tools to build the projects to start with. Except for the web project, just use the standard archetype. This is beyond the scope of this page.

faces-generated-config

Maven project that contains the XML configuration files with the required information for the plugin to generate the Java classes.

pom.xml

Add these elements to the standard jar project pom.xml:

<project>
...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.myfaces.trinidadbuild</groupId>
        <artifactId>maven-faces-plugin</artifactId>
        <version>1.2.3</version>
        <configuration>
          <typePrefix>com.mysite</typePrefix>
          <packageContains>com.mysite</packageContains>
          <jsfVersion>1.1</jsfVersion>
          <force>false</force>
          <excludes>
            <exclude>**/includes/**</exclude>
          </excludes>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>generate-master-faces-config</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
...

This sets up the plugin to find the XML files in the default directory (src/main/resources/META-INF/maven-faces-plugin/). The structure of the sub-directories is unimportant.

What is important, is the above exclude definition. This allows you to have re-usable XML files to pull elements from that will not be included. The common usage for includes are shared attributes (for example, javascript events - onclick, onmouseover, etc.)

Make sure you change the value of the typePrefix and packageContains element values to match the component types and java packages for your site.

Building the XML Files

Component Files

To illustrate creating a component file, we will abuse the Hello World project and setup a component that takes a name and has a renderer that prints hello to that user.

To have much more complex examples, see the trinidad-build source tree from Apache SVN.

Create the directory src/main/resources/META-INF/maven-faces-plugin/components/

HtmlHelloWorld.xml

<?xml version="1.0" encoding="utf-8"?>
<faces-config
  version="1.1"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:my="http://mysite.com/components"
  xmlns:xi="http://www.w3.org/2001/XInclude"
  xmlns:mfp="http://myfaces.apache.org/maven-faces-plugin"
  xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <component>
    <description>
      <![CDATA[Says hi to someone]]>
    </description>
    <component-type>com.mysite.HelloWorld</component-type>
    <component-class>com.mysite.faces.component.example.HtmlHelloWorld</component-class>
    <property>
      <description>
        <![CDATA[Style class]]>
      </description>
      <property-name>styleClass</property-name>
      <property-class>java.lang.String</property-class>
    </property>
    <property>
      <description>
        <![CDATA[inline style]]>
      </description>
      <property-name>inlineStyle</property-name>
      <property-class>java.lang.String</property-class>
    </property>
    <property>
      <description>
        <![CDATA[Name of the person to say hi to]]>
      </description>
      <property-name>name</property-name>
      <property-class>java.lang.String</property-class>
      <default-value>world</default-value>
    </property>
    <component-extension>
      <mfp:component-family>com.mysite.HelloWorld</mfp:component-family>
      <mfp:implementation-type>trinidad</mfp:implementation-type>
      <mfp:component-supertype>org.apache.myfaces.trinidad.CollectionBase</mfp:component-supertype>
      <mfp:component-superclass>
        org.apache.myfaces.trinidad.component.UIXComponentBase
      </mfp:component-superclass>
      <mfp:renderer-type>com.mysite.HelloWorld</mfp:renderer-type>
      <mfp:tag-name>my:helloWorld</mfp:tag-name>
    </component-extension>
  </component>
</faces-config>

This is just a simple example of the properties that can be used. If you need a custom tag handler, set a <mfp:tag-handler>class name</mfp:tag-handler> inside the <component-extension/> element.

Note that everything is very important in this file. For example, the "my:" prefix in the <tag-name/> element directly references the declared namespace at the top of the file (xmlns:my="http://mysite.com/components"). If you don't have the namespaces declared exactly correct you will have problems of the component not being generated, and most of the time you will get no message telling you why.

For version 1.2.3 of the faces-maven-plugin, you will only be able to use Trinidad style components, this is because JSF 1.1 is not supported by the MyFaces component generator. The way the Trinidad generator creates components, they are able to work in a 1.1 or 1.2 environment.

Event Files

TODO, please feel free to contribute

Converter Files

TODO, please feel free to contribute

Validator Files

TODO, please feel free to contribute

Render Kit Files

TODO, please feel free to contribute

Renderer Files

TODO, please feel free to contribute

faces-api

Maven JAR project that will contain the generated Java files and should house any of your Java classes that you wish to have these components use. Think of this as your shared classes.

pom.xml

Add these elements to the standard jar project pom.xml:

<project>
...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.myfaces.trinidadbuild</groupId>
        <artifactId>maven-faces-plugin</artifactId>
        <version>1.2.3</version>
        <configuration>
          <typePrefix>com.mysite</typePrefix>
          <packageContains>com.mysite</packageContains>
          <jsfVersion>1.1</jsfVersion>
          <force>false</force>
          <excludes>
            <exclude>**/includes/**</exclude>
          </excludes>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>generate-components</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

This addition is exactly like the one for the generated configuration project, but the goal is different. Here we are instructing the plugin to build the component Java classes. We have not included any configuration files because those are going to be included in the faces-impl project instead.

Since we are using Trinidad for this example, we need to support JDK 1.5 at least, I chose 1.6. Make sure mvn uses the correct JDK version:

      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>

If you want to have a custom MANIFEST.MF file, you need to tell mvn to use it. This is required if you wish to use the jsf-comp facelets annotations:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
          </archive>
        </configuration>
      </plugin>

Make sure you add your configuration project as a depenency:

  <dependencies>
    <dependency>
      <groupId>com.mysite</groupId>
      <artifactId>faces-generated-config</artifactId>
      <version>${project.version}</version>
    </dependency>

Here I am assuming that you are using the same version for all of your projects, so I used the project current version here so you don't have to update the dependency when you bump the version of your projects.

Remember to add any dependencies that are required to compile the Java code. This will include the generated files, the *Template.java files as well as any source files in src/main/java/.

Adding Code To The Generated Components

Chances are that when you start making more complex components than our HelloWorld component, you will want to add code to the generated file. There are many reasons, but here are some possible use cases:

  • Handle the decode method
  • For components without renderers, implement the encode* methods
  • Override default behavior of the process* methods
  • Override the queueEvent or broadcastEvent functions
  • Etc.

In order to do this, you need to create a template file. This file will include an abstract Java class. The plugin will take the contents of this file and merge it into the final Java source file.

If we had wanted to do this for our hello world component, we would create src/main/java-templates/com/mysite/faces/component/example/HtmlHelloWorldTemplate.java. Notice that the package structure and the file name are exactly the same as the value from the HtmlHelloWorld.xml file from above (Element's XPATH of /faces-config/component/component-class) except for the addition of the "Template" in the name.

Here is what this class may look like:

   1 package com.mysite.faces.component.example;
   2 
   3 import org.apache.myfaces.trinidad.component.UIXComponentBase;
   4 
   5 public abstract class HtmlHelloWorldTemplate
   6   extends UIXComponentBase
   7 {
   8   // functions here
   9 }

This WIKI page is not meant as a tutorial on how to build these files, but you should know that Lines that start with /**/ will not be included. This is extremely helpful if you need to call a generated method. For example, if we had wanted to call the generated getName function from the encodeBegin, we could do so like this:

   1 /**/  public abstract String getName();
   2 
   3   @Override
   4   public void encodeBegin(javax.faces.context.FacesContext facesContext)
   5     throws java.io.IOException
   6   {
   7     String name = this.getName();
   8   }

We can do this because we know that one of our properties was "name" and that it was of type java.lang.String and that the plugin will generate this getName() function as a result. If we did not include the /**/ at the beginning of the line, we would have a javac compile error.

Including Support For Facelets Annotations

As a side note, if any of your files in src/main/java/ want to be deployed using the jsf-comp facelets annotations, you need to include the support in the manifest of this projects JAR file. You can do this by creating a src/main/resources/META-INF/MANIFEST.MF file with the following contents:

Manifest-Version: 1.0

Name: net/sf/jsf-comp/facelets/deployment/
Scan: true
RegisterPriority: FORCE

This tells the facelets annotation deployment that you wish to scan this JAR and, in this example, that you wish to force the registration of classes with the Faces Application.

Checking the Output

At this point you are ready to run the compile for your config and API projects if you wish. Run mvn install from the faces-generated-config project directory and mvn compile from the faces-api project directory. If you look in faces-api/target/maven-faces-plugin/main/java/ you will see the Java source files that have been generated. You may also have noticed that these files have automatically been compiled to the target directory with your normal source without any additional pom configuration.

Have a look at the HtmlHelloWorld.java file to see what the generated component looks like.

A Word On Dependencies

You may have classes that need to talk to your generated components inside the API project. This could be because one component needs to reference another, for example. This is not easy since these files have not been generated yet. If you not using an IDE, it should work (I haven't tried it though) to have the reference in the file, since the component generation should proceed any Java compilation. However, I think it is a cleaner solution, especially with the usage of an IDE, to use interfaces or abstract classes. Any interfaces that your Template class implements, will also be implemented by the generated Java class. So in our example, the HtmlHelloWorldTemplate class could extend a HelloWorld interface that may look like:

   1 package com.mysite.faces.component.example;
   2 
   3 public interface class HelloWorld
   4 {
   5   String getName();
   6 }

You would then change the definition of the template to:

   1 ...
   2 public abstract class HtmlHelloWorldTemplate
   3   extends UIXComponentBase
   4   implements HelloWorld
   5 ...

Note that you no longer need the /**/ public abstract String getName(); function in the template since the interface now defines this method.

From any class that would normally want to reference the HtmlHelloWorld class, have it instead reference the HelloWorld interface instead. If this is not enough, and you want to have a class reference instead, you can use a base class. One reason for this is that you don't want to have to cast a the interface to UIXComponentBase or UIComponent from the referencing code. To do this, you could create HtmlHelloWorldBase and then change the HtmlHelloWorld.xml file to extend this class instead of UIXComponentBase.

Note that this brings up an important point, the plugin uses the super class from the XML file and not from the template. That means that you could (although please don't) have the template extend a class that is different from the one in the XML file.

faces-impl

This project will house the generated configuration files as well as any Java files that reference the generated components. This is where you will want to put your renderer classes as well as your facelet custom ComponentHandler implementations.

pom.xml

We need to include the plugin again:

      <plugin>
        <groupId>org.apache.myfaces.trinidadbuild</groupId>
        <artifactId>maven-faces-plugin</artifactId>
        <version>1.2.3</version>
        <executions>
          <execution>
            <goals>
              <goal>generate-faces-config</goal>
              <goal>generate-facelets-taglibs</goal>
              <goal>generate-renderer-map</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <taglibs>
            <my>http://mysite.com/components</my>
          </taglibs>
          <faceletHandlerClass>com.mysite.faces.component.BaseComponentHandler</faceletHandlerClass>
          <typePrefix>com.mysite</typePrefix>
          <renderKitPrefix>com.mysite</renderKitPrefix>
          <packageContains>com.mysite</packageContains>
          <removeRenderers>true</removeRenderers>
          <force>false</force>
        </configuration>
      </plugin>

As you can see, this configuration is more complex for this project. The one that is possibly the easiest to make a mistake with is the <taglibs/> children elements. In this snippet, I have declared the namespace of my to be http://mysite.com/components. This must be exactly the same namespace from the XML file and the same prefix from that file as well.

Looking back, you will see the namespace from the XML file had xmlns:my="http://mysite.com/components". Also in the XML file was the tag name declaration of <mfp:tag-name>my:helloWorld</mfp:tag-name>. Include all the namespaces for each of your components here so that each taglib.xml files is generated. In our example, we have just the one.

I have shows a custom facelet handler class that will be used by default, if the component <tag-handler/> is not defined. You can create your own (as shown), or just use the standard Trinidad one (org.apache.myfaces.trinidadinternal.facelets.TrinidadComponentHandler).

Once again, also add the maven-compiler-plugin:

      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>

Once again, if you choose to use the jsf-comp facelets annotation deployment library, specify a custom manifest:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
          </archive>
        </configuration>
      </plugin>

Add your dependencies, including these:

    <dependency>
      <groupId>com.mysite</groupId>
      <artifactId>faces-api</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>com.mysite</groupId>
      <artifactId>faces-generated-config</artifactId>
      <version>${project.version}</version>
    </dependency>

You really shouldn't need the reference to the config project as it should be brought in by maven as a child dependency of the API project, but I like to be explicit here.

Customizing the Facelets Tag Library

You may want to customize the contents of the facelets tag library to include more content than just what is generated by the plugin. To do so, create a "base" XML. For our hello world example, we would want to extend the my.taglib.xml file. Do this by creating a faces-impl/src/main/conf/META-INF/my-base.taglib.xml. Start with this content:

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">

<facelet-taglib xmlns="http://java.sun.com/JSF/Facelet">
  <namespace>http://mysite.com/components</namespace>
</facelet-taglib>

The default namespace (http://java.sun.com/JSF/Facelet) is very important to include. Without it the XML include that is performed by the plugin will not work correctly. Also note that the namespace here matches the one from the XML file.

Important note
you cannot use library-class with this namespace

Customizing the faces-config.xml File Contents

If you wish to include additional XML into the generated faces-config.xml, it is done in a manner very simpilar to the taglib file. Generate a file faces-impl/src/main/conf/META-INF/faces-config-base.xml. Here are the contents you can start with:

<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" >
  <application>
    <default-render-kit-id>org.apache.myfaces.trinidad.core</default-render-kit-id>
  </application>
</faces-config>

I'm not sure that the default render kit is installed by the plugin, so I explicitly set it here. I know that the xmlns definition is not the same as the one for the JSF 1.1 faces-config.xml, this is intentional. The http://java.sun.com/xml/ns/javaee namespace is the one the plugin is looking for. The generated file will have the correct one of http://java.sun.com/JSF/Configuration.

Important note

in the 1.2.3 version of the plugin, this contents of this file is also filtered. That means that any components here must share the component type as the prefix from the plugin or else they will be stripped from the generated value. I have requested this be changed in JIRA issue https://issues.apache.org/jira/browse/TRINIDAD-741

Building the Renderer

The next big step is to build the renderer for our hello world component. I will use the Trinidad rendering design for it, but you don't have to (that is, using FacesBean and extending a Trinidad renderer).

Source of faces-impl/src/main/java/com/mysite/component/example/HelloWorldRenderer.java:

   1 package com.mysite.component.example;
   2 
   3 import java.io.IOException;
   4 
   5 import javax.faces.component.UIComponent;
   6 import javax.faces.context.FacesContext;
   7 import javax.faces.context.ResponseWriter;
   8 
   9 import net.sf.jsfcomp.facelets.deploy.annotations.FacesRenderer;
  10 
  11 import org.apache.myfaces.trinidad.bean.FacesBean;
  12 import org.apache.myfaces.trinidad.bean.PropertyKey;
  13 import org.apache.myfaces.trinidad.bean.FacesBean.Type;
  14 import org.apache.myfaces.trinidad.context.RenderingContext;
  15 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.XhtmlConstants;
  16 import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.XhtmlRenderer;
  17 
  18 @FacesRenderer(componentFamily = HtmlHelloWorld.COMPONENT_FAMILY,
  19   rendererType = "com.mysite.HelloWorld")
  20 public class HelloWorldRenderer
  21   extends XhtmlRenderer
  22 {
  23   private PropertyKey nameKey;
  24 
  25   public HelloWorldRenderer()
  26   {
  27     super(HtmlHelloWorld.TYPE);
  28   }
  29 
  30   @Override
  31   protected void findTypeConstants(Type type)
  32   {
  33     super.findTypeConstants(type);
  34     this.nameKey = type.findKey("name");
  35   }
  36 
  37   protected String getName(FacesBean bean)
  38   {
  39     return toString(bean.getProperty(this.nameKey));
  40   }
  41 
  42   /**
  43    * @see javax.faces.render.Renderer#getRendersChildren()
  44    */
  45   @Override
  46   public boolean getRendersChildren()
  47   {
  48     return true;
  49   }
  50   
  51   /**
  52    * @see org.apache.myfaces.trinidad.render.CoreRenderer#encodeAll(javax.faces.context.FacesContext, org.apache.myfaces.trinidad.context.RenderingContext, javax.faces.component.UIComponent, org.apache.myfaces.trinidad.bean.FacesBean)
  53    */
  54   @Override
  55   protected void encodeAll(FacesContext context, RenderingContext arc,
  56     UIComponent component, FacesBean bean) throws IOException
  57   {
  58     ResponseWriter writer = context.getResponseWriter();
  59     
  60     writer.startElement(XhtmlConstants.SPAN_ELEMENT, component);
  61 
  62     renderId(context, component);
  63     renderAllAttributes(context, arc, bean);    
  64     encodeAllChildren(context, component);
  65 
  66     String name = this.getName(bean);
  67     writer.writeText("Hello ", null);
  68     writer.writeText(name, "name");
  69     writer.writeText("!", null);
  70 
  71     writer.endElement(tagName);
  72 }

Obviously this is an extremely plain renderer, but I want to highlight some of its functionality line by line.

Line 18: @FacesRenderer

This is annotation from the jsf-comp facelet annotation library. It allows deployment of Facelet classes via annotations instead of XML. I choose to use it since it saves having to locate the XML file and maintain it, for me, it is much easier to find and maintain the annotations. Here I declare the component family and the renderer type. Since the component family is added as a constant by the plugin, I can reference it here. The renderer-type is not added to the generated component, and therefore must be declared in a string. Make sure your renderer type matches the one from the XML file

Using the Plug-in Instead of jsf-comp

TODO: show how to use the maven-faces-plugin will generate the XML in the faces-config.xml instead of using the jsf-comp annotations.

Line 21: XhtmlRenderer

Extending the Trinidad renderer makes life quite easy for "normal" components. Since the access to the FacesBean is not typed, but uses string key names (like "styleClass"), the XhtmlRenderer will automatically pick up the styleClass and inlineStyle properties on our component.

Line 27: Passing the Type

Trinidad renderers use a FacesBean.Type class to define the properties that a component exposes. Here, I am telling the parent renderer that this renderer will support the properties of the HtmlHelloWorld component.

Lines 30-35: Getting local references to property keys

Our renderer needs to access the name property of the component. In order to keep to the Trinidad development design, we will use a PropertyKey to obtain the name of the person to say hello to.

For your own component renderer, add a lookup for all properties that you will want to access in your renderer. Note, that if I provided a second constructor like:

   1   public HelloWorldRenderer(FacesBean.Type type)
   2   {
   3     super(type);
   4   }

Now, this renderer could be used from other renderers. This renderer would now be able to support any component that has a name property (and styleClass and inlineStyle if we had required this).

Lines 37-40: Property access methods

For our simple component, we only need to access the name property, but for most components will have several. Here we get the value of the name property from the FacesBean and convert it to a String. The CoreRenderer class that XhtmlRenderer extends has several methods for converting standard return types.

If we wanted to customize the value, here would be the proper place. An example for this would be to customize the JavaScript produced by an onclick property to inject JavaScript that this renderer would depend on.

Lines 42-49: Renders children

Using the encodeAll of a Trinidad renderer is much easier than having to have similar code in the encodeBegin and encodeEnd methods. Having getRendersChildren return true makes sure the encodeAll method is used.

Lines 62-64: Leveraging XhtmlRenderer

These methods allow XhtmlRenderer to do its work. It will attempt to find any standard properties that the current component supports and encode them. In our case it should automatically pick up the styleClass and inlineStyle properties.

Lines 66-69: Saying hello

Here is the meat of our example. We get the name from faces bean and display it to the user. Exciting isn't it?

Checking the Plugin Output

It would be a good time to now run the compile or install of maven for our impl project. Look in the faces-impl/target/maven-faces-plugin/main/resources/META-INF/ directory for the *.taglib.xml and faces-config.xml files and check if they look correct.

web

There is nothing special to do in the web project to leverage our components. The plugin does not need to be used in this project. Use maven to setup the standard WAR project.

pom.xml

Make sure to reference your other projects:

    <dependency>
      <groupId>com.mysite</groupId>
      <artifactId>faces-impl</artifactId>
      <version>${project.version}</version>
      <exclusions>
        <exclusion>
          <groupId>com.mysite</groupId>
          <artifactId>faces-generated-config</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

You don't need to reference the API project as Maven will automatically pull it in since the impl project references it. You do need to make sure that the generated config JAR is not automatically pulled in though. If you do not, facelets will attempt to load my-base.taglib.xml as a tag library.

Note that my artifactId's for this example are not maven standard, and I don't recommend them for your site. A better naming could have been mysite-faces-* for the artifactIds. This would help prevent the JAR files from colliding with other 3rd party libraries. I just kept it simple for this example.

HelloWorld.xhtml

Use the component in web/src/main/webapp/hello.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html 
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:my="http://mysite.com/components">
  <body>
    <my:helloWorld />
  </body>
</html>

Now you can change it up and use tr:document, tr:form and other functionality (like asking for the user's name to say hi to).

Wrapping it up

I have not shown the faces-config.xml web project or the web.xml as it is beyond the scope of this WIKI page. You should follow the instructions for Trindad, facelets and the jsf-comp facelets projects for information on how to configure the web application.

Help references:

Credits

Original Version
Andrew Robinson

Facelets_and_JSF_1.1_maven-faces-plugin_Getting_started (last edited 2009-09-20 23:01:34 by localhost)