Home > Grimoire, JSF > JSF: SelectOneComboBox

JSF: SelectOneComboBox

Post Technology Stack
  • MyFaces 1.1.7 (JSF 1.1 spec)
  • Java 1.5
  • MyFaces Tomahawk 1.1.9
  • ExtJS 3.1 *

Not Again…

JSF, that steaming pile of undocumented, developer-hating obfuscation has yet again soaked up about 16 hours of my time to do something that should have been a three second configuration. Just to reiterate, much like Randall Munroe’s hatred of velociraptors spurs him to evaluate houses based on their ability to repulse dinosauriod attacks, I find myself now evaluating jobs by how close the project requirements are to actually using the JSF framework.

That said, when you’re a contractor, you use the tools selected for you by the almighty architects. In my case, it’s JSF or my paycheck. Being the Alpha Geek that I am, I gratefully welcomed the paycheck an opportunity to learn more about JSF.

What did they do now???

The crux of the problem is right in the title of this post. Combo box. JSF doesn’t have one, and I needed one. Being the Jack of All Tech that I am, I figured it would be relatively simple to grab the ExtJS combobox and front the JSF selectOneMenu control with it. So long as the ExtJS code didn’t monkey with all the javascript and DOM that the JSF selectOneMenu control created, I’d have myself a nice little combobox that worked with JSF. Well, if you’ve been coding for any longer than two minutes, you know just how screwed up things can get right after you utter the words “That was easy!”.

In this case, the problem emerged as an inability to submit the form of which the combobox was a part. Weirder still, submitting was only a problem when I entered in a value that was not in the dropdown list. Armed with Eclipse and 18mg of ritilin, I spent a few hours tracing the problem down to validation which myfaces was perpetrating on the submitted combobox value – validation that was not implemented as an official Validator.

Here I give you a few lines in javax.faces.components.UISelectOne from where the problem stems. Line 69 is perfectly fine, setting off an execution chain that includes getting the list of validators that have been assigned to the component and executing them. That is not where our problem validation happens, and that’s the problem. Line 77, when parsed, creates an iterator for the list of options created for the drop-down. That iterator is then sent to the _SelectItemsUtil.matchValue method which then makes sure that the submitted value for the drop-down component is one of the available options… and there’s the reason I will never get hours of my life back. See, we’ve got code that runs all the validators assigned the UISelect descendant class, then we’ve got what amounts to a hardcoded validation right after that. Isn’t that great!

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
protected void validateValue(FacesContext context, Object value)
{
    super.validateValue(context, value);
 
    if (!isValid() || value == null)
    {
        return;
    }
 
    // selected value must match to one of the available options
    if (!_SelectItemsUtil.matchValue(value, new _SelectItemsIterator(this)))
    {
        _MessageUtils.addErrorMessage(context, this, INVALID_MESSAGE_ID,
                        new Object[] {getId()});
        setValid(false);
    }
}

Showing the code who’s boss

You’d think that would be the end of the combobox quest. I mean, if checking the submitted value against the options is hardcoded into a core-core API class, you’re basically screwed, right? No getting around that… If you answered yes, please stop reading here, turn in your geek badge, and go read yourself some FARK. If you answered by opening up firefox to the myfaces api and started looking for chinks in the API armour, then read on! …Oh – and if your answer was to break out your Wiha precision screwdriver set and pop the hood off your rig, it’s time to switch to decaf.

* A few important notes first. This solution uses an ExtJS combobox for the UI portion of the hack. ExtJS is, unfortunately, a semi-free library. The “semi” part comes in if you use the library for any public facing user-interfaces which are not open source. Of course, the licensing scheme is waaaay more complicated than that, so I suggest you visit ExtJS’s Licensing Overview page to figure out just what your options are. At the time of this writing, I don’t know of any completely open-source combobox solutions that can interact successfully with the underlying generated JSF DOM structures. If you do, leave a comment!!! Otherwise, the Java/JSF portion of this hack takes care of the application side of things and should be usable (or make usable) any UI combobox solution.

Let’s start with the html/jsp code to make the front end of our combobox work:

1
2
3
4
5
6
7
8
9
10
11
12
13
<f:view>
<h:form id="carCreatorForm">
    <t:selectOneMenu id="colourSelect"
                             value="#{car.Colour}"
                             binding="#{selectOneComboHelper.htmlSelectOneMenu}">
        <t:selectItems var="colour"
                             value="#{colourShelf.colours}"
                             itemLabel="#{colour}"
                             itemValue="#{colour}"/>
    </t:selectOneMenu>
...
</form>
</view>

