JSF feeds a Set<String> into a Set<Foo>

E

Elegie

Hello,

I am currently learning JSF2.0, and have just encountered a behavior I
do not understand. Basically, I have a test case (see below) where I
have a typed set (Set<Foo>) whose content, automatically populated by
JSF, is made of strings (Set<String>). This should not even compile, yet
this runs. What am I missing?

To provide you with a bit of context, my business scenario is as
follows. I have a web form which will be used to create/update users in
my application, and to assign the user with relevant roles. All roles
will be displayed as checkboxes, the checked checkboxes will represent
the roles that the user possesses. The form will be linked to a backing
bean, whose properties will be automatically populated, using EL
expressions. The bean itself will extend a JPA entity (User), which will
then be copied into a regular User object, then sent to some EJB3.1 for
processing (controls and persistence). Having the backing bean extend
the JPA entity is a simple way, for me, to automatically provide
getters/setters to it.

I could probably achieve my goal using the ui-repeat tag, but it seems
that the selectManyCheckBox tag does precisely that, hence my attempt.
While setting up the scenario though, I encountered a problem with the
way JSF would feed my backing bean - putting String objects where I
expect Role objects. I have trimmed down the problem to the following
test case.

The test case is made of three files:
- an XHTML file, with h:selectManyCheckbox, f:selectItems and
h:commandButton,
- a backing bean whose methods are called by the JSF tags, using EL
expressions. In the application, the bean extends the User class, but
not in the test case,
- a Foo class, with a name, and overriden Object methods (toString,
equals and hashCode). In the application, the Foo class is a Role.

I use JBoss Application Server 6.0.0, and my java -version yields
1.6.0_24-b07.

Since I'm learning JSF, I am trying various approaches while exploring
the technology. After all, the more issues I am faced with, the faster I
progress. The test case could therefore look a bit off to experts, and I
apologize in advance if this is the case. I welcome any comment. Also,
if I am in the wrong group, could you redirect me to the appropriate
group? Thank you.


Kind regards,
Elegie.


===
XHTML file
===

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

<h:head>
<title>Test</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
</h:head>

<h:body>
<div>
<h:form>
<h:selectManyCheckbox
value="#{testCase.foos}"
layout="pageDirection">
<f:selectItems
value="#{testCase.allFoos}"
var="f"
itemLabel="#{f.name}" />
</h:selectManyCheckbox>
<h:commandButton value="Test" action="#{testCase.doTest}" />
</h:form>
</div>
</h:body>
</html>



===
Backing bean - TestCase.java
===

package test ;

import java.io.Serializable;
import java.util.ArrayList ;
import java.util.LinkedHashSet ;
import java.util.List ;
import java.util.Set ;
import javax.faces.bean.ManagedBean ;
import javax.faces.bean.RequestScoped ;

@ManagedBean
@RequestScoped
public class TestCase implements Serializable {

private static final long serialVersionUID = 1L ;

private Set<Foo> myFoos ;

public Set<Foo> getFoos() {
this.myFoos = new LinkedHashSet<Foo>() ;
this.myFoos.add(new Foo("Foo1")) ;
this.myFoos.add(new Foo("Foo3")) ;
this.myFoos.add(new Foo("Foo5")) ;
return this.myFoos ;
}

public void setFoos(Set<Foo> foos) {
this.myFoos = foos ;
}

public List<Foo> getAllFoos() {
ArrayList<Foo> myFoos = new ArrayList<Foo>() ;
myFoos.add(new Foo("Foo1")) ;
myFoos.add(new Foo("Foo2")) ;
myFoos.add(new Foo("Foo3")) ;
myFoos.add(new Foo("Foo4")) ;
myFoos.add(new Foo("Foo5")) ;
myFoos.add(new Foo("Foo6")) ;
return myFoos ;
}

public Object doTest() {
System.out.println("myFoos contains " + this.myFoos) ;
if (!this.myFoos.isEmpty()) {
Object[] content = this.myFoos.toArray() ;
Object candidate = content[0] ;

// Candidate is not a Foo, but a String!
System.out.println("Nice to meet you. I am " + candidate) ;
System.out.println("Am I a foo? " + (candidate instanceof Foo)) ;
System.out.println("What am I? " + (candidate.getClass())) ;

// A real Foo should behave like a Foo!
Foo test = new Foo("fooTest") ;
Object fooTest = (Object) test ;
System.out.println("Nice to meet you. I am " + fooTest) ;
System.out.println("Am I a foo? " + (fooTest instanceof Foo)) ;
System.out.println("What am I? " + (fooTest.getClass())) ;
}
return new String("") ;
}
}



===
POJO - Foo.java
===

package test ;

public class Foo {
private String name ;

public String getName() {
return this.name ;
}

public void setName(String name) {
this.name = name ;
}

public Foo(String name) {
this.name = name ;
}

@Override
public boolean equals(Object o) {
if (o==this) return true ;
if (!(o instanceof Foo)) return false ;
Foo f = (Foo) o ;
return f.name == this.name ;
}

@Override
public int hashCode() {
return this.name.hashCode() ;
}

@Override
public String toString() {
return "I'm a Foo, and my name is " + this.name ;
}
}
 
S

Stanimir Stamenkov

Thu, 02 Jun 2011 21:32:09 +0200, /Elegie/:
I am currently learning JSF2.0, and have just encountered a behavior
I do not understand. Basically, I have a test case (see below) where
I have a typed set (Set<Foo>) whose content, automatically populated
by JSF, is made of strings (Set<String>). This should not even
compile, yet this runs. What am I missing?
(...)

You're missing the generics type erasure [1]. JSF doesn't have the
generics type information at run time. A related (but not the same)
issue [2] has been brought to the JSF Dev mailing list.

To resolve the issue you could attach converter to your input
component explicitly, e.g.:

<h:selectManyCheckbox
value="#{testCase.foos}"
converter="#{...}">
...
</h:selectManyCheckbox>

where you bind the converter instance directly, or:

<h:selectManyCheckbox
value="#{testCase.foos}">
<f:converter converterId="..." />
...
</h:selectManyCheckbox>

where you provide a registered converter ID. With JSF 1.2 one
register a custom converter by adding such an element in the
"faces-config.xml" file:

<converter>
<converter-id>test.FooConverter</converter-id>
<converter-class>test.faces.FooConverter</converter-class>
</converter>

JSF 2.0 may provide more convenient annotation placed on the
converter class directly (don't know, I have really no JSF 2
experience, yet).

[1]
http://download.oracle.com/javase/tutorial/java/generics/erasure.html
[2] "JSF & Support for Generic Types... Again..."
<http://java.net/projects/javaserverfaces/lists/dev/archive/2011-04/message/15>
 
E

Elegie

Le 04/06/2011 16:42, Stanimir Stamenkov a écrit :

Hi,
You're missing the generics type erasure [1]. JSF doesn't have the
generics type information at run time.

Ah, I see. I did not know about generics type erasure at all, so that
explains it.
A related (but not the same)
issue [2] has been brought to the JSF Dev mailing list.

I have also submitted the case as a minor bug to the Java.Net
repository, this morning. I wish I had waited for your explanation
before submitting it, though - making a fool of myself, again... I'll
update the entry with a comment.
To resolve the issue you could attach converter to your input component
explicitly, e.g.:

<h:selectManyCheckbox
value="#{testCase.foos}"
converter="#{...}">
...
</h:selectManyCheckbox>

This looks like a fine approach, I will investigate it and try to work
it out properly in my design.

Thank you for all your explanations and suggestions!

Kind regards,
Elegie.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top