selectOneMenu CDI @Named invalid value problem
ben_utzer Sep 19, 2011 5:51 AMHi guys,
I'm back with another problem that keeps me puzzling.
I have a named session scoped bean CustomerRegistration which has a named producer method getNewCustomer which returns a Customer object. There is also a CustomerListProducer class which produces all customers as list from the database.
On the selectCustomer.xhtml page the user is then able to select one of the customers and submit the selection to the application which then simply prints out the last name of the selected customer.
Now this only works when I reference the selected customer on the facelets page via #{customerRegistration.newCustomer}. When I simply use #{newCustomer} which to my understanding should also work then JSF reports (javax.faces.PROJECT_STAGE=DEVELOPMENT) a validation error complaining about an invalid value whenever I submit the form.
What's going on here?
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>de.beracom</groupId> <artifactId>helloworld_as7</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <seam.bom.version>3.1.0.Beta2</seam.bom.version> <jboss.javaee6.version>2.0.0.Final</jboss.javaee6.version> <jodatime.version>1.6.2</jodatime.version> <arquillian.version>1.0.0.CR4</arquillian.version> <arquillian.jbossas7.version>7.0.1.Final</arquillian.jbossas7.version> <richfaces.version>4.0.0.Final</richfaces.version> <prettyfaces.version>3.3.0</prettyfaces.version> <junit.version>4.8.2</junit.version> <jbossas.7.version>7.0.1.Final</jbossas.7.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.seam</groupId> <artifactId>seam-bom</artifactId> <version>${seam.bom.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.richfaces</groupId> <artifactId>richfaces-bom</artifactId> <version>${richfaces.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>${jboss.javaee6.version}</version> <type>pom</type> <scope>import</scope> <exclusions> <exclusion> <groupId>org.apache.xalan</groupId> <artifactId>xalan</artifactId> </exclusion> </exclusions> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>xalan</groupId> <artifactId>xalan</artifactId> <version>2.7.1</version> </dependency> <dependency> <groupId>org.jboss.seam.servlet</groupId> <artifactId>seam-servlet</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.faces</groupId> <artifactId>seam-faces</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.international</groupId> <artifactId>seam-international</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.catch</groupId> <artifactId>seam-catch</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.security</groupId> <artifactId>seam-security</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.config</groupId> <artifactId>seam-config-xml</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.persistence</groupId> <artifactId>seam-persistence</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.validation</groupId> <artifactId>seam-validation</artifactId> </dependency> <dependency> <groupId>org.jboss.seam.solder</groupId> <artifactId>seam-solder-tooling</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>${jodatime.version}</version> </dependency> <dependency> <groupId>com.ocpsoft</groupId> <artifactId>prettyfaces-jsf2</artifactId> <version>${prettyfaces.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.jboss.seam.transaction</groupId> <artifactId>seam-transaction</artifactId> </dependency> <dependency> <groupId>org.jboss.arquillian.junit</groupId> <artifactId>arquillian-junit-container</artifactId> <version>${arquillian.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.shrinkwrap.resolver</groupId> <artifactId>shrinkwrap-resolver-impl-maven</artifactId> <version>1.0.0-beta-3</version> <scope>test</scope> </dependency> <!-- Import the CDI API, we use provided scope as the API is included in JBoss AS 7 --> <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <scope>provided</scope> </dependency> <!-- Import the Common Annotations API (JSR-250), we use provided scope as the API is included in JBoss AS 7 --> <dependency> <groupId>org.jboss.spec.javax.annotation</groupId> <artifactId>jboss-annotations-api_1.1_spec</artifactId> <scope>provided</scope> </dependency> <!-- Import the Servlet API, we use provided scope as the API is included in JBoss AS 7 --> <dependency> <groupId>org.jboss.spec.javax.servlet</groupId> <artifactId>jboss-servlet-api_3.0_spec</artifactId> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <!-- This plugin config is used to set the Dependencies entry in META-INF/MANIFEST.MF so that in AS7 both JBoss Logging and the JBoss Log manager will be available in the classpath --> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> <archive> <manifestEntries> <Dependencies>org.jboss.logging,org.jboss.logmanager,org.apache.log4j,org.slf4j</Dependencies> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build> </project>
Resources.java:
package util; import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Produces; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceUnit; import org.jboss.seam.solder.core.ExtensionManaged; /** * This class uses CDI to alias Java EE resources, such as the persistence * context, to CDI beans * * <p> * Example injection on a managed bean field: * </p> * * <pre> * @Inject * private EntityManager em; * </pre> */ public class Resources { // use @SuppressWarnings to tell IDE to ignore warnings about field not // being referenced directly @SuppressWarnings("unused") @ExtensionManaged @SessionScoped @Produces @PersistenceUnit private EntityManagerFactory emf; }
CustomerConverter.java:
package util; import java.io.Serializable; import javax.enterprise.context.SessionScoped; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.FacesConverter; import javax.inject.Inject; import javax.persistence.EntityManager; import domain.Customer; @SessionScoped @FacesConverter(forClass = Customer.class) public class CustomerConverter implements Converter, Serializable { private static final long serialVersionUID = -6093400626095413322L; @Inject EntityManager entityManager; @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { Long id = Long.valueOf(value); return entityManager.find(Customer.class, id); } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { return ((Customer) value).getId().toString(); } }
Customer.java:
package domain; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.jboss.seam.solder.core.Veto; @javax.persistence.Entity @Veto public class Customer implements Serializable, Entity { private static final long serialVersionUID = 122193054725297662L; @Column(name = "first_name") private String firstName; @Column(name = "last_name") private String lastName; @Id @GeneratedValue() private Long id; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String toString() { return firstName + ", " + lastName; } @Override public Long getId() { return this.id; } }
CustomerListProducer.java:
package data; import java.io.Serializable; import java.util.List; import javax.annotation.PostConstruct; 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 javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import org.jboss.seam.logging.Category; import org.jboss.seam.logging.Logger; import domain.Customer; @SessionScoped public class CustomerListProducer implements Serializable { @Inject private EntityManager em; private List<Customer> customers; @Inject @Category("helloworld_as7") Logger log; // @Named provides access the return value via the EL variable name // "members" in the UI (e.g., // Facelets or JSP view) @Produces @Named public List<Customer> getCustomers() { return customers; } public void onCustomerListChanged( @Observes(notifyObserver = Reception.IF_EXISTS) final Customer customer) { // retrieveAllCustomersOrderedByName(); log.info(customer.toString()); } @PostConstruct public void retrieveAllCustomersOrderedByName() { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Customer> criteria = cb.createQuery(Customer.class); Root<Customer> customer = criteria.from(Customer.class); // Swap criteria statements if you would like to try out type-safe // criteria queries, a new // feature in JPA 2.0 // criteria.select(member).orderBy(cb.asc(member.get(Member_.name))); criteria.select(customer).orderBy(cb.asc(customer.get("lastName"))); customers = em.createQuery(criteria).getResultList(); } }
CustomerRegistration.java:
package controller; import java.io.Serializable; import javax.annotation.PostConstruct; import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Produces; import javax.inject.Inject; import javax.inject.Named; import org.jboss.seam.logging.Category; import org.jboss.seam.logging.Logger; import domain.Customer; @Named @SessionScoped public class CustomerRegistration implements Serializable { @Inject @Category("helloworld_as7") private Logger log; private Customer newCustomer; @Produces @Named public Customer getNewCustomer() { return newCustomer; } public void selected() { log.info("Customer " + newCustomer.getLastName() + " ausgewählt."); } @PostConstruct public void initNewCustomer() { newCustomer = new Customer(); } public void setNewCustomer(Customer newCustomer) { this.newCustomer = newCustomer; } }
selectCustomer.xhtml (JSF complains about invalid value after submitting the selected customer):
<?xml version="1.0" encoding="UTF-8"?> <!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:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>Auswahl</title> </h:head> <h:body> <h:form> <h:selectOneMenu value="#{newCustomer}"> <f:selectItems value="#{customers}" var="current" itemLabel="#{current.firstName}, #{current.lastName}" /> </h:selectOneMenu> <h:panelGroup id="auswahl"> <h:outputText value="#{newCustomer.lastName}" /> </h:panelGroup> <h:commandButton value="Klick" action="#{customerRegistration.selected}" /> </h:form> </h:body> </html>
selectCustomer.xhtml (working):
<?xml version="1.0" encoding="UTF-8"?> <!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:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>Auswahl</title> </h:head> <h:body> <h:form> <h:selectOneMenu value="#{customerRegistration.newCustomer}"> <f:selectItems value="#{customers}" var="current" itemLabel="#{current.firstName}, #{current.lastName}" /> </h:selectOneMenu> <h:panelGroup id="auswahl"> <h:outputText value="#{newCustomer.lastName}" /> </h:panelGroup> <h:commandButton value="Klick" action="#{customerRegistration.selected}" /> </h:form> </h:body> </html>