DigitalJoel

2010/05/01

Guice and JSF 2

Filed under: java, JSF, JSF2 — Tags: , , — digitaljoel @ 8:38 pm

As I’m sure you can tell from previous entries, I’ve been playing with JSF 2 in Google App Engine.  I decided to give Google’s Guice a try for dependency injection instead of Spring.  Why?  This is really just a learning app and why not try something new.

So how do you get Guice to play nicely with JSF?  One solution I saw online was to write a custom ELResolver for JSF that will do the injection.  I found this resource http://snippets.dzone.com/posts/show/7171 that had a full ELResolver implementation.  I had to modify it slightly to get it to work.  Namely, I had to move the Guice Injector creation out of getValue and into a private static variable.  I suspect I could make it not static, but I never gave it a try.  Here’s the source just in case that link dies at some point.  Again, I take no credit for this code other than the small modification mentioned.

/**
 * Bulk of the code taken from
 * http://snippets.dzone.com/posts/show/7171
 **/
package jota.soc.guice;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.ServletModule;
import java.beans.FeatureDescriptor;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.el.ELContext;
import javax.el.ELResolver;

/**
 */
public class GuiceResolver extends ELResolver
{

    Injector injector = Guice.createInjector(
            new ServletModule(),
            new SocGuiceModule() );

    //We only need to see the currently processed Objects in our Thread, that
    //prevents multithread issues without synchronization
    private static ThreadLocal currentlyProcessedThreadLocal = new ThreadLocal()
    {
        @Override
        protected Object initialValue()
        {
            return new LinkedList();
        }
    };

    //Im not sure if the synchronized lists seriously slow down the whole EL
    //resolving process
    private static List<WeakReference> alreadyInjectedObjects = Collections.
            synchronizedList( new LinkedList() );

        @Override
    public Object getValue( ELContext context, Object base, Object property )
    {

        //if the list of currently processed property objects doesnt exist for this
        //thread, create it
        List<Object> currentlyProcessedPropertyObjects = (List<Object>) currentlyProcessedThreadLocal.
                get();

        //Handle only root inquiries, we wont handle property resolving
        if ( base != null )
        {
            return null;
        }

        //checking if this property is currently processed, if so ignore it -> prevent
        //endless loop
        if ( checkIfObjectIsContained( property,
                currentlyProcessedPropertyObjects ) )
        {
            return null;
        }

        //add the to-be-resolved object to the currently processed list
        currentlyProcessedPropertyObjects.add( property );

        //now we can savely invoke the getValue() Method of the composite EL
        //resolver, we wont process it again
        Object resolvedObj = context.getELResolver().getValue( context, base,
                property );

        //ok, we got our result, remove the object from the currently processed list
        removeObject( property, currentlyProcessedPropertyObjects );

        if ( resolvedObj == null )
        {
            return null;
        }

        //ok we got an object
        context.setPropertyResolved( true );

        //check if the object was already injected
        if ( !checkIfObjectIsContainedWeak( resolvedObj, alreadyInjectedObjects ) )
        {
            injector.injectMembers( resolvedObj );
            //prevent a second injection by adding it as weakreference to our list
            alreadyInjectedObjects.add( new WeakReference( resolvedObj ) );
        }

        return resolvedObj;
    }