A few assumptions:

  1. colourShelf is an object that holds available colors and is defined as a JSF managed bean, and it has a getColours method that returns a Java List collection of color names.
  2. car is also defined as a JSF managed bean, and has a setColour method.
  3. selectOneComboHelper is defined as a JSF managed bean, and will be discussed shortly.
  4. I dislike JSF immensely.

The above code is standard for creating a selectOneMenu component for MyFaces using the Tomahawk taglibs. The selectOneMenu tag has the value attribute tying the selected value to the “car” managed bean. The selectItems tag creates the option list. In short:

  1. The value attribute says to call the getColours method on the colourShelf managed bean, which in the example’s case returns a Java List collection of colour name strings.
  2. The var attribute states that “colour” will be the variable name in which the string colour names from the list will be stored.
  3. The selectItems tag iterates over the strings in the colour name list, creating new option elements for the select element created by the selectOneMenu tag.

Now that we have our list menu, here’s the code that makes it a combobox:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<html>
<head>
    <!-- Ext relies on its default css so include it here. -->
    <!-- This must come BEFORE javascript includes! -->
    <link rel="stylesheet" type="text/css" href="js/extjs/resources/css/ext-all.css">
 
    <!-- Include here your own css files if you have them. -->
 
    <!-- First of javascript includes must be an adapter... -->
    <script type="text/javascript" src="js/extjs/adapter/ext/ext-base.js"></script>
 
    <!-- ...then you need the Ext itself, either debug or production version. -->
    <script type="text/javascript" src="js/extjs/ext-all-debug.js"></script>
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
    <script type="text/javascript">
 
    // Path to the blank image must point to a valid location on your server
    Ext.BLANK_IMAGE_URL = 'js/extjs/resources/images/default/s.gif';
 
    // Main application entry point
    Ext.onReady(function() {
        var converted = new Ext.form.ComboBox({
            typeAhead: true,
            triggerAction: 'all',
            transform:'carCreatorForm:colourSelect',
            width:135
        });
    });
    </script>
</head>
<body>
...

The above is standard ExtJS – you include all the ExtJS scripts in the head element of the page, and then use the Ext.onReady function to initialise your ExtJS UI components. The secret to getting ExtJS to recognise and transform the JSF select box is to remember that the HTML form element IDs JSF generates are the name of the form first, a colon, and then the ID you assigned the element. In the case of the code above, our form name is “carCreatorForm”, and the ID of the select menu is “colourSelect”, hence “carCreatorForm:colourSelect”.

The ExtJS combobox transform attribute tells the component that it isn’t going to create a combobox from scratch, but will instead create one from an existing select element (I’ve gotta tell ya – I love those guys at ExtJS!). That means when the html page’s body’s “onLoad” event fires, ExtJS will seek out a DOM element with the name “carCreatorForm:colourSelect”, suck all the information out of it, remove it, and replace it with pod-person combobox version.

Managing JSF shortcomings

With the user-interface portion of our little hack done, we turn now to the underlying MyFaces code that so offends our idea of “How Things Should Work”. Honestly, I don’t have time to figure out if this same “bug” exists in other JSF implementations, or even later versions of MyFaces. If not, then you are already home free and the rest of this post only serves to show you how clever I am (I am extremely clever!). If you try the above and find that the page refreshes without any form elements getting bound to their backing beans, however, this next part my serve as a guide, if not an outright shortcut to getting a working combobox.

To recap (damn, this is a long post!), the symptoms of the problem is the lack of anything actually happening when you hit the form’s submit button. The screen refreshes, but none of the form values are bound to their associated backing beans. Another symptom is this nebulous entry in your log files:

DEBUG | 2010-01-12 15:19:18,550 | LifecycleImpl.java:178 | exiting from lifecycle.execute in PROCESS_VALIDATIONS(3) because getRenderResponse is true from one of the after listeners

According to the code, there should have been another error along the lines of:

javax.faces.component.UISelectOne.INVALID

…but no amount of fiddling with Log4j would make it appear.

Anyhoo, that debug log message is the clue to what is really happening. When the code decides that you are vainly trying to trick it by providing a value that wasn’t on the approved option list, it stops the bus at the third JSF lifecycle step of “Process Validations”, turns it around and leaves the way it drove in. Just so you know, the fourth lifecycle step is “Update Model Values”, which is where the form values would have been bound to the backing beans. It’s also where the bathroom was, which is why you get that bloated, unfulfilled feeling when you see the page refresh with no joy for the backing beans.

Giving JSF more “options”

I know this post is aging you where you sit, that your coffee is cold, and that you are starting to worry about deep vein thrombosis, but there’s only two parts to this hack and all the background’s been covered, so just stay with me a few more paragraphs!

