This is not a duplicate, but related to thread. Please not, the difference is the use of CDI instead of the traditional ManagedBean concept.
I have a category list producer ...
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import ch.winterraeder.model.Category;
@SessionScoped
public class CategoryListProducer implements Serializable {
private static final long serialVersionUID = 8872019830611018574L;
@Inject
private CategoryRepository categoryRepository;
private List<Category> categories;
// @Named provides access the return value via the EL variable name
// "categories" in the UI (e.g.
// Facelets or JSP view)
@Produces
@ApplicationScoped
@Named(value="categories")
public List<Category> getCategories() {
return categories;
}
public void onCategoryListChanged(
@Observes(notifyObserver = Reception.IF_EXISTS) final Category category) {
retrieveAllCategorysOrderedByName();
}
@PostConstruct
public void retrieveAllCategorysOrderedByName() {
categories = categoryRepository.findAllOrderedByName();
}
}
... which delivers the categories for my ...
<h:selectManyListbox id="parentCategories" value="#{newCategory.parentCategories}">
<f:selectItems value="#{categories}" var="c" itemLabel="#{c.categoryName}" itemValue="#{c}"/>
<!-- CategoryConverter is Applied here -->
<f:converter converterId="categoryConverter" />
</h:selectManyListbox >
When I press the add button ...
<h:commandButton id="add" action="#{categoryController.add}"
value="Hinzufügen" styleClass="addButton" />
... I can successfully persist a new category but it fails if I select a parent category whith the CategoryController class.
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Produces;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import ch.winterraeder.data.CategoryRepository;
import ch.winterraeder.model.Category;
@Named
@SessionScoped
public class CategoryController implements Serializable {
private static final long serialVersionUID = -6377428573950716575L;
@Inject
private FacesContext facesContext;
@Inject
private CategoryRepository categoryRepository;
@Produces
@Named
private Category newCategory;
@PostConstruct
public void initNewMember() {
newCategory = new Category();
}
public void add() throws Exception {
try {
categoryRepository.register(newCategory);
FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_INFO,
"Registered!", "Registration successful");
facesContext.addMessage(null, m);
initNewMember();
} catch (Exception e) {
String errorMessage = e.getMessage();
FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_ERROR,
errorMessage, "Registration unsuccessful");
facesContext.addMessage(null, m);
}
}
}
The error message is Validation Error: value is not valid.
It's interesting, I can do the exact thing with @ManagedBean, but I fail to do it with the CDI.
I must be related to the initially linked threat and the select list is different when submitting the values. What am I missing?
Here is the model class to get an understanding of the data.
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.xml.bind.annotation.XmlRootElement;
import org.hibernate.validator.constraints.NotBlank;
@Entity
@XmlRootElement
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotBlank
private String categoryName;
private boolean active = true;
@ManyToMany(fetch=FetchType.LAZY, cascade={CascadeType.DETACH, CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST})
private List<Category> parentCategories = new LinkedList<Category>();
@ManyToMany(mappedBy="parentCategories")
private List<Category> childCategories = new LinkedList<Category>();
private String imagePath;
@SuppressWarnings("unused")
private Date dateCreated;
@SuppressWarnings("unused")
private Date dateModified;
/***** EVENT HOOKS *****/
@PrePersist
protected void onCreate() {
dateCreated = new Date();
}
@PreUpdate
protected void onUpdate() {
dateModified = new Date();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String category) {
this.categoryName = category;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public List<Category> getParentCategories() {
return parentCategories;
}
public void setParentCategories(List<Category> parentCategories) {
this.parentCategories = parentCategories;
}
public List<Category> getChildCategories() {
return childCategories;
}
public void setChildCategories(List<Category> childCategories) {
this.childCategories = childCategories;
}
public String getImagePath() {
return imagePath;
}
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (active ? 1231 : 1237);
result = prime * result
+ ((categoryName == null) ? 0 : categoryName.hashCode());
result = prime * result
+ ((childCategories == null) ? 0 : childCategories.hashCode());
result = prime * result
+ ((dateCreated == null) ? 0 : dateCreated.hashCode());
result = prime * result
+ ((dateModified == null) ? 0 : dateModified.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result
+ ((imagePath == null) ? 0 : imagePath.hashCode());
result = prime
* result
+ ((parentCategories == null) ? 0 : parentCategories.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Category other = (Category) obj;
if (active != other.active)
return false;
if (categoryName == null) {
if (other.categoryName != null)
return false;
} else if (!categoryName.equals(other.categoryName))
return false;
if (childCategories == null) {
if (other.childCategories != null)
return false;
} else if (!childCategories.equals(other.childCategories))
return false;
if (dateCreated == null) {
if (other.dateCreated != null)
return false;
} else if (!dateCreated.equals(other.dateCreated))
return false;
if (dateModified == null) {
if (other.dateModified != null)
return false;
} else if (!dateModified.equals(other.dateModified))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (imagePath == null) {
if (other.imagePath != null)
return false;
} else if (!imagePath.equals(other.imagePath))
return false;
if (parentCategories == null) {
if (other.parentCategories != null)
return false;
} else if (!parentCategories.equals(other.parentCategories))
return false;
return true;
}
}
Here is my converter ...
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
import javax.inject.Inject;
import ch.winterraeder.data.CategoryRepository;
import ch.winterraeder.model.Category;
@FacesConverter(forClass=Category.class, value="categoryConverter")
public class CategoryConverter implements Converter {
@Inject
CategoryRepository catRepo;
@Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
if (value.isEmpty()) {
return null;
}
Long id = new Long(value);
try{
Category cat = catRepo.findById(id);
return cat;
}catch (Exception ex){
throw new ConverterException(new FacesMessage("Category cannot be fetched with value " + value));
}
}
@Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
if (value == null || value.toString().isEmpty()) {
return "";
}
Category cat = (Category) value;
return cat.getId().toString();
}
}