javakaffee

Just another weblog about (web) development technologies like spring, tapestry, jersey, jsf, hibernate and more

December 11, 2006

How to add a new attribute to an existing JSF component (using facelets)

Filed under: development, java, webdev — martin.grotzke @ 10:20 pm

I just had the problem that I wanted to add a new attribute to the t:dataTable. The attribute should be a method binding that is invoked before toggleDetail is invoked on the HtmlDataTable (this method is invoked when the templates contains the detailStamp facet and the user toggles the detail view of a table row).

In our application we’re using facelets for templating, so that this had also to be considered.

So what had to be done?

  1. Write your custom component, e.g. a CustomHtmlDataTable with the additional attibute
  2. Write a custom ComponentHandler (for facelets)
  3. Create a facelets taglib where you define your tag with the component-type and the custom component handler
  4. Add a component definition to your face-config.xml where you define the component class to the component-type
  5. Use the namespace that you defined in the facelets taglib in your facelets template and use this namespace for the tag
  6. You’re done :)

In the following you find the single steps in more detail…


1. Write your custom component, e.g. a CustomHtmlDataTable with the additional attibute


public class CustomHtmlDataTable extends HtmlDataTable {
    
    private static final Log LOG = LogFactory.getLog( CustomHtmlDataTable.class );
    
    private MethodBinding _toggleDetailActionListener;
    
    public CustomHtmlDataTable() {
    }

    /**
     * @return the toggleDetailActionListener
     */
    public MethodBinding getToggleDetailActionListener() {
        return _toggleDetailActionListener;
    }

    /**
     * @param toggleDetailActionListener the toggleDetailActionListener to set
     */
    public void setToggleDetailActionListener( MethodBinding toggleDetailActionListener ) {
        _toggleDetailActionListener = toggleDetailActionListener;
    }

    /* (non-Javadoc)
     * @see org.apache.myfaces.component.html.ext.HtmlDataTable#toggleDetail()
     */
    @Override
    public void toggleDetail() {
        if ( _toggleDetailActionListener != null ) {
            final FacesContext context = getFacesContext();
            try {
                final ActionEvent event = new ActionEvent( this );
                event.setPhaseId( PhaseId.INVOKE_APPLICATION );
                _toggleDetailActionListener.invoke(context, new Object[] {event});
            } catch ( Exception e ) {
                LOG.error( "Caught exception when calling toggleDetailActionListener.", e );
                MessageUtil.addMessage( context, e );
            }
        }
        super.toggleDetail();
    }

    /* (non-Javadoc)
     * @see org.apache.myfaces.component.html.ext.HtmlDataTable#restoreState(javax.faces.context.FacesContext, java.lang.Object)
     */
    @Override
    public void restoreState( FacesContext context, Object state ) {
        Object values[] = (Object[])state;
        super.restoreState(context, values[0]);
        _toggleDetailActionListener = (MethodBinding)restoreAttachedState(context, values[1]);
    }

    /* (non-Javadoc)
     * @see org.apache.myfaces.component.html.ext.HtmlDataTable#saveState(javax.faces.context.FacesContext)
     */
    @Override
    public Object saveState( FacesContext context ) {
        Object values[] = new Object[2];
        values[0] = super.saveState(context);
        values[1] = saveAttachedState(context, _toggleDetailActionListener);
        return ((Object) (values));
    }

}




2. Write a custom ComponentHandler (for facelets)


public final class CustomHtmlDataTableHandler extends ComponentHandler {
    
    private static final String ACTION_LISTENER_ATT_NAME = "toggleDetailActionListener";

    public final static Class[] ACTION_LISTENER_SIG = new Class[] { ActionEvent.class };
    
    protected final static MethodRule actionListenerTagRule = new MethodRule(
            ACTION_LISTENER_ATT_NAME, void.class, ACTION_LISTENER_SIG );

    public CustomHtmlDataTableHandler(ComponentConfig config) {
        super( config );
    }
    
    protected MetaRuleset createMetaRuleset(Class type) {
        return super.createMetaRuleset(type).addRule( actionListenerTagRule );
    }

}




3. Create a facelets taglib where you define your tag with the component-type and the custom component handler

Create e.g. a file mytags.taglib.tld with the following 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>
    <namespace>http://www.yourcompany.org/jsf/facelet</namespace>
    <!-- we want to use dataTable as tag name... -->
    <tag>
        <tag-name>dataTable</tag-name>
        <component>
            <component-type>foo.bar.HtmlDataTable</component-type>
        	<handler-class>your.class.path.to.CustomHtmlDataTableHandler</handler-class>
        </component>
    </tag>
</facelet-taglib>

You have to add the filename to your web.xml in the context parameter facelets.LIBRARIES that you should already have defined, so afterwards it might look like the following:


	<context-param>
		<param-name>facelets.LIBRARIES</param-name>
		<param-value>
			/WEB-INF/tomahawk.taglib.xml; /WEB-INF/tomahawk-sandbox.taglib.xml; /WEB-INF/mytags.taglib.xml
		</param-value>
	</context-param>




4. Add a component definition to your face-config.xml where you define the component class to the previous component-type


	<component>
		<component-type>foo.bar.HtmlDataTable</component-type>
		<component-class>your.class.path.to.CustomHtmlDataTable</component-class>
	</component>




5. Use the namespace that you defined in the facelets taglib in your facelets template and use this namespace for the tag


<html xmlns="http://www.w3.org/1999/xhtml"
    ...
    xmlns:foo="http://www.yourcompany.org/jsf/facelet">
    ...
    <table jsfc="foo:dataTable" ... toggleDetailActionListener="#{myBean.myActionListener}">
        ...
    </table>
    ...
</html>




You’re done - okay, you have to add the actionlistener method to your bean, but I think this is clear :)


Btw: if anybody has suggestions/improvements please let me know, thanx!

Tags: , ,

July 4, 2006

JSF/Acegi authentication with a backing bean

Filed under: development, java, security — martin.grotzke @ 10:40 am

Powered by WordPress