I’ve seen very few APIs that were bullet proof – meaning that I couldn’t screw with them. The general rule is the more complex or less well-developed the API, the easier it is to screw with. That axiom in mind, JSF is extremely easy to screw with! Let’s start with a “helper” class, and when I say helper, I mean l33t h4x0r c0Dz:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.facets.examples;
 
import java.util.List;
import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import org.apache.commons.lang.StringUtils;
import org.apache.myfaces.component.html.ext.HtmlSelectOneMenu;
 
public class SelectOneComboBox {
 
    private HtmlSelectOneMenu component;
 
    public HtmlSelectOneMenu getHtmlSelectOneMenu() {
        return this.component;
    }
 
    public void setHtmlSelectOneMenu(HtmlSelectOneMenu component) {
        this.component = component;
        @SuppressWarnings("unchecked")
        // Get _ComponentChildrenList, with list of child components of the SelectOneMenu component.
        List children = component.getChildren();
        if(children==null || children.isEmpty()) {
            // If no children, page is probably just being rendered, not submitted, so nothing
            // needs to be (or can be) done.
            return;
        }
        // Tell myfaces to marshal the submitted values so we can get to them. 
        component.decode(FacesContext.getCurrentInstance());
        // This is the previous value of the component - the last value to be submitted.
        Object value = component.getValue();
        // This is the current value of the component - the value just submitted.
        Object subValue = component.getSubmittedValue();
        if(!(subValue instanceof String)) {
            component.setSubmittedValue(value);
            return;
        }
        String valueStr = (String)subValue;
        // Get the only child of the SelectOneMenu - the SelectItems component.
        UISelectItems itemsComp = (UISelectItems)children.get(0);
        // Get the array of values from the SelectOneMenu.
        SelectItem[] items = (SelectItem[])itemsComp.getValue();
        // Is the submitted value one of the listed option values?
        for(SelectItem item : items) {
            if(!StringUtils.isBlank((String)item.getValue()) && item.getValue().equals(valueStr)) {
                return; // Submitted value is a listed option - nothing needs to be done.
            }
        }
        // Create a new value for the written-in value of the combobox.
        SelectItem newValue = new SelectItem(valueStr,valueStr);
 
        // Create and fill a new array for the existing option values and the one written in value.
        SelectItem[] newItems = new SelectItem[items.length+1];
        for(int i=0;i<items.length; i++) {
            newItems[i] = items[i];
        }
        newItems[items.length] = (new SelectItem(valueStr));
        // Set the new value array on the SelectItems component so the backing list now includes
        // the written-in value.
        itemsComp.setValue(newItems);
        // Tell the component that the submitted value is an object from the backing list.
        component.setSubmittedValue(newValue);
    }
}

For the most part, everything you need to know about the class exists as inline comments in the code above. Regardless of whether the backing list for the SelectItems component contains strings or objects of your own design, the above should work because it is called between the second and third lifecycle steps (2=Apply Request Values, 3=Process Validations). Since the “Process Validations” step is where everything is converted to the underlying object model from the necessary string values for the front-end HTML UI, strings are all you really have to deal with. I would be careful if you are using Enums as your backing list objects, however, as you can’t just create a new Enum member on the fly from a new value.

To get this to work, you need two pieces of code. The first you saw in the jsp code above, specifically the “binding” attribute of the SelectOneMenu component. That snippet hands the select component object to the SelectOneComboBox.setHtmlSelectOneMenu method above. In order for the SelectOneComboBox object to exist, we need to add it as a managed bean to your faces-config file, like so:

<faces-config>
    <managed-bean>
        <managed-bean-name>selectOneComboHelper</managed-bean-name>
        <managed-bean-class>com.facets.examples.SelectOneComboBox</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>
    ...

