Replace JSF tag by an own tag

Hi,

I'm trying to replace a <h:inputText> by my own tag. The used HtmlInputText component should not be replaced.

The reason is, that i want to pass permissions to the tag and map them to readonly and rendered attributes. My Problem is, that my own tag doesn't reolv bean values. If i use the <h:inputText> the bean values show up correctly. If i use my own tag, the input text shows "#{myBean.value}".

What could be the problem?

Thx in advance

Henrik

Here is my tag:

import javax.faces.component.UIComponent;

import javax.faces.component.html.HtmlInputText;

import javax.faces.context.FacesContext;

import javax.faces.webapp.UIComponentTag;

import com.arvato.jsf.security.JSFSecurity;

import com.arvato.jsf.security.Permissions;

import com.sun.faces.taglib.html_basic.InputTextTag;

publicclass ArvatoTextFieldTagextends InputTextTag{

private String role;

private String onkeypress;

private String id;

private String value;

public String getRole(){

return role;

}

publicvoid setRole(String permissions){

this.role = permissions;

}

protectedvoid setProperties(UIComponent component){

super.setProperties(component);

if(id !=null){

component.getAttributes().put("id", id);

}

if(onkeypress !=null){

component.getAttributes().put("onkeypress", onkeypress);

}

if(value !=null){

component.getAttributes().put("value", value);

}

if(role !=null){

FacesContext facesContext = FacesContext.getCurrentInstance();

Permissions p = JSFSecurity.getInstance().getPermissionsForRole(role, facesContext);

if(!p.isWritable()){

component.getAttributes().put("readonly",true);

}

if(!p.isReadable()){

component.getAttributes().put("rendered",false);

}

}else{

component.getAttributes().put("readonly",false);

component.getAttributes().put("rendered",true);

}

}

@Override

public String getComponentType(){

return"javax.faces.HtmlInputText";

}

@Override

public String getRendererType(){

returnnull;// no standalone renderer

}

@Override

publicvoid release(){

super.release();

role =null;

}

public String getValue(){

return value;

}

publicvoid setValue(String value){

this.value = value;

}

public String getOnkeypress(){

return onkeypress;

}

publicvoid setOnkeypress(String onkeypress){

this.onkeypress = onkeypress;

}

public String getId(){

return id;

}

publicvoid setId(String id){

this.id = id;

}

}

and my tld definition:

<taglib>

<tlib-version>0.03</tlib-version>

<jsp-version>1.2</jsp-version>

<short-name>arvato-jsf</short-name>

<uri>http://arvato.com/jsf/component/tags</uri>

<description>Arvato JSF tags</description>

<tag>

<name>textfield</name>

<tag-class>com.arvato.jsf.tag.ArvatoTextFieldTag</tag-class>

<attribute>

<name>id</name>

</attribute>

<attribute>

<name>role</name>

</attribute>

<attribute>

<name>value</name>

</attribute>

<attribute>

<name>onkeypress</name>

</attribute>

</tag>

</taglib>

[6808 byte] By [henrik_niehausa] at [2007-11-26 19:48:15]
# 1

In the setProperties method, you've got to resolve the valuebinding (#{blah.whatever}) to get the actual value. But you only want to do that if the attribute is set to a valuebinding.

Here's an example:

/**

* @see javax.faces.webapp.UIComponentTag#setProperties(UIComponent)

*/

protected void setProperties(UIComponent component) {

super.setProperties(component);

//Set the field reference property

if (fieldRef != null) {

if (UIComponentTag.isValueReference(fieldRef)) {

ValueBinding vb = getFacesContext().getApplication().

createValueBinding(fieldRef);

component.setValueBinding("fieldRef", vb);

} else {

component.getAttributes().put("fieldRef", fieldRef);

}

}

}

This example shows how to determine if the "fieldRef" attribute is a value binding, and extracts the value accordingly. But there's one more step. In your component class, or anywhere you access the attributes of your custom tag, use the following code:

public String getFieldRef() {

if (fieldRef != null)

return fieldRef;

ValueBinding vb = getValueBinding("fieldRef");

if (vb != null)

return (String)vb.getValue(getFacesContext());

else

return null;

}

The above snippet is what you would put in your custom component getter method.

JSF is open source. Something I found very helpful while learning how to create custom components was looking at how the JSF developers created theirs. You should go ahead and download the JSF source code.

CowKing

IamCowKinga at 2007-7-9 22:35:43 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 2

You are right, the ValueBinding was missing. Now it works perfectly.

I replaced

if(value != null) {

component.getAttributes().put("value", value);

}

with

if (value != null) {

if (isValueReference(value)) {

ValueBinding vb = getFacesContext().getApplication().createValueBinding(value);

component.setValueBinding("value", vb);

} else {

component.getAttributes().put("value", value);

}

}

henrik_niehausa at 2007-7-9 22:35:44 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 3

setProperties() for InputTextTag already sets id, onkeypress, and value, so you don't have to put code for setting them in your setProperties.

I've used this technique before to set default properties on a component and thought it was great, until I wanted to add that component programmatically (i.e., I had a panel that was bound to a component that added this component as a child). Then I found that I had to set the property values myself on the base component, which isn't ideal.

The right way to do this that would allow you to use your AvartoTextField programatically would be to create a new class that extends UIInputText and sets the default property values in the constructor. Then your Tag class would be set up to create an instance of my class and set the Role property on the AvartoTextField.

In your case (AvartoTextField), you'd have something like:

public class ArvatoTextField extends HtmlInputText {

public ArvatoTextField() {

setReadonly( false );

setRendered( true );

}

public void setRole( String role ) {

Permissions p = JSFSecurity.getInstance().getPermissionsForRole(role, getFacesContext() );

setReadonly( !p.isWritable() );

setRendered( p.isReadable() );

}

}

BrantKnudsona at 2007-7-9 22:35:44 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 4

Thank you for your hint on this. I didn't think about programatically adding components and things like that. Maybe I really should write my own components, which extend the standard components.

Though this is the point, where I wanted to save time / man power ;-)

Greetings

Henrik

henrik_niehausa at 2007-7-9 22:35:44 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 5

Surprisingly it doesn't take too long to extend an existing component (by an attribute or two). Obviously it will take longer than just extending the tag though. But not much.

What is time consuming is creating completely new components from scratch. Especially when they require new renderers.

Brant is right about the id, onkeypress, and value attributes. These are already handled by the existing component and tag class. So when you call super.setProperties from your setProperties method, it will process the existing attributes.

CowKing

IamCowKinga at 2007-7-9 22:35:44 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 6

Of course you are right about the id, onkeypress etc attributes. That was my fault. I didn't want to extend InputTextTag, because I want to be independent of the JSF-Implementation. As far as I know in this case I have to extend UIComponentTag to create my own tags and that means a little bit more work.

Am I right on this?

Henrik

henrik_niehausa at 2007-7-9 22:35:44 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 7

It's good to remain independent of any specific JSP implementation, but in this case you're using components that are part of the standard and should be part of any implementation. So I wouldn't worry about that here.

If you use UIComponentTag rather than InputTextTag, you'll have to override some more methods (getComponentType() and getRendererType()), and handle the property tags (except id, binding, and rendered are already handled by UIComponentTag).

If you want implementation independence you'll also have to create your own renderer.

BrantKnudsona at 2007-7-9 22:35:44 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...