I have Upgrade my project from JSFContainer 2.2 to JSFContainer 2.3
<p:selectManyListbox id="acl" value="#{controller.process.basisList}" >
<f:selectItems value="#{controller.filinglist}" />
</p:selectManyListbox>
filinglist has class object like ob(1L, 'data1');
basisList with generic type String
when working with JSFContainer 2.2, CDI 1.2 and EL 3.0. it's working fine the Long data has been stored as String in basisList List. I understand the this concept in below URL
Java Reflection API
But in JSFContainer 2.3, CDI 2.0 and EL 3.0. I got the below error
when I run the code
for(String i : basisList) {
System.out.println(i);
}
java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String.
I debug using below code
for(Object i : basisList) {
System.out.println(i.getClass() + " > " + i);
}
The output what am getting is below
class java.lang.Long > 3
This behavior is correct when you upgrade from JSF 2.2 to JSF 2.3. Previously, JSF 2.2 and older didn't auto-convert these values which was actually not the expected behavior.
It's specified in UISelectMany javadoc for JSF 2.3.
Obtain the Converter using the following algorithm:
If the component has an attached Converter, use it.
If not, look for a ValueExpression for value (if any). The ValueExpression must point to something that is:
An array of primitives (such as int[]). Look up the registered by-class Converter for this primitive type.
An array of objects (such as Integer[] or String[]). Look up the registered by-class Converter for the underlying element type.
A java.util.Collection. Do not convert the values. Instead, convert the provided set of available options to string, exactly as done during render response, and for any match with the submitted values, add the available option as object to the collection.
If for any reason a Converter cannot be found, assume the type to be a String array.
The emphasized section of the above blockquote is new since JSF 2.3 (to compare, here's the JSF 2.2 variant of UISelectMany javadoc).
You need to fix your basisList to become of exactly same type as filinglist, or else you'll need to attach an explicit Converter.
You basisList is probably of type <Object> so when you create your for loop with a String Java tries to cast that value into the String variable i. In your case it seems that you have a list partially, or fully filled with primitive long types which can't just be cast to a string. You could write some code like this which would support both cases.
List<Object> basisList = new ArrayList<>();
for (Object o : basisList) {
if (o instanceof String) {
System.out.println(o.toString());
} else if(o instanceof Long){
System.out.println(Long.toString((Long) o));
} else {
System.out.println("Some other type = " + o.toString());
}
}
Related
I am using <p:selectCheckboxMenu> on a List<Long>:
<p:selectCheckboxMenu value="#{bean.selectedItems}">
<f:selectItems value="#{bean.availableItems}" />
</p:selectCheckboxMenu>
private List<Long> selectedItems;
private Map<String, Long> availableItems;
When submitting the form and looping over the selected items as below,
for (int i = 0; i < selectedItems.size(); i++) {
Long id = selectedItems.get(i);
// ...
}
Then I get a class cast exception:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
at com.example.Bean.submit(Bean.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.el.parser.AstValue.invoke(AstValue.java:278)
at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:274)
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:87)
... 27 more
The same problem occurs with <p:selectManyCheckbox>, <p:selectManyMenu>, <h:selectManyMenu>, etc. All multiple-selection components basically. It works fine in <p:selectOneMenu> and all other single-selection components on a single value Long property.
How is this caused and how can I solve it?
Your problem is caused by the following facts:
Java generics are compiletime syntactic sugar and completely absent during runtime.
EL expressions runs during runtime and not during compiletime.
HTTP request parameters are obtained as Strings.
Logical consequence is: EL doesn't see any generic type information. EL doesn't see a List<Long>, but a List. So, when you don't explicitly specify a converter, EL will after obtaining the submitted value as String set it unmodified in the List by reflection means. When you attempt to cast it to Long afterwards during runtime, you'll obviously face a ClassCastException.
The solution is simple: explicitly specify a converter for String to Long. You can use the JSF builtin LongConverter for this which has the converter ID javax.faces.Long. Other builtin converters are listed here.
<p:selectCheckboxMenu ... converter="javax.faces.Long">
Another solution without the need to explicitly specify the converter is to change List<T> type to a T[]. This way the EL will see the Long typed array and thus perform automatic conversion. But this possibly requires changes elsewhere in the model which may not be desirable.
private Long[] selectedItems;
In case you're using a complex object (javabean, entity, POJO, etc) as select item value instead of a standard type like Long for which JSF has builtin converters, then the same rules also apply. You only need to create a custom Converter and explicitly specify it in input component's converter attribute, or rely on forClass if you can use T[]. How to create such a converter is elaborated in Conversion Error setting value for 'null Converter'.
I have an input place that should get a number. I want it to be displayed as empty. However when I run my program, I get 0 or 0.00 as default. How can I make it empty?
This will happen if you bound the value to a primitive instead of its wrapper representation. The primitive int always defaults to 0 and the primitive double always defaults to 0.0. You want to use Integer or Double (or BigDecimal) instead.
E.g.:
public class Bean {
private Integer number;
// ...
}
Then there are two more things to take into account when processing the form submit. First, you need to instruct JSF to interpret empty string submitted values as null, otherwise EL will still coerce the empty string to 0 or 0.0. This can be done via the following context parameter in web.xml:
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Second, if you're using Tomcat/JBoss or any server which uses Apache EL parser under the covers, then you need to instruct it to not coerce null to 0 or 0.0 in case of Number types by the following VM argument (it's unintuitively dealing with Number types as if they are primitives):
-Dorg.apache.el.parser.COERCE_TO_ZERO=false
See also:
The empty string madness
h:inputText which is bound to Integer property is submitting value 0 instead of null
h:inputText which is bound to String property is submitting empty string instead of null
javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL does not work anymore since Java EE 7 / EL 3.0
I don't disagree with previous answers given, but I think you might be looking for
<h:inputText id="number" binding="#{controller.inputBox}" value="#{controller.number}" >
<f:event type="preRenderComponent" listener="#{controller.preRenderInput}" />
</h:inputText>
and inside the controller
private double number; // getters and setters
private UIInput inputBox; // getters and setters
public void preRenderInput() {
// if it is the first request
if(!FacesContext.getCurrentInstance().isPostback()) {
inputBox.setValue("");
}
}
JSF 2.0+
You can use inputNumber in primefaces-extension instead of inputText.
Like this
<pe:inputNumber />
In your XHTML code, assume you have your input field as :
<h:inputText id="newname" value="#{youAction.newName}"
style="width:130px" styleClass="form">
</h:inputText>
In your Managed Bean Action Class, map this field as :
#ManagedBean(name="yourAction")
public class YourAction {
private String newName;
...
// Add Getter & Setter Methods
...
}
By doing in this way, you won't get 0 or 0.00 as default value in your input fields.
I have an input place that should get a number. I want it to be displayed as empty. However when I run my program, I get 0 or 0.00 as default. How can I make it empty?
This will happen if you bound the value to a primitive instead of its wrapper representation. The primitive int always defaults to 0 and the primitive double always defaults to 0.0. You want to use Integer or Double (or BigDecimal) instead.
E.g.:
public class Bean {
private Integer number;
// ...
}
Then there are two more things to take into account when processing the form submit. First, you need to instruct JSF to interpret empty string submitted values as null, otherwise EL will still coerce the empty string to 0 or 0.0. This can be done via the following context parameter in web.xml:
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Second, if you're using Tomcat/JBoss or any server which uses Apache EL parser under the covers, then you need to instruct it to not coerce null to 0 or 0.0 in case of Number types by the following VM argument (it's unintuitively dealing with Number types as if they are primitives):
-Dorg.apache.el.parser.COERCE_TO_ZERO=false
See also:
The empty string madness
h:inputText which is bound to Integer property is submitting value 0 instead of null
h:inputText which is bound to String property is submitting empty string instead of null
javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL does not work anymore since Java EE 7 / EL 3.0
I don't disagree with previous answers given, but I think you might be looking for
<h:inputText id="number" binding="#{controller.inputBox}" value="#{controller.number}" >
<f:event type="preRenderComponent" listener="#{controller.preRenderInput}" />
</h:inputText>
and inside the controller
private double number; // getters and setters
private UIInput inputBox; // getters and setters
public void preRenderInput() {
// if it is the first request
if(!FacesContext.getCurrentInstance().isPostback()) {
inputBox.setValue("");
}
}
JSF 2.0+
You can use inputNumber in primefaces-extension instead of inputText.
Like this
<pe:inputNumber />
In your XHTML code, assume you have your input field as :
<h:inputText id="newname" value="#{youAction.newName}"
style="width:130px" styleClass="form">
</h:inputText>
In your Managed Bean Action Class, map this field as :
#ManagedBean(name="yourAction")
public class YourAction {
private String newName;
...
// Add Getter & Setter Methods
...
}
By doing in this way, you won't get 0 or 0.00 as default value in your input fields.
I'm trying to put an autocomplete that fetches suggestions as a list of Entry<String, Integer>
<p:autoComplete completeMethod="#{suggester.suggestTopics}"
var="x1" itemLabel="#{x1.key}" itemValue="#{x1.value.toString()}"
value="#{topicController.selected}" />
Manged bean code is as follows:
private int selected;
public int getSelected() {
return selected;
}
public void setSelected(int selected) {
this.selected= selected;
}
But this fails saying the Integer class doesn't have method/property named key. If I remove the value attribute from autocomplete then it starts working properly. But when I put value attribute it starts expecting that the object inside var should be of the same type as that inside value attribute. I believe/expect it should be that the object inside itemValue should be of the same type as that inside value attribute.
I want to use POJOs for suggestions but pass just the entity Id to the value
Using :
Primefaces 3.1
JSF 2.1.6
I believe/expect it should be that the object inside itemValue should
be of the same type as that inside value attribute.
Yes this makes sense, and it is the same in the primefaces showcase:
<p:autoComplete value="#{autoCompleteBean.selectedPlayer1}"
id="basicPojo"
completeMethod="#{autoCompleteBean.completePlayer}"
var="p" itemLabel="#{p.name}" itemValue="#{p}"
converter="player" forceSelection="true"/>
As you see is var="p" and itemValue="#{p} where p is an instance of Player. And selectedPlayer1 is also an instance of Player.
I don't know if it works with a Map since the Primefaces example is called "Pojo support" and the suggestions should be a List of elements of the same type as in the value attribute.
I think you want to use the Simple auto complete , but instead you looked at the wrong example on the showcase of the Pojo Support
x1 refers to the int selected - while it expect to be referred to a POJO (with key and value properties.) , that's why you get the message
Integer class doesn't have method/property named key
Or simple use the Simple auto complete
As commented to Matt you dont need to rebuild Player(Pojo) from Db. You can set simply id property of Player(Pojo) and in action method may be utilize this id to fetch it from DB.
In your case in convertor you might do
Entry<String, Integer> e = new Entry<String, Integer>();
e.setId(value) // where value is passed in to convertor in method getAsObject.....
This value will be set to private Entry<String, Integer> selected
I have used Pojo autocomplete but not tried with generic classes.
Hope this helps.
I know the question is outdated but I've had the same problem.
The point is that you have to assign var to p (var="p"). I think it's terribly unobvious (documentation doesnot mention it has to be that way) 'cause I thought I can assign any var name I want.
I'm storing value expressions in a JSF component with the f:attribute tag, e.g.:
<h:inputText ...>
<f:attribute name="myId1" value="#{bean.prop1}" />
<f:attribute name="myId2" value="#{bean.prop2}" />
<f:attribute name="myId3" value="#{bean.prop3}" />
</h:inputText>
Is there a way to access all of those value expressions programmatically? (without knowlegde of the names myId1, myId2,...)
Section 9.4.2 of the JSF 2.1 specification says that those values are stored "in the component’s ValueExpression Map".
That's the only occurrence of the term "ValueExpression Map" in the complete spec.
How do I access that map?
In the UIcomponent's Method getValueExpression() of the Jboss/Mojarra implementation the map
getStateHelper().get(UIComponentBase.PropertyKeys.bindings)
is used to obtain a single value expression.
I guess that map is a super set of the "ValueExpression Map"?
Can I be sure that all implementations and all inherited (standard) components use that map to store ValueExpressions?
Thanks.
In theory you should be able to see them all by UIComponent#getAttributes():
Map<String, Object> attributes = component.getAttributes();
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
System.out.printf("name=%s, value=%s%n", entry.getKey(), entry.getValue());
}
However, that doesn't work the way as you'd expect. It only returns static attributes. This does not seem to ever going to be fixed/implemented. See also JSF issue 636. I'd suggest to stick to attribtues with predefinied prefix and an incremental numerical suffix, like as you've presented in your example. That's also what I've always used to pass additional information from the component on to custom validators and converters. You can just collect them as follows:
Map<String, Object> attributes = component.getAttributes();
List<Object> values = new ArrayList<Object>();
for (int i = 1; i < Integer.MAX_VALUE; i++) {
Object value = attributes.get("myId" + i);
if (value == null) break;
values.add(value);
}
System.out.println(values);
An alternative to the answer given by BalusC might be to use nested facets or UIParameter components. Facets can be retrieved as a map using getFacets but you probably need to put an additional UIOutput inside each facet to access its value expression.
Nested UIParameters can be accessed by iterating over the components children and checking for instanceof UIParameter. UIParameters have name and value attributes and so could be easily converted to a map.
I have used parameters in a custom component, but I'm not sure how a standard UIInput like in your example reacts to these.
BalusC is right. UIComponent#getAttributes().get(name) gets values from both places - at first from attributes map and then if not found from "value expression map". To put some value you have to call UIComponent#setValueExpression(name, ValueExpression). If value is literal, it gets stored into the attribute map, otherwise into the "value expression map". Everything is ok then.