JSF FacesConverter ELResolver.getValue returns null - jsf

I am trying to use a converter for an input form where I use some "selectOneMenu" components.
When I try to obtain my controller object inside converter's "getAsObject" method using:
facesContext.getApplication().getELResolver().
getValue(facesContext.getELContext(), null, "usersController");
I get always null.
This is my converter class which is nested inside UserController class.
#FacesConverter(forClass = User.class, value="userConverter")
public static class UsersControllerConverter implements Converter {
#Override
public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
if (value == null || value.length() == 0) {
return null;
}
UserController controller = (UserController) facesContext.getApplication().getELResolver().
getValue(facesContext.getELContext(), null, "usersController");
System.out.println(value);
System.out.println(getKey(value));
System.out.println(controller);
return controller.getUsers(getKey(value));
}
java.lang.String getKey(String value) {
java.lang.String key;
key = value;
return key;
}
String getStringKey(java.lang.String value) {
StringBuilder sb = new StringBuilder();
sb.append(value);
return sb.toString();
}
#Override
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof User) {
User o = (User) object;
return getStringKey(o.getUsername());
} else {
throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + User.class.getName());
}
}
}
UserController class declaration:
#Named("userController")
#ManagedBean
#SessionScoped
public class UserController implements Serializable{
PS: I am a newbie, I just started learning Java EE/Faces.

This,
UserController controller = (UserController) facesContext.getApplication().getELResolver().
getValue(facesContext.getELContext(), null, "usersController");
is indeed not the right way. The following is the right way:
UserController controller = facesContext.getApplication().evaluateExpressionGet(
facesContext, "#{usersController}", UsersController.class);

Related

Primefaces OrderList and Converter: getAsObject() called with "[object Object]" String

This can be seen as a continuation of my other question.
In my backend view, I have a list of some POJO. The POJO is converted to JSON with its toString() method:
BackingView.java
#Getter #Setter private List<SomePOJO> pojoList = ...
SomePojo.java
#EqualsAndHashCode(callSuper = false, of = {"id"})
public class SomePOJO implements Serializable {
private static final long serialVersionUID = 1L;
#Getter #Setter private Long id;
#Getter #Setter private Long date;
#Getter #Setter private Date name;
....
#Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
//making a readable string-representation of the object to display on the orderList
public String toStringOrderlistDisplay() {
return "Pojo with id " + id + "and so on... "
}
In my frontend, I want to allow the user to sort this pojo-list using Primefaces orderList :
<p:orderList id="ordList" widgetVar="ordList"
value="#{backingview.pojoList }" var="rfg"
controlsLocation="left" responsive="true" itemValue="#{rfg}"
itemLabel="#{rfg.toStringOrderlistDisplay()}" converter="#{pojoConverter}">
</p:orderList>
PojoConverter.java
#Named
#FacesConverter(value = "pojoConverter")
public class PojoConverter implements Converter {
#Override
public Pojo getAsObject(FacesContext context, UIComponent component, String value) {
if (value != null && value.trim().length() > 0) {
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(value, RechtsaktFallgeschichte.class);
}
catch (NumberFormatException | IOException ex) {
ex.printStackTrace();
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid Pojo."));
}
}
else { return null; }
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
SomePOJO r = (SomePOJO) value;
if (r != null) {
return r.toString();
}
else { return null; }
}
}
Converting to JSON seems to work just fine, if I print the output of the getAsString() method, everything looks as you would expect. However, when the getAsObject() method is called, the value-String-parameter always contains "[object Object]" instead of the POJOs JSON representation. What am I doing wrong here?
I don't know if it is possible to achieve what you are trying to do, but a better way (and the working one) is to pass the POJO id in the getAsString() method, and convert it back to Pojo using a cache map Map<Long, Pojo> pojoCache.
The converter should look like this:
PojoConverter.java
#Named
#FacesConverter(value = "pojoConverter")
public class PojoConverter implements Converter<Pojo> {
#Inject
private PojoService pojoService;
#Override
public Pojo getAsObject(FacesContext context, UIComponent component, String value) {
if (value != null && value.trim().length() > 0) {
return pojoService.getPojoCache().get(Long.valueOf(value));
} else {
return null;
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Pojo value) {
if (value != null) {
return String.valueOf(value.getId());
} else {
return null;
}}
}
For a more detailed explanation check this: p:orderList converter getAsObject() doesn't call Object.toString()

javax.el.PropertyNotFoundException: itemLabel="#{projet.nomProjet}": Property 'nomProjet' not found on type java.lang.String

I'm trying to apply a JSF converter to an Entity inside a selectOneMenu,
but the converter is not recognized, I get this warning in my xhtml file,
<<"nomProjet" cannot be resolved>>
and when I run the application I'm getting Error HTTP 500 :
itemLabel="#{projet.nomProjet}": Property 'nomProjet' not found on type java.lang.String
Here is my code:
The selectOneMenu in my view
<p:selectOneMenu id="projet" converter="projetConverter" value="# {affectation.selectedProjet}" >
<f:selectItems var="projet" itemValue="#{projet}" itemLabel="#{projet.nomProjet}" value="#{affectation.projetsAffectablesCollaborateur()}" />
</p:selectOneMenu>
The converter
#Component
#FacesConverter("projetConverter")
public class ProjetConverter implements Converter {
#Autowired
private ProjetRepository projetRepository;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
Projet projet = projetRepository.findByIdProjet(Long.valueOf(value));
return projet;
} catch (NumberFormatException exception) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur de conversion", "ID de projet invalide"));
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (value instanceof Projet) {
return String.valueOf(((Projet) value).getIdProjet());
} else {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erreur de conversion", "Instance de projet invalide"));
}
}
}
And my Entity :
#Entity
#NamedQuery(name = "Projet.findAll", query = "SELECT p FROM Projet p")
public class Projet implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long idProjet;
private String nomProjet;
#Transient
private List<Role> listRoles = new ArrayList<Role>();
public List<Role> getListRoles() {
return listRoles;
}
public void setListRoles(List<Role> listRoles) {
this.listRoles = listRoles;
}
// bi-directional many-to-one association to AffectationProjetRole
#OneToMany(mappedBy = "projet")
private List<AffectationProjetRole> affectationProjetRoles;
public Projet() {
}
public Projet(String nomProjet) {
this.nomProjet = nomProjet;
}
public long getIdProjet() {
return this.idProjet;
}
public void setIdProjet(long idProjet) {
this.idProjet = idProjet;
}
public String getNomProjet() {
return this.nomProjet;
}
public void setNomProjet(String nomProjet) {
this.nomProjet = nomProjet;
}
public List<AffectationProjetRole> getAffectationProjetRoles() {
return this.affectationProjetRoles;
}
public void setAffectationProjetRoles(List<AffectationProjetRole> affectationProjetRoles) {
this.affectationProjetRoles = affectationProjetRoles;
}
public AffectationProjetRole addAffectationProjetRole(AffectationProjetRole affectationProjetRole) {
getAffectationProjetRoles().add(affectationProjetRole);
affectationProjetRole.setProjet(this);
return affectationProjetRole;
}
public AffectationProjetRole removeAffectationProjetRole(AffectationProjetRole affectationProjetRole) {
getAffectationProjetRoles().remove(affectationProjetRole);
affectationProjetRole.setProjet(null);
return affectationProjetRole;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (idProjet ^ (idProjet >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Projet other = (Projet) obj;
if (idProjet != other.idProjet)
return false;
return true;
}
}
How is this caused and how can I solve it?
itemLabel="#{projet.nomProjet}": Property 'nomProjet' not found on type java.lang.String
This error message tells that #{projet} is during runtime actually a java.lang.String. Let's look where's #{projet} is coming from.
<f:selectItems value="#{affectation.projetsAffectablesCollaborateur()}"
var="projet" itemValue="#{projet}" itemLabel="#{projet.nomProjet}" />
Thus, #{affectation.projetsAffectablesCollaborateur()} actually returned a List<String>. If this is unexpected, then beware of generic type erasure and doublecheck all unchecked casts that the generic type is not incorrectly assumed. Generally, the mistake is in the persitence layer. For example, when you mistakenly execute the query SELECT p.nomProject FROM Project p instead of SELECT p FROM Project p and then performed an unchecked cast against List<Projet> instead of List<String>.
Do note that rendering item labels doesn't involve the converter at all, so showing and blaming it is unnecessary. The converter is only used on item values.

p:orderList converter getAsObject() doesn't call Object.toString()

I've written a custom converter as follows:
#FacesConverter(value = "orderListConverter")
public class OrderListConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Object ret = null;
if (component instanceof OrderList) {
Object list = ((OrderList) component).getValue();
ArrayList<ExampleEntity> al = (ArrayList<ExampleEntity>) list;
for (Object o : al) {
String name = "" + ((ExampleEntity) o).getName();
if (value.equals(name)) {
ret = o;
break;
}
}
}
return ret;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
String str = "";
if (value instanceof ExampleEntity) {
str = "" + ((ExampleEntity) value).getNumber();
}
return str;
}
}
My ExampleEntity is implemented as follows:
public class ExampleEntity {
private String name;
private int number;
public ExampleEntity(String name, int number) {
this.name = name;
this.number = number;
}
#Override
public String toString() {
return "toString(): [name=" + name + ", number=" + number + "]";
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
the orderList-Component from Primefaces looks like that:
<p:orderList value="#{orderListBean.exampleList}"
var="exampleEntity" itemValue="#{exampleEntity}"
converter="orderListConverter">
<p:column style="width:25%">
#{exampleEntity.number}
</p:column>
<p:column style="width:75%;">
#{exampleEntity.name}
</p:column>
</p:orderList>
and the bean is implemented as follows:
#SessionScoped
#ManagedBean(name = "orderListBean")
public class OrderListBean {
private List<ExampleEntity> exampleList;
#PostConstruct
public void init() {
exampleList = new ArrayList<ExampleEntity>();
exampleList.add(new ExampleEntity("nameOne", 1));
exampleList.add(new ExampleEntity("nameTwo", 2));
exampleList.add(new ExampleEntity("nameThree", 3));
exampleList.add(new ExampleEntity("nameFour", 4));
exampleList.add(new ExampleEntity("nameFive", 5));
}
public List<ExampleEntity> getExampleList() {
return exampleList;
}
public void setExampleList(List<ExampleEntity> exampleList) {
this.exampleList = exampleList;
}
}
1) when debugging, the value Parameter of getAsObject() contains
the number of the ExampleEntity, but I had expected the
toString() method of ExampleEntity to be called!
2) What is the correct content for itemValue attribute?
Is it a kind of convention over configuration? Or how does the component
'know', to use the whole object, when inserting exampleEntity into itemValue
Hope everything is clear! Tanks a lot for any explanation!
Converters basically serve to transform values in 2 directions:
Server to client, when the value is rendered.
Client to server, when the value is submitted.
In your getAsString you established, that the string representation, the one which client uses, is exampleEntity's number. So that's what gets rendered to client as a value. And later, when the client submits its value, that value is number. To convert it to the object (server) representation, the getAsObject called with the number as a parameter.
The server can't possibly call getAsObject with exampleEntity.toString(), because it doesn't have the exampleEntity instance at that point, only the submitted number.
To illustrate, this should hold:
obj.equals(conv.getAsObject(ctx, comp, conv.getAsString(ctx, comp, obj)));
getAsObject and getAsString should be inversive in their input and output.
To answer your 2nd question: that depends on your needs. You could say itemValue="#{exampleEntity.number}", but that would make sense only if you're not interested in the exampleEntity itself, i.e. on submit you would get the number from the client and that's all you need for your server-side logic.
#FacesConverter(value = "EntityConverter")
public class EntityConverter implements Converter, Serializable {
private static final long serialVersionUID = 1L;
public EntityConverter() {
super();
}
#Override
public Object getAsObject(final FacesContext context, final UIComponent component, final String value) {
if (value == null) {
return null;
}
return fromSelect(component, value);
}
/**
* #param currentcomponent
* #param objectString
* #return the Object
*/
private Object fromSelect(final UIComponent currentcomponent, final String objectString) {
if (currentcomponent.getClass() == UISelectItem.class) {
final UISelectItem item = (UISelectItem) currentcomponent;
final Object value = item.getValue();
if (objectString.equals(serialize(value))) {
return value;
}
}
if (currentcomponent.getClass() == UISelectItems.class) {
final UISelectItems items = (UISelectItems) currentcomponent;
final List<Object> elements = (List<Object>) items.getValue();
for (final Object element : elements) {
if (objectString.equals(serialize(element))) {
return element;
}
}
}
if (!currentcomponent.getChildren().isEmpty()) {
for (final UIComponent component : currentcomponent.getChildren()) {
final Object result = fromSelect(component, objectString);
if (result != null) {
return result;
}
}
}
if (currentcomponent instanceof OrderList) {
Object items = ((OrderList) currentcomponent).getValue();
List<Object> elements = (List<Object>) items;
for (final Object element : elements) {
if (objectString.equals(serialize(element))) {
return element;
}
}
}
return null;
}
/**
* #param object
* #return the String
*/
private String serialize(final Object object) {
if (object == null) {
return null;
}
return object.getClass() + "#" + object.hashCode();
}
#Override
public String getAsString(final FacesContext arg0, final UIComponent arg1, final Object object) {
return serialize(object);
}
}