    /**
     * This method will search for an object in a Weak List. If there are any
     * WeakReferences on the way that were removed by the garbage collection
     * we will remove them from this list
     * @param object
     * @param list
     * @return
     */
    private boolean checkIfObjectIsContainedWeak( Object object,
                                                  List<WeakReference> list )
    {
        for ( int i = 0; i < list.size(); i++ )
        {
            WeakReference curReference = list.get( i );
            Object curObject = curReference.get();
            if ( curObject == null )
            {
                //ok, there is are slight chance that could go wrong, if you
                //have to prevent a double injection by all means, you might
                //want to add a synchronized block here
                list.remove( i );
                i--;
            }
            else
            {
                if ( curObject == object )
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * checks if an object is contained in a collection (really the same object '==' not equals)
     * @param object
     * @param collection
     * @return
     */
    private boolean checkIfObjectIsContained( Object object,
                                              Collection collection )
    {
        for ( Object curObject : collection )
        {
            if ( object == curObject )
            {
                return true;
            }
        }
        return false;
    }

    /**
     * removes an object from a list. really removes the given instance, not an other
     * object that fits equals
     * @param object
     * @param list
     */
    private void removeObject( Object object, List list )
    {
        for ( int i = 0; i < list.size(); i++ )
        {
            if ( list.get( i ) == object )
            {
                list.remove( i );
            }
        }
    }

    @Override
    public Class<?> getType( ELContext context, Object base, Object property )
    {
        return null;
    }

    @Override
    public void setValue( ELContext context, Object base, Object property,
                          Object value )
    {
    }

    @Override
    public boolean isReadOnly( ELContext context, Object base, Object property )
    {
        return false;
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors( ELContext context,
                                                              Object base )
    {
        return null;
    }

    @Override
    public Class<?> getCommonPropertyType( ELContext context, Object base )
    {
        return null;
    }
}

That sure looks like a lot of code to me. I know Spring also provides a custom ELResolver for dependency injection. I found two links talking about resource injection here and here and thought I would try writing my own resource injector. Here’s how it ended up:

package jota.soc.guice;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.ServletModule;

import com.sun.faces.spi.InjectionProvider;
import com.sun.faces.spi.InjectionProviderException;
import com.sun.faces.vendor.WebContainerInjectionProvider;

/**
 * JSF injection provider for Guice.
 * @author Joel Weight
 */
public class GuiceInjectionProvider implements InjectionProvider
{
    public GuiceInjectionProvider()
    {
        System.out.println( "creating guice injection provider" );
    }

    /**
     * default injector provided by the web container.
     */
    private static final WebContainerInjectionProvider con =
                                                       new WebContainerInjectionProvider();

    /**
     * Custom guice injector that will load our modules.
     */
    private static final Injector injector = Guice.createInjector(
            new ServletModule(),
            new SocGuiceModule() );

    @Override
    public void inject( Object managedBean ) throws InjectionProviderException
    {
        // allow the default injector to inject the bean.
        con.inject( managedBean );
        // then inject with the google injector.
        injector.injectMembers( managedBean );
    }

    @Override
    public void invokePostConstruct( Object managedBean )
            throws InjectionProviderException
    {
        // don't do anything here for guice, just let the default do its thing
        con.invokePostConstruct( managedBean );
    }

    @Override
    public void invokePreDestroy( Object managedBean ) throws
            InjectionProviderException
    {
        con.invokePreDestroy( managedBean );
    }
}

It’s a very simple implementation. It delegates everything to the default container InjectionProvider, and then adds a touch of Guice. I then added this to web.xml:

    <context-param>
        <param-name>com.sun.faces.injectionProvider</param-name>
        <param-value>jota.soc.guice.GuiceInjectionProvider</param-value>
    </context-param>

And all my guice injections are occurring as expected. I am no longer using the custom ELResolver implementation. Obviously it’s not a general solution since I am instantiating my guice modules right in the provider rather than configuring it somehow, but it was enough for me right now.

If I run into any problems with this solution I’ll let you know, otherwise I would love to hear from any gurus as to how this could be problematic.

JSF 2 Ajax Submission Exceptions

Filed under: java, JSF2 — Tags: , — digitaljoel @ 7:52 pm

Tonight I was messing around with my pet project.  I have a composite component that I am trying to use in three different places in a page.  I am passing an action into the component, and that action is called via ajax on a command button.  In the first instance on the page, that action was working just fine.  In the second and third it was failing with a strange error message in a javascript popup.

serverError: class javax.faces.el.EvaluationException
blah blah blah with my source file followed by
Property 'myAction' not found on type MyType

MyType is the bean, and myAction is the action method I was attempting to call from the composite component.  I checked, and double checked and triple checked that everything was spelled correctly and everything.  The autocomplete in Netbeans was even showing that the action method was there.  I was especially surprised that it would work in one instance on the page, but not in the others, especially since the usage was identical other than the action method.

I have a co-worker who’s favorite saying is “Read the Error, Fix the Error” and in 99% of the cases, it works great, but not in this case.

The problem ended up being that some code in my action was causing a runtime exception that I wasn’t catching.  Once I resolved that, the error went away.  The reason it took me so long to figure that out is because neither the server log, nor the javascript popup gave me any clue as to the real cause of the error.

So, if you are seeing this strange error and KNOW that your action is defined correctly, set a break point in the action, or add some trace level logging so you can figure out what’s going on much faster than I did.

2010/04/12

JSF 2, JSR-303, and you

Filed under: development, java, JSF, JSF2 — Tags: , — digitaljoel @ 9:21 pm

In JSF 2, they added some nifty stuff.  One of the items they added was that JSF will seamlessly validate input values using a JSR-303 implementation if one is available.  ”That’s nifty!” you say, “But how do I get an implementation of JSR-303 into my webapp?”

The reference implementation of JSR-303 is in hibernate validator version 4.  What if you don’t want to use hibernate?  That’s fine, because the validator project stands alone.  You don’t need to use any other parts of hibernate if you don’t want to.  Here’s what you should put in your maven pom in order to get hibernate 4 validators.

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>4.0.2.GA</version>
        </dependency>

Now you should be able to add JSR-303 validators to your project. Something like this should do:

    @NotNull
    @Size(min=3, max=10)
    private String username;

Now, when you tie an input control to that field in the bean, the value will be validated with the NotNull and Size constraints.

Obviously, the next thought is, “Awesome, now I can easily do multi-field constraints.”  For instance, you have a New Password form. You want the user to enter the password, and then to confirm the password. The simple answer is to write a validator that checks that the values of the two fields are equal and place the constraint on the Type, not on the Field. Something like the FieldMatch validator in the answer to this question on stackoverflow would be perfect.

So you implement the FieldMatch validator, and place it on your type. You then deploy your fancy JSF application and test the validator. Strangely your validator is never run. After reading more closely, you discover that JSF does “Field Level” validation using JSR-303 validators. Well, that should be easily enough overcome, right?

Next, you add an f:event tag to the page containing the bean to be validated. The event is preValidate, and you call a method on your bean containing the values. The method you call runs the validator manually. Perfect, right? More testing reveals that the your validator returns true every time, and even more than that, the values it’s testing are both null (which is why they are also equal).

Alright, let’s figure out what’s really going on.

After a half day of reading and testing, you finally go back to JSF basics and remember the JSF lifecycle. Alright, the phases are:

  1. Restore View
  2. Apply Request Values
  3. Process Validations
  4. Update Model Values
  5. Invoke Application
  6. Render Response

Notice that process validations comes before update model values. Well, no wonder putting the type constraint on the bean wasn’t validating the values that were set in the input controls. The values from the controls are validated before they are set on the bean. That explains why JSF 2 only does field level validation. They can take those validators, and apply them to the values that are set on the input controls before they are set in the bean.

So, what’s the answer to this problem? I haven’t decided yet. In this particular case, there are already JSF validators available for just this kind of validation, but what about the general case of type level validators. If I figure anything out, I’ll let you know. If you have any great ideas, chime in on the comments.

2010/02/09

What’s in a name?

Filed under: development, java — Tags: — digitaljoel @ 11:27 pm

At work I’m in a committee where we are discussing a versioning strategy for one of our legacy projects. I’m not involved directly with the project, but they pulled me into the committee because of my experience with subversion on our current project. In a part of this meeting, we were talking about the database.

In the application I work on, we are able to version the database. So far, it is small enough that we can copy it in a couple of hours, then run the migration scripts on the new database for the new version, and finally point the new application code at the new database. It’s all quite simple really. Then, if things go awry during the release, we can simply replace the webapp with the old version and point back at the old database. In our case, the database name is the application name, followed by the version. For instance, app-1.7.4. When we do the next release, all the data in app-1.7.4 is copied into app-1.7.5. Anyway, you see what I mean.

So I mentioned this in the meeting today. The DBA for the legacy project looked at me as if I was speaking sacrilege. His eyes were wide as he said;
“You actually change the name of the database for every release?!”
My Response: “Well, yeah. That’s the magic of JNDI. My app doesn’t care about the name of the database it’s pointing at, we just set that up in the JNDI properties and merrily go along.”

So, what’s in a name? I’ll admit I don’t have the most experience in technologies other than Java. This isn’t peculiar to Java web applications, is it?

2010/02/02

So Long Kenai

Filed under: development, java, tools — Tags: , , , — digitaljoel @ 11:25 pm

A couple of my recent posts have dealt with JSF 2 and Google’s app engine for Java. I was experimenting with this stuff in a small hobby project that was role-playing game related. It’s been benched for another role-playing game related project that I’m going to be writing in C# in XNA, but before I get to that, I want to say a fond farewell to Kenai.

This morning I had an email from the Kenai team in my inbox. Now that Sun is a part of Oracle, it has been decided by the powers that be on the Oracle side that hosting free, open source projects doesn’t help the bottom line. I think this is really unfortunate. Kenai had a really great integration story with Netbeans.

Kenai included free use of Jira, a wiki, Subversion hosting, downloads, and all of it integrated seamlessly with Netbeans. Setting up my project on Kenai was actually a joy. I simply clicked the menu in Netbeans 6.8 to “share this project on Kenai” and everything just worked. When I then went to my desktop from my laptop, I was able to “get” the project from Kenai and it checked it out, allowed me to login to Kenai and look at my jira tasks and modify them within Netbeans.

I haven’t used any other project hosting solution, but I’ve viewed plenty of projects in google code and sourceforge and have yet to see such a complete story. If I had the money to spare for a pet/hobby project I would certainly go to jira studio, but I suspect I’m going to have to find something a little more free to move my project too. I am happy that I didn’t have too much invested in Kenai, but it’s pretty sad that it’s been killed.

So long Kenai, it was good to know you while it lasted.

Update:
I just got this email from the Kenai folks. Fortunately, it looks like good news, and I think it will be a great benefit to all the projects on java.net.

Gentlepeople,

In an effort to get information out to the Kenai community quickly, while trying to manage the integration of our two companies, I think we did a poor job at communicating our plans for Kenai.com to you. I would like to remedy that now. Our strategy is simple. We don’t believe it makes sense to continue investing in multiple hosted development sites that are basically doing the same thing. Our plan is to shut down kenai.com and focus our efforts on java.net as the hosted development community. We are in the process of migrating java.net to the kenai technology. This means that any project currently hosted on kenai.com will be able to continue as you are on java.net. We are still working out the technical details, but the goal is to make this migration as seamless as possible for the current kenai.com projects. So in the meantime I suggest that you stay put on kenai.com and let us work through the details and get back to you later this month.

Thanks for your feedback and patience.

Ted Farrell
Oracle Corporation

2010/01/11

JSF 2 Custom Converter

Filed under: development, facelets, GAE, java, JSF — Tags: , , , — digitaljoel @ 9:50 pm

I ran into a very strange problem in my little Google App Engine application. I’m using JSF 2 as the presentation layer. I’m using Google’s low level api for accessing the data store and have written some little utilities to convert from my objects to Google’s Entity objects.

I thought I had the mapping working OK, but had only tried setting simple String properties on my data objects. I finally tried to map a simple association, where one object would have a list of keys of other objects. In my case I have Tables. Each table can be associated with zero or more Genres. So, my Table data object has a List of Keys for the genres that it is a part of, like this:

public class Table
        extends DataObjectAbstract
        implements Serializable
{
    @Persistent
    private List<Key> genres;

    // other private attributes here

    public List<Key> getGenres()
    {
        return genres;
    }

    public void setGenres( List<Key> genres )
    {
        this.genres = genres;
    }

    // other public getters and setters and other methods here
}

You can safely ignore DataObjectAbstract and the @Persistent annotation. They aren’t part of this blog post.

The strange behavior I was seeing was that when I would get the genre list from the data store, it would be a List<String> instead of the List<Key> I was expecting. After some debugging, I finally discovered that I was setting it as a List<String> because Strings are what I was getting from my JSF page, and thanks to Java’s type erasure with generics, my code didn’t know any better. So, the solution is a custom converter, which will allow me to pass the Keys to and from the browser.

Fortunately, in JSF 2, custom converters are even easier than they are in JSF 1.x. Here’s my simple converter class for the com.google.appengine.api.datastore.Key class. Making this even more simple is the fact that Google offers a KeyFactory that takes care of giving a URL safe representation of their Key object.

package jota.soc.ui.converter;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

/**
 * Converter for Google Key.
 * @author Joel.Weight
 */
@FacesConverter( value="keyConverter" )
public class KeyConverter implements Converter {

    /**
     * converts the String representation of the key back to the Object
     */
    public Object getAsObject( FacesContext context, UIComponent component,
                               String value )
    {
        // will throw new IllegalArgumentException if it can't parse.
        return KeyFactory.stringToKey( value );
    }

    /**
     * converts the Key object into its String representation.
     */
    public String getAsString( FacesContext context, UIComponent component,
                               Object value )
    {
        if ( value instanceof Key )
        {
            return KeyFactory.keyToString( (Key)value );
        }
        else
        {
            throw new IllegalArgumentException( "Cannot convert non-key object in KeyConverter" );
        }
    }
}

With the @FacesConverter annotation, you don’t have to register the converter in faces-config or anything. You can simply reference it using the id.

Here’s the code that uses the converter.

<h:selectManyListbox id="genresBox" value="#{createTable.table.genres}">
    <f:converter converterId="keyConverter" />
    <f:selectItems value="#{createTable.genres}"
        var="item"
        itemValue="#{item.key}"
        itemLabel="#{item.name}" />
</h:selectManyListbox>

And just like that, I’m storing Keys in the data store instead of Strings.

2009/12/21

JSF 2.0.2 and Google App Engine

Filed under: facelets, GAE, java, JSF — Tags: , — digitaljoel @ 6:46 pm

I spent a couple hours today trying to get a JSF 2 based webapp up in app-engine.  Of course, it wasn’t as easy as simply following the instructions in

configuring jsf 2.0 to run on the google app engine using netbeans

I ran into the problem detailed here  ( JNDI problem in jsf 2.0 and google app engine ) right off the bat.  I tried to use his packaged jar, but app-engine kept giving me an exception that it couldn’t load the zip file. Perhaps it was corrupted when I downloaded it or something.

If you follow his link to the WebConfiguration.java file, you will see that he created a new WebConfiguration.java file within the same package as the existing one and copied the implementation, commenting out the offending code.  I decided to try a different approach. I’m sure there is nothing wrong with Josh’s approach, and if it weren’t for his blog and all the ground work he had already done in finding the offending code, I probably wouldn’t have even bothered going further with JSF 2 in app engine. In any case, here’s what I did.

I downloaded the JSF 2.0.2 source from here ( jsf 2.0.2 source ) and expanded it.  I then found the WebConfiguration.java file and decided to modify it.  Rather than comment out all the source, I added a new config enumeration value to the BooleanWebContextInitParameter enumerations as follows

// around line 1071 in the finished file, after the previously final enum value AllowTextChildren
EnableJNDI(
"com.sun.faces.enableJNDI",
true
);

Then, I check for the value of that parameter in private constructor, around line 113
        // add the isOptionEnabled call to this conditional statement
        if ( isOptionEnabled( BooleanWebContextInitParameter.EnableJNDI ) && canProcessJndiEntries()) {
            processJndiEntries(contextName);
        }

When that is done, simply compile, and you have a new jsf-api/build/lib/jsf-api.jar and jsf-ri/build/lib/jsf-impl.jar that you can use in your project. For my compile, I simply copied the build.properties.glassfish.orig to build.properties and set the jsf.build.home property to the directory in which the source was checked out.

Finally, put a new context-param in web.xml with the name com.sun.faces.enableJNDI and a value of false.

All I’ve got so far is the hello world, so we’ll see if I run into other difficulties as I use further functionality.

2009/11/16

How to remove cruft from Open Resource in Eclipse

Filed under: eclipse, java, maven — Tags: , — digitaljoel @ 1:07 pm

I got really tired of seeing my maven target directory and .svn directory entries show up in the ctrl+shift+R (open resource) dialog in Eclipse. Here’s what I did to remove them.

For .svn directories, I simply shared the project through subversive. I suspect doing the same with subclipse will work. Once eclipse knows that it is in version control, it removes the .svn directories from the Open Resource dialog.

For the maven target directory, I marked the directory as Derived. To do this, simply open the properties for the directory by pressing alt+enter or right clicking and selecting properties. Then, select the “Derived” property.

Set Directory to Derived

Derived Setting

Finally, you can remove the target directory from your Package Explorer using filtering. Simply select the little down arrow menu in the package explorer, then “filters”, and in the Name filter patterns, type “target”. Note that this will remove target from the Package Explorer, so if you want to mark it as Derived, you will need to do so before removing it from the Package Explorer.

Filtering in Package Explorer

2009/08/25

Passing action methods in Facelets using array notation

Filed under: facelets, java, JSF — digitaljoel @ 3:21 am

When I was first learning facelets, I often ran into a problem when I was passing an action method into an included facelet file. For instance, I had file1.xhtml, which would include my custom tag component file2.xhtml (defined within my taglib.xml file.) file2.xhtml had a commandLink or commandPrompt that needed an action. Since I would re-use file2.xhtml and allow it to call different actions, I needed a way to pass the action method to file2.

The first thought was to include it as an attribute of my component.

<myns:mytag
   myActionAttribute="${mybean.myaction}" />

and then within file2, which in this case is mapped to mytag:
<h:commandLink value="${mybean.myValue}" action="${myActionAttribute}" />

When I tried to execute this, faces would complain to me that mybean had no myaction property. I knew it did and would be endlessly frustrated by this.
The answer is to reference the action method using array notation. On any bean referenced in facelets you can reference the properties using dot notation, as in ${mybean.myaction}, OR you can also reference it using array notation, such as ${mybean["myaction"]}.
I never did dig in far enough in order to figure out exactly what was going on under the hood, but I’m guessing ${mybean.myaction} was actually evaluated before being sent to mytag. In order to avoid this, pass the bean, and then pass the action method as a string to the component, such as:
<myns:mytag
   myActionController="${mybean}"
   myActionMethod="myaction" />

Notice that the method is just a string. Then in your component, reference it in array notation ( how many times can I say array notation! )
<h:commandLink value="${mybean.myValue}" action="${myActionController[myActionMethod]}" />

Notice that the method is NOT quoted. myActionMethod will be replaced by the String value passed in the value of the attribute, and that method on the bean will be called as the action of the commandLink.

2009/07/07

JSF Tri State Checkbox

Filed under: facelets, java, jquery, JSF — digitaljoel @ 5:05 am

In my work with JSF I found that I needed a tri-state checkbox.  In my case, the checkbox represented whether or not a decorator was added to all selected objects, no selected objects, or some of the selected objects.  This corresponded to checked, unchecked, or partially checked.

When I was looking for something that would fit my needs I found a Javascript implementation at Shams’ Blog which is a bit of a different use case than what I was looking for, but has some good points and useful images.

I created the following facelet file that manages state transitions and image management for the JSF tri state checkbox.  My control doesn’t handle the nested selection like you’ll see in Sham’s blog, but it fills the needs stated above.

<!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:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jstl/core" >

<!--
  An input component that is represented as a checkbox with three states.  Checked, Unchecked, and Intermediate.

  Required attributes:
    checkedValue - the value that should be set for this component when the checkbox is checked
    uncheckedValue - the value that should be set for this component when the checkbox is unchecked
    intermediateValue - the value that should be set for this component when the checkbox is in the intermediate state.
    value - bean property to hold the value.  Should evaluate to checkedValue, uncheckedValue, or intermediateValue the first time.
    controller - controller that implements the value change listener.
    key - key value to pass as an attribute of the control that contains the checkbox value.
    valueChangeListener - change listener for value of this checkbox component.
 -->

<ui:composition>

  <script type="text/javascript">
    function setCheckValue( img, next, newVal )
    {
      img.css('display', 'none');
      img.siblings('[id$='+next+']').css('display','inline');
      img.siblings('[id$=triStateCheck]').val(newVal);
    }
  </script>

  <h:graphicImage
    id="trueValue"
    url="/images/ico_checked.gif"
    style="display: #{value == checkedValue ? 'inline' : 'none'}"
    onmouseover="jQuery(this).attr('src','/images/ico_checked_highlighted.gif')"
    onmouseout="jQuery(this).attr('src','/images/ico_checked.gif')"
    onclick="setCheckValue( jQuery(this), 'falseValue', '#{uncheckedValue}')"
    rendered="#{(empty rendered) ? true : rendered}" />
  <h:graphicImage
    id="falseValue"
    style="display: #{value == uncheckedValue ? 'inline' : 'none'}"
    onmouseover="jQuery(this).attr( 'src', '/images/ico_unchecked_highlighted.gif' )"
    onmouseout="jQuery(this).attr('src', '/images/ico_unchecked.gif')"
    url="/images/ico_unchecked.gif"
    onclick="setCheckValue( jQuery(this),
        '#{value == intermediateValue ? 'intermediateValue' : 'trueValue'}',
        '#{value == intermediateValue ? intermediateValue : checkedValue}')"
    rendered="#{(empty rendered) ? true : rendered}" />
  <h:graphicImage
    id="intermediateValue"
    style="display: #{value == intermediateValue ? 'inline' : 'none'}"
    onmouseover="jQuery(this).attr( 'src', '/images/ico_intermediate_highlighted.gif' )"
    onmouseout="jQuery(this).attr('src', '/images/ico_intermediate.gif')"
    url="/images/ico_intermediate.gif"
    onclick="setCheckValue( jQuery(this), 'trueValue', '#{checkedValue}')"
    rendered="#{(empty rendered) ? true : rendered}" />

  <h:inputHidden
    id="triStateCheck"
    value="#{value}"
    valueChangeListener="#{controller[valueChangeListener]}">
    <f:attribute name="key" value="#{key}" />
  </h:inputHidden>

</ui:composition>
</html>

The control starts with a very simple script that uses jquery.

    function setCheckValue( img, next, newVal )
    {
      img.css('display', 'none');
      img.siblings('[id$='+next+']').css('display','inline');
      img.siblings('[id$=triStateCheck]').val(newVal);
    }

The script takes three parameters. The current image, the next image, and the new value. It then hides the current image, shows the new image, and sets the value of the control to the new value.

Each image in the control has a couple of Javascript event handlers. One to show the highlighted version of the current image on mouseover, and another to show the regular version on mouse out. Finally, a third to transition to the next state and set the value on click. You’ll notice that in the falseValue image, the onclick is a bit longer than the others.

    onclick="setCheckValue( jQuery(this),
        '#{value == intermediateValue ? 'intermediateValue' : 'trueValue'}',
        '#{value == intermediateValue ? intermediateValue : checkedValue}')"

This is because if the value doesn’t start at intermediate, then we don’t ever want to hit the intermediate value in transitions. For instance, if the value starts at true or false, the checkbox will function just like a normal checkbox, toggling only between true and false. If the checkbox starts at the intermediate value, then it will go from intermediate to true, then to false, then back to intermediate on subsequent clicks. If this is not the desired behavior, then the conditional statements in the onclick can be removed so that it always goes to intermediate.

The final interesting part of the control is the hidden field that holds the value of the checkbox.

  <h:inputHidden
    id="triStateCheck"
    value="#{value}"
    valueChangeListener="#{controller[valueChangeListener]}">
    <f:attribute name="key" value="#{key}" />
  </h:inputHidden>

This inputHidden control contains the current value of the checkbox. The value is set initially, and then changed through javascript when the checkboxes are clicked. When debugging, it can be helpful to change the inputHidden to a simple input text control so you can inspect it and see the value change. The control will call a value change listener which can use the key attribute to determine which entity the checkbox belongs to. This is valuable when you are stamping the checkboxes ( as in a ui:repeat ) so you know which entity in the collection had the checkbox changed.

In order to use the control, simply save the text to a file and include a pointer to it in your JSF taglib definition. You can modify the image paths for your needs and set the other attributes as they are commented at the top of the file.

Suggestions on how to improve this control? I’d love to hear them.

« Newer PostsOlder Posts »

Theme: Silver is the New Black. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.