|
Download zip archive for current lesson here - EMailCustomComponent.zip
JavaServer Faces v1.1.01 Reference Implementation is required for this application.
To build the war file ,you should set path to catalog with TomCat in build.properties file.
And in the same file you should set the path to all needed JSF jar files(jsf-api.jar, jsf-impl.jar,...).
In this lesson I will tell you about building custom components in JSF.
I think, each of you have noticed links to emails on the various web sites before.
Like this - Vladimir Bezugliy.
If you click this link, then email program will open and new email will be created.
The field "To:" of this email will contain an email address from this link.
In the current lesson we will build a new component UIOutputEMail.
This component will create such links.
Also we will write JSP tag <outputEMail> for that component.
We will use this tag on our JSP pages.
So, what we need in order to build custom component?
-
Create java class for our component.
For this purpose we should subclass some specific component class
or implement some specific interface.
-
Register custom component in a JSF configuration file faces-config.xml.
-
Create new JSP tag for custom component.
-
Register new JSP tag in a TLD file.
Create UI component
There are several ways to build custom components in JSF.
You can build your custom component from scratch.
In this case you should implement some specific interfaces.
But you can also just subclass some exist component and add some functionality to it.
For example you can extend UIOutput, if your custom component just output some data.
Or you can extend UIInput,
if your custom component output some data as well as manage user's input.
Besides, you can extend abstract class UIComponent or
UIComponentBase.
UIComponentBase
contains default implementation of all methods, except one, of
UIComponent abstract class.
That's why we will use UIComponentBase as base class for our custom components.
Let's create derived class UIOutputEMail from UIComponentBase.
UIOutputEMail.java
|
package com.vbez.jsfstepbystep;
import javax.faces.component.UIComponentBase;
public class UIOutputEMail extends UIComponentBase{
...
}
|
First of all we should write an implementation of the method getFamily().
I will tell you about this method in next lessons, and now we just return a null from this method.
Each component has a type.
Type of component it is just a string by which you will identify your component.
We will use a constant COMPONENT_TYPE for this purpose.
COMPONENT_TYPE
|
public static final String COMPONENT_TYPE = "com.vbez.jsfstepbystep.OutputEMail";
|
As you can see, the type of the component it is not the same as the full name of the class.
In JSF you not only are able to create new instances of some component by using "new SomeComponent()",
but also you can use special functionality wich allow you create some component by its type(string identifier).
But we will discuss it in the next lessons.
Now we have next java class:
UIOutputEMail.java
|
package com.vbez.jsfstepbystep;
import javax.faces.component.UIComponentBase;
public class UIOutputEMail extends UIComponentBase{
public static final String COMPONENT_TYPE = "com.vbez.jsfstepbystep.OutputEMail";
public String getFamily() {
return null;
}
}
|
Let's make us component to generate some HTML code.
We should implement a method encodeBegin() for that.
encodeBegin()
|
...
public void encodeBegin(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("a", this);
writer.writeAttribute("href", "mailto:VladimirBezugliy@gmail.com", "email");
writer.writeText("Vladimir Bezugliy", "label");
writer.endElement("a");
}
|
As you can see, first of all we get an instance of
ResponseWriter from the context.
We will use this object for writing our HTML representation of our component.
In this case we generated next link: <a href="mailto:VladimirBezugliy@gmail.com">Vladimir Bezugliy</a>.
Let's look at ResponseWriter class and its methods.
We will use this class to write the response to the client.
ResponseWriter has low-level methods for writing characters as well as high-level methods.
We will use high-level methods:
public void startElement(java.lang.String tagName, javax.faces.component.UIComponent component)
|
startElement writes the start tag of the element to the stream.
tagName - name of the tag.
component - the UIComponent (if any) to which this element corresponds.
But in this lesson we will not use this feature. And you can just set null instead of real component, or you can use "this".
It will not have any influence on our example.
public void endElement(java.lang.String tagName) throws java.io.IOException
|
endElement closes start tag.
tagName name of the tag to be ended.
public void writeAttribute(java.lang.String attName,
java.lang.Object attValue,
java.lang.String property)
throws java.io.IOException
|
writeAttribute writes to the stream an attribute of the current tag.
attName - name of the attribute to be added to the current tag.
attValue - value of the attribute.
property - name of the property of the component to wich this attribute corresponds.
public void writeText(java.lang.Object text,
java.lang.String property)
throws java.io.IOException
|
writeText check at first if any tag is opened.
If the tag is opened, then this method closes start tag.
text parameter will be converted to string. And this string will be writed to the stream.
property - name of the property of the component which corresponds to this attribute.
Look at next code in our encodeBegin() method:
writer.writeAttribute("href", "mailto:VladimirBezugliy@gmail.com", "email");
writer.writeText("Vladimir Bezugliy", "label");
|
email and label - properties of our component.
Property email will contain some e-mail address.
And property label will contain some label for this e-mail.
For example first name and second name of some person.
Add these two properties to our class.
UIOutputEMail.java
|
package com.vbez.jsfstepbystep;
import javax.faces.component.UIComponentBase;
public class UIOutputEMail extends UIComponentBase{
private String email = null;
private String label = null;
...
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setLabel(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
...
}
|
As you can see these are simple properties of simple JavaBean.
Now let's use these properties in our encodeBegin() method:
UIOutputEMail.java
|
package com.vbez.jsfstepbystep;
import javax.faces.component.UIComponentBase;
public class UIOutputEMail extends UIComponentBase{
private String email = null;
private String label = null;
...
public void encodeBegin(FacesContext context) throws IOException {
System.out.println("UIOutputEMail.encodeBegin_start");
ResponseWriter writer = context.getResponseWriter();
writer.startElement("a", this);
writer.writeAttribute("href", "mailto:"+getEmail(), "email");
writer.writeText(getLabel(), "label");
writer.endElement("a");
System.out.println("UIOutputEMail.encodeBegin_end");
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setLabel(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
...
}
|
To begin with, encodeBegin() send to the stream first part of the start tag - "<a".
Then it sends the attribute "href" to the stream.
As the value of "href" attribute we use simple string "mailto:".
And we add value from property getEmail() to that string.
Then we close tag "à" - we just send a character ">" to the stream.
Then we send to the stream value that we get from property getLabel().
And at the end we write close tag </a>.
After all we should look at constructor of our class.
It looks like that:
Constructor
|
public UIOutputEMail(){
super();
setRendererType(null);
}
|
First of all we called constructor from superclass.
And then we set property rendererType to null.
There are two ways to render component in JSF:
-
Component is able to do rendering by itself.
-
Component can delegate rendering to special classes - renderers.
In our example UIOutputEMail render itself (look at the method encodeBegin).
That's why it does not have any associated renderer.
And that's why we set property rendererType to null.
It's all. We've just wrote our first java class of our component.
All code of our class looks like that:
UIOutputEMail.java
|
package com.vbez.jsfstepbystep;
import java.io.IOException;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
public class UIOutputEMail extends UIComponentBase{
public static final String COMPONENT_TYPE = "com.vbez.jsfstepbystep.OutputEMail";
private String email = null;
private String label = null;
public UIOutputEMail(){
super();
setRendererType(null);
}
public String getFamily() {
return null;
}
public void encodeBegin(FacesContext context) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("a", this);
writer.writeAttribute("href", "mailto:"+getEmail(), "email");
writer.writeText(getLabel(), "label");
writer.endElement("a");
System.out.println("UIOutputEMail.encodeBegin_end");
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setLabel(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
|
Register our component in config file faces-config.xml
First of all we've created java class for our component. Now we should register it in JavaServer Faces config file - faces-config.xml.
faces-config.xml
|
<?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>
<component>
<component-type>com.vbez.jsfstepbystep.OutputEMail</component-type>
<component-class>com.vbez.jsfstepbystep.UIOutputEMail</component-class>
<property>
<property-name>eMail</property-name>
<property-class>String</property-class>
</property>
<property>
<property-name>label</property-name>
<property-class>String</property-class>
</property>
</component>
</faces-config>
|
We should use the tag <component> in order to register our component.
At first we should declare type of the component. We should use the tag <component-type> for that.
If you remember, we created special constant COMPONENT_TYPE for declaring type of the component.
And value from that constant we will use in this tag.
Tag <component-class> declares java class of our component.
Also tag <component> contains declarations of properties of the component - look at the <property> tag.
And this tag declares name of the property in its child tag <property-name>,
and java class of the property in the tag <property-class>.
There are two properties eMail and label in our example.
Both have String type.
It's all - we've just registered our component.
Create JSP tag for UIOutputEMail component
We want to use our component on JSP pages.
And thus we should create JSP tag for UIOutputEMail component.
We can use two base classes for this purpose -
UIComponentTag and
UIComponentBodyTag.
You will use UIComponentBodyTag in the cases when you want to process body of the tag.
But our JSP tag will not have any body content.
And therefore we will use the class UIComponentTag as base clase of our JSP tag class.
OutputEMailTag.java
|
package com.vbez.jsfstepbystep;
import javax.faces.webapp.UIComponentTag;
public class OutputEMailTag extends UIComponentTag {
...
}
|
Now we should link our component UIOutputEMail with the tag OutputEMailTag.
We should implement two methods for that - ñomponentType è
rendererType.
Method ñomponentType should return COMPONENT_TYPE of our component.
componentType
|
public String getComponentType() {
return UIOutputEMail.COMPONENT_TYPE;
}
|
And method rendererType should return type of the renderer wich render that component.
But our component does not have any associated renderer, but it render itself.
That's why that method will return null.
rendererType
|
public String getRendererType() {
return null;
}
|
We will use our tag in the next way - <ourTag eMail="VladimirBezugliy@gmail.com" label="Vladimir Bezugliy" />.
As you can see there are two attributes in our tag - eMail and label.
Therefore the class of our tag should have two properties - eMail and label.
OutputEMailTag.java
|
...
private String email = null;
private String label = null;
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setLabel(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
...
|
Now we should implement the method setProperties.
setProperties()
|
protected void setProperties(UIComponent component) {
super.setProperties(component);
UIOutputEMail emailComponent = (UIOutputEMail) component;
if(null != email){
emailComponent.setEmail(email);
}
if(null != label){
emailComponent.setLabel(label);
}
}
|
First of all we should call the method setProperties from our superclass.
We do it in order to support the core properties.
Then we have to check, if properties of our tag have some values.
If yes - then we write these values from properties of tag to properties of our component.
After all we should implement method release().
In that method we should release all resources wich we've used.
release()
|
public void release() {
email = null;
label = null;
super.release();
}
|
Now we have next java class of our JSP tag:
OutputEMailTag.java
|
package com.vbez.jsfstepbystep;
import javax.faces.component.UIComponent;
import javax.faces.webapp.UIComponentTag;
import com.vbez.jsfstepbystep.UIOutputEMail;
public class OutputEMailTag extends UIComponentTag {
private String email = null;
private String label = null;
public String getComponentType() {
return UIOutputEMail.COMPONENT_TYPE;
}
public String getRendererType() {
return null;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setLabel(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
protected void setProperties(UIComponent component) {
super.setProperties(component);
UIOutputEMail emailComponent = (UIOutputEMail) component;
if(null != email){
emailComponent.setEmail(email);
}
if(null != label){
emailComponent.setLabel(label);
}
}
public void release() {
email = null;
label = null;
super.release();
}
}
|
Create tags library description file
In order to start to use our new tag on JSP pages, we should describe our new tag in the TLD(tags library description) file.
Look at TLD file for current lesson:
jsfsbs.tld
|
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<uri>jsf-step-by-step</uri>
<tag>
<name>outputEMail</name>
<tag-class>com.vbez.jsfstepbystep.OutputEMailTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>email</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>label</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
|
There is the root tag <taglib> for the tag library.
Tag <tlib-version> indicates version of the library - 1.0.
Also we indicated that current version of the library works with JSP 1.2.
We use the tag <uri> to store unique identificator for current library - jsf-step-by-step.
Now, if you want to use the library in your JSP pages
you should just add next code to your page:
<%@ taglib uri="jsf-step-by-step" prefix="jsfsbs" %>
And voila - you are able to use all tags from this library.
Next we can see the tag <tag>.
This tag descibes our new JSP tag.
At first we indicated that our new tag has the name - outputEMail.
This name we will use on JSP pages.
Next we indicated the java class, wich represents current tag - com.vbez.jsfstepbystep.OutputEMailTag.
Tag <body-content> describes that our JSP tag will not have any body content.
Our tag has two attributes - email and label.
And both are required.
Tag <rtexprvalue> indicated that our tag works only with static text - it does not work with JSF EL.
It's all - we described our tag in TLD file. Now we should put this TLD file into WEB-INF directory.
Our custom component is ready - let's use it.
Let's write simple JSP page.
index.jsp
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="jsf-step-by-step" prefix="jsfsbs" %>
<HTML>
<BODY>
<f:view>
<jsfsbs:outputEMail email="vladimirbezugliy@gmail.com" label="Vladimir Bezugliy" />
</f:view>
</BODY>
</HTML>
|
As you can see, first of all we've just imported tag library with the uri "jsf-step-by-step".
Then we indicated that we will use "jsfsbs" prefix for tags from that library.
And next we've used our tag from that library - outputEMail.
We'we used my e-mail vladimirbezugliy@gmail.com as the value of the attribute email.
And we've used my name as the value of the attribute label.
Now, if you run this application, you will see next link on the page:
Ðåçóëüòàò ðàáîòû
|
<a href="vladimirbezugliy@gmail.com">Vladimir Bezugliy<a>
|
We've just created the first and the simplest JSF custom component. But it is not the end - we will improve it in our future lessons.
Also in the next lessons I will show you how to develop more complicated custom components.
|