p:selectManyMenu report validation error (illegal select options) [duplicate]

This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 7 years ago.
I use primefaces's p:selectManyMenu. When I submit the value, it always report validation error Illegal selection options. Can you help to solve this issue?
JSF code:
<h:outputLabel for="scenario" value="GivenStories:" />
<p:selectManyMenu id="givenstoryselect" value="#{anotherDynaFormController.selectedGivenStory}" converter="scenarioConverter" var="g" style="width:500px;height:70px;align:left;" showCheckbox="true" >
<f:selectItems value="#{anotherDynaFormController.givenStorys}" var="scenario" itemLabel="#{scenario.name}" itemValue="#{scenario}" />
<p:column>#{g.name}</p:column>
</p:selectManyMenu>
Bean class:
//converter
#FacesConverter(value="scenarioConverter")
public class ScenarioConverter implements Converter {
public static List<Scenario> scenarioDB = new ArrayList<Scenario>();
private ScenarioDao scenarioDao = new ScenarioDao();
public Object getAsObject(FacesContext facesContext, UIComponent component, String submittedValue) {
if (submittedValue.trim().equals("")) {
return null;
} else {
try {
int number = Integer.parseInt(submittedValue);
scenarioDB = scenarioDao.findAll();
for (Scenario p : scenarioDB) {
if (p.getId() == number) {
return p;
}
}
} catch(NumberFormatException exception) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Conversion Error", "Not a valid scenario"));
}
}
return null;
}
public String getAsString(FacesContext facesContext, UIComponent component, Object value) {
if (value == null) {
return null;
} else {
return String.valueOf(((Scenario) value).getId());
}
}
}
#ManagedBean
#ViewScoped
public class AnotherDynaFormController extends SuperBean implements Serializable {
private List<Scenario> givenStorys;
private List<Meta> selectedMetas;
private List<Scenario> selectedGivenStory;
private List<ProjectTree> parents;
public DynaFormModel getModel() {
givenStorys = scenarioDao.findAll();
...
}
...
}
Try this generic converter and see if problem still there .
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.WeakHashMap;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
#FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {
private static Map<Object, String> entities = new WeakHashMap<Object, String>();
#Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
synchronized (entities) {
if (!entities.containsKey(entity)) {
String uuid = UUID.randomUUID().toString();
entities.put(entity, uuid);
return uuid;
} else {
return entities.get(entity);
}
}
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
for (Entry<Object, String> entry : entities.entrySet()) {
if (entry.getValue().equals(uuid)) {
return entry.getKey();
}
}
return null;
}
}
thanks a lot. I have find the reason of this issue. I should overwrite the equals and hashcode method in this object. Then it works.
#Override
public boolean equals(Object obj) {
if(obj == null)
return false;
if(!(obj instanceof Scenario))
return false;
return ((Scenario)obj).getId().equals(this.id);
}
#Override
public int hashCode() {
int hash = 1;
return hash * 31 + name.hashCode();
}