Congratulations! Not only do you now have a working JSF combo box, but more importantly you have made it through my longest post yet!

  1. January 13th, 2010 at 15:06 | #1

    As a committer for the Apache MyFaces project, I have to say that this is a very, very poor and bad solution for this problem.

    I mean yes, it works, but there are just sooo many things that are against JSF’s specified behavior, e.g. the binding attribute is not for changing the components behavior, but for accessing its data. If you want to change the components behavior, write your own one or just change the component’s renderer – it is really simple!

    Another solution would be to use a JSF component framework like RichFaces, ICEfaces,… which already provides this kind of component.

    • ogradyjd
      January 13th, 2010 at 18:40 | #2

      Yes, writing my own component would have been a viable option, if I were allowed to introduce a new tag library and had the time to write enough javascript, css, and html to have the same functionality as the ExtJS component already has. But I didn’t. Also, I wasn’t allowed to bring in a new JSF UI framework as Tomahawk was already settled upon, and ICEFaces apparently does not have a combobox component, and has been waiting for one for almost three years:

      http://jira.icefaces.org/browse/ICE-2191

      It just seemed easier to have ExtJS surreptitiously use the existing component to give me the functionality I was looking for. As long as the code works and provides a shortcut to getting what you need, why is it a bad thing?

  2. January 14th, 2010 at 06:17 | #3

    @ogradyjd
    Writing your own component does not mean you can’t use the ExtJS component. You just could have written a JSF-wrapper component for the ExtJS combobox. This would be a far better solution.

  3. January 14th, 2010 at 21:13 | #4

    First, it’s clear that you have a solid understanding of JSF
    fundamentals. Adding a combo box component is a great idea. I’ve added
    it to the JSF specification issue tracker
    .

    That said, speaking for the entire expert group, I’m deeply sorry that
    we’ve offended your sensibilities so deeply. Please consider trying JSF
    2.0. It fixes most of the problems the “jsf-sucks” searches find.

    Now, on to your specific problems.

    You say, “That iterator is then sent to the _SelectItemsUtil.matchValue
    method which then makes sure that the submitted value for the drop-down
    component is one of the available options… and there’s the reason I
    will never get hours of my life back.”

    If we didn’t have such a feature, then it would be trivial for malicious
    javascript to modify the value that is submitted and bypass validation.
    The base javax.faces.component.UI* components had to be rock solid
    secure. As you may know JSF is used by Intuit for their Quicken Online
    product. I’ve had engineers from Quicken come up to me at conferences
    and thank me for this very feature. Once again, we see that mileage may
    vary.

    Later on, you say, “…no amount of fiddling with Log4j would make it
    appear”

    That’s because JSF doesn’t use the non-standard Log4J. It uses JDK 1.4
    logging. A quick web search for “jsf logging” shows the docs you need
    as the second hit.

    Thanks again for this useful blog. I just wish it could have been less
    polemical.

    Sincerely,

    Ed Burns

    JSF Spec Lead

    • ogradyjd
      January 15th, 2010 at 11:45 | #5

      I’ll start with thanking you for commenting – any information or suggestions are always welcome. There’s a lot to contemplate in your comment, so I’ll take it one point at a time.

      1) You’ve added combobox to the JSF spec tracker. Hooray for the good guys!

      2)

      That said, speaking for the entire expert group, I’m deeply sorry that
      we’ve offended your sensibilities so deeply.

      I have sensibilities? Look – I’m aware that I might have been slightly less than complementary in my superfluous diatribe regarding the arduous travails I experienced in attempting to remediate my deliverables. The thing is that I like to be brutally honest about what I think when something causes me that much trouble (The blog is called My Tech Hell). The fact is that that little piece of hardcoded validation isn’t called out in any of the JSF documentation I’ve read over the past two weeks, and it required actual runtime tracing through the sourcecode to find. JSF documentation is sparse enough as it is, but that is too serious a little implementation detail to not be called out anywhere.

      3) Regarding the hardcoded validation of the UISelectOne class, I still don’t agree that any validation should be hardcoded at all. I do understand that integrity checking in this case can be absolutely necessary, as you point out in the case of Intuit. We are, however, talking about back end Java infrastructure validation – not front end UI validation. If you stripped out the select item validation code and implemented it as a Validator, then added it by default to descendants of the UISelectOne class, you would have had the same integrity checking you have now, but with the added bonus of allowing implementers to have full control over whether or not they wanted that particular validation. Intuit would still have had their needs met as removing validators is not possible from Javascript, and implementers would gain that much more configurability to get exactly what they need.

      3)

      JSF doesn’t use the non-standard Log4J. It uses JDK 1.4 logging.

      Oooooooohhhh… Yeah, that’s a JSF newb mistake. Between the J2EE container using Log4J and the application code using a mix of SLF4J, commons logging and an internal logging infrastructure, I’m not surprised I missed that. Definitely my mistake. I’ll take a look at the logging config and see if I can get that to output properly, then amend the post with the new info.

      4) Now that I’ve finally delved into JSF, I’ll be more amenable to checking out JSF 2.0. I still think JSF has a ways to go before I’d actually use it for any of my projects however. Just the scoping problems alone were enough to make me hesitant, and I think there’s a lot of concerns that result in most devs using JSF as only a face (no pun intended) for Spring webflow. I found this article here and this article as well especially interesting. Of course, the wheels turn, the code gets better, and Iterative Development wins in the end (except in Microsoft’s case), but as of right now…

  1. No trackbacks yet.

*