selectOneMenu do not render/display value

Made a post earlier about this topic (Display values in drill-down SelectOneMenus). The problem that have is not connected to the Object converters as I first thought.
I have two selectOneMenu depending on each other, Sector -> Category. When I persist the background business object that holds a Sector and Category, everything works as expected. Even after the current user session terminates and the user log on again and edit the previous saved business object.
The problem occur when and the entity manager flushes the objects and user wants to display the Sector, Category again from the database. The Sector selectOneMenu displays its value as it should, but the Category selectOneMenu value is not displayed. Although the backing bean has the correct value from the database.
What makes the selectOneMenuto not display certain persisted values/objects when they are loaded from database instead from in-memory?
Edit.xhtml
<h:outputLabel value="Sector:" />
<h:selectOneMenu id="sectorSelector" value="#{activityController.selected.category.sector}" title="#{bundle.CreateSectorLabel_sectorName}" valueChangeListener="#{activityController.changeSectorMenu}" immediate="true">
<a4j:ajax event="change" execute="#this categoryMenu" render="categoryMenu"/>
<f:selectItems value="#{sectorController.itemsAvailableSelectOne}"/>
</h:selectOneMenu>
<h:outputLabel value="Category:" />
<h:selectOneMenu id="categoryMenu" value="#{activityController.selected.category}" title="#{bundle.CreateSectorLabel_sectorName}"
binding="#{activityController.categoryMenu}"
required="true" requiredMessage="#{bundle.CreateCategoryRequiredMessage_sector}">
<f:selectItems value="#{activityController.categorySelection}"/>
</h:selectOneMenu>
Controller bean for Category
#ManagedBean(name = "categoryController")
#SessionScoped
public class CategoryController implements Serializable{
....
#FacesConverter(forClass = Category.class)
public static class CategoryControllerConverter implements Converter {
#Override
public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
if (value == null || value.length() == 0) {
return null;
}
CategoryController controller = (CategoryController) facesContext.getApplication().getELResolver().
getValue(facesContext.getELContext(), null, "categoryController");
return controller.ejbFacade.find(getKey(value));
}
java.lang.Integer getKey(String value) {
java.lang.Integer key;
key = Integer.valueOf(value);
return key;
}
String getStringKey(java.lang.Integer value) {
StringBuffer sb = new StringBuffer();
sb.append(value);
return sb.toString();
}
#Override
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Category) {
Category o = (Category) object;
return getStringKey(o.getIdCategory());
}
else {
throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + CategoryController.class.getName());
}
}
}
Part of POJO object
...
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idCategory")
private Integer idCategory;
...
#Override
public boolean equals(Object object) {
if (!(object instanceof Category)) {
return false;
}
Category other = (Category) object;
if ((this.idCategory == null && other.idCategory != null) || (this.idCategory != null && !this.idCategory.equals(other.idCategory))) {
return false;
}
return true;
}
Functon for creating the SelectItem[] array
public static SelectItem[] getSelectItems(List<?> entities, boolean selectOne) {
int size = entities.size();
SelectItem[] items;
int i = 0;
if (selectOne) {
items = new SelectItem[size + 1];
items[0] = new SelectItem("", ResourceBundle.getBundle("/resources/Bundle").getString("Select_item"));
i++;
} else {
items = new SelectItem[size];
}
for (Object x : entities) {
items[i++] = new SelectItem(x, x.toString());
}
return items;
}
The problem was that I didn't have the right collection of SelectItems when the user edited a business object. Plus that I added the <f:converter> to explicitly invoke the converter, where I did some changes in the getAsString() (the else part).
Thank you for your time, and hope the post can be useful to someone else.
SelectMenuOne
public String prepareEditOrganisationActivity() {
current = (Activity) organisationActivityItems.getRowData();
Sector sector = current.getCategory().getSector();
CategoryController categoryBean = JsfUtil.findBean("categoryController", CategoryController.class);
categorySelection = categoryBean.categoryItemsBySector(sector);
return "EditActivity";
}
CategoryControllerConverter
...
#Override
public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
if (object == null) {
return null;
}
if (object instanceof Category) {
Category o = (Category) object;
return getStringKey(o.getIdCategory());
}
else {
Category tmp = new Category();
return getStringKey(tmp.getIdCategory());
}
}

Resources