4 Replies Latest reply on Jan 14, 2009 4:01 PM by cmoerz.seam.ny-central.org

    h:selectOne returns null

    felixk2

      I've been using Seam for almost a year now and i still run into the stupidest problems...You'd think that after a year i would have the experience to know what to do, but i don't have a clue. But here is my situation:


      I have an h:selectOne:


      <rich:column>
                        <f:facet name="header">Duplicate</f:facet>
                        <h:selectOneMenu value="#{duplicateIVRScript.selectedIVRForDup}" rendered="#{not empty ivrListForDup}">
                             <s:selectItems value="#{ivrListForDup}" var="ivr" label="#{ivr.serviceCode}" noSelectionLabel="Select..."/>
                             <s:convertEntity />
                       </h:selectOneMenu>
                        <s:button value="Duplicate" action="#{duplicateIVRScript.duplicateIVR(ivrScript)}"></s:button>
                   </rich:column>



      I run the debugger and duplicateIVRScript.selectedIVRForDup is always NULL!!


      Here is the bean:


      @Stateful
      @Scope(ScopeType.SESSION)
      @Name("duplicateIVRScript")
      public class DuplicateIVRScriptAction implements DuplicateIVRScript
      {
           @In(value="#{productAdmin.customerProduct}")
           private CustomerProduct customerProduct;
           
           @DataModel
           private List<SEIVR> ivrListForDup;
           
           private SEIVR selectedIVRForDup;
           
           @PersistenceContext
           private EntityManager em;
           
           @Logger
           private Log log;
           
           @In
           private FacesMessages facesMessages;
      
           @Factory("ivrListForDup")
           public void populateIVRList()
           {
                ivrListForDup = (List) em.createQuery("FROM SEIVR seivr WHERE seivr.customerProduct=:cp")
                                       .setParameter("cp", customerProduct)
                                       .getResultList();
           }
           
           public void duplicateIVR(SEIVRScript script)
           {
                SEIVRScript s = new SEIVRScript();
                s.setSeIVR(selectedIVRForDup);
                s.setScriptXML(script.getScriptXML());
                s.setScriptDescription(script.getScriptDescription());
                s.setIsDefaultAsBool(false);
                
                em.persist(s);
           }
           
           public void setIvrListForDup(List<SEIVR> ivrListForDup)
           {
                this.ivrListForDup = ivrListForDup;
           }
      
           public List<SEIVR> getIvrListForDup()
           {
                return ivrListForDup;
           }
      
           public void setSelectedIVRForDup(SEIVR selectedIVRForDup)
           {
                this.selectedIVRForDup = selectedIVRForDup;
           }
      
           public SEIVR getSelectedIVRForDup()
           {
                return selectedIVRForDup;
           }
      
           @Remove @Destroy
           public void destroy()
           {          
           }
      }



      What the heck am i doing wrong? Can anyone spot it?


      Thanks,
      Felix


        • 1. Re: h:selectOne returns null
          cmoerz.seam.ny-central.org

          Did you ever find a solution to this? I'm running into pretty much precisely the same problem. I get it running so far that the drop down is filled but it doesn't select the right element and it always results in Seam thinking that (null) was selected.


          The only reason my app isn't crashing is that I prevent null from being set on the specific @NotNull item.


          my components.xml


          <?xml version="1.0" encoding="UTF-8"?>
          <components xmlns="http://jboss.com/products/seam/components"
                      xmlns:core="http://jboss.com/products/seam/core"
                      xmlns:persistence="http://jboss.com/products/seam/persistence"
                      xmlns:drools="http://jboss.com/products/seam/drools"
                      xmlns:bpm="http://jboss.com/products/seam/bpm"
                      xmlns:security="http://jboss.com/products/seam/security"
                      xmlns:mail="http://jboss.com/products/seam/mail"
                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xmlns:ui="http://jboss.com/products/seam/ui"
                      xmlns:framework="http://jboss.com/products/seam/framework"
                      xsi:schemaLocation=
                          "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd 
                           http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.1.xsd 
                           http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.1.xsd
                           http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.1.xsd
                           http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd
                           http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.1.xsd
                           http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">
          
             <core:init debug="@debug@" jndi-pattern="@jndiPattern@"/>
               
             <core:manager concurrent-request-timeout="500" 
                           conversation-timeout="120000" 
                           conversation-id-parameter="cid"
                           parent-conversation-id-parameter="pid"/>
              
             <persistence:managed-persistence-context name="entityManager"
                                               auto-create="true"
                                persistence-unit-jndi-name="java:/HermesEntityManagerFactory"/>                          
          
             <security:rule-based-permission-resolver security-rules="#{securityRules}"/> 
             
             <!-- security:identity authenticate-method="#{authenticator.authenticate}" remember-me="true" /-->
             <!-- security:identity authenticate-method="#{identity.login}" remember-me="true" / -->
             <security:identity-manager identity-store="#{jpaIdentityStore}" />
             <security:jpa-identity-store name="jpaIdentityStore"
                       user-class="org.nycentral.hermes.entity.User"
                       role-class="org.nycentral.hermes.entity.Role" />
             <security:jpa-permission-store 
                       user-permission-class="org.nycentral.hermes.entity.UserPermission" />
          
             <drools:rule-base name="securityRules">
                <drools:rule-files>
                   <value>/security.drl</value>
                </drools:rule-files>
             </drools:rule-base>
             
             <event type="org.jboss.seam.security.notLoggedIn">
                <action execute="#{redirect.captureCurrentView}"/>
             </event>
             <event type="org.jboss.seam.security.loginSuccessful">
                <action execute="#{redirect.returnToCapturedView}"/>
             </event>
             
             <mail:mail-session host="localhost" port="2525" username="test" password="test" />
          
               <ui:jpa-entity-loader entity-manager="#{entityManager}" />
               
               <framework:entity-query name="allCountries" 
                    ejbql="select country from Country country" 
                    order="country.name" />        
                  
             <!-- For use with jBPM pageflow or process management -->
             <!--  
             <bpm:jbpm>
                <bpm:process-definitions></bpm:process-definitions>
                <bpm:pageflow-definitions></bpm:pageflow-definitions>
             </bpm:jbpm>
             -->
                
          </components>
          



          The data/entity I want to be rendered in the dropbox:


          package org.nycentral.hermes.entity;
          
          import java.io.Serializable;
          
          import javax.persistence.Entity;
          import javax.persistence.GeneratedValue;
          import javax.persistence.Id;
          import javax.persistence.Table;
          import javax.persistence.Transient;
          
          import org.hibernate.validator.Length;
          import org.hibernate.validator.NotNull;
          
          @Entity
          @Table(name="Countries")
          public class Country implements Serializable {
               private static final long serialVersionUID = -5515171854078135507L;
               //private String          id;
               private Long          id;
               private String          name;
               
               @Id
               @GeneratedValue
               public Long getId() {
                    return this.id;
               }
               public void setId( Long id ) {
                    this.id = id;
               }
               
               /*@Id
               @Length(max=2)
               public String getId() {
                    return id;
               }
               public void setId(String id) {
                    this.id = id;
               }*/
               
               @NotNull
               @Length(max=64)
               public String getName() {
                    return name;
               }
               public void setName(String name) {
                    this.name = name;
               }
               
               @Transient
               @Override
               public String toString() {
                    return this.name;
               }
               
               @Transient
               @Override
               public int hashCode() {
                    // FIXME non-optimal hash-code
                    return (this.id+this.name).hashCode();
               }
               
               @Transient
               @Override
               public boolean equals(Object o) {
                    Country     c;
                    
                    if ( !( o instanceof Country )) {
                         return false;
                    }
                    
                    c = (Country) o;
                    if ( c.getId() == this.getId() ) {
                         return true;
                    }
                    return false;
                    
               }
          }
          



          I initially had a String as primary key and changed that to Long, just to be sure it's not caused by that. However, that didn't work either.


          The xhtml file rendering:


          <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
          <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                             xmlns:ui="http://java.sun.com/jsf/facelets"
                                xmlns:h="http://java.sun.com/jsf/html"
                                xmlns:f="http://java.sun.com/jsf/core"
                                xmlns:s="http://jboss.com/products/seam/taglib"
                          xmlns:a="http://richfaces.org/a4j"
                          xmlns:rich="http://richfaces.org/rich"
                              template="companydetail.xhtml">
          
               <ui:define name="details">
                    <s:decorate id="fieldCompanyName" template="../layout/edit.xhtml">
                         <ui:define name="label">Name:</ui:define>
                         <h:inputText id="CompanyName" value="#{company.name}" required="true" />
                    </s:decorate>
          
                    <s:decorate id="fieldCompanyStreet" template="../layout/edit.xhtml">
                         <ui:define name="label">Street:</ui:define>
                         <h:inputText id="CompanyStreet" value="#{company.address.street}" required="true" />
                    </s:decorate>
          
                    <s:decorate id="fieldCompanyCity" template="../layout/edit.xhtml">
                         <ui:define name="label">City:</ui:define>
                         <h:inputText id="CompanyCity" value="#{company.address.city}" required="true" />
                    </s:decorate>
          
                    <s:decorate id="fieldCompanyZIP" template="../layout/edit.xhtml">
                         <ui:define name="label">ZIP:</ui:define>
                         <h:inputText id="CompanyZIP" value="#{company.address.zip}" required="true" />
                    </s:decorate>
          
                    <s:decorate id="fieldCompanyState" template="../layout/edit.xhtml">
                         <ui:define name="label">State:</ui:define>
                         <h:inputText id="CompanyState" value="#{company.address.state}" required="true" />
                    </s:decorate>
          
                    <s:decorate id="fieldCompanyCountry" template="../layout/edit.xhtml">
                         <ui:define name="label">Country:</ui:define>
                         <h:selectOneMenu id="CompanyCountry" value="#{company.address.country}">
                              <s:selectItems value="#{allCountries}" 
                                   var="choice"
                                   label="#{choice.name}" 
                                   hideNoSelectionLabel="true" />
                              <s:convertEntity />
                         </h:selectOneMenu>
                    </s:decorate>
               </ui:define>
               <ui:define name="actionButtons">
                    <h:commandButton value="Save Changes" action="#{companyEdit.saveCompany}" /> 
                    <h:commandButton value="Cancel" action="#{companyEdit.cancel}" />
               </ui:define>
          </ui:composition>
          



          It's really weird. I don't understand why it's so damn difficult to render a stupid dropdown box??? I even tried to implement my own converter, that didn't work either:


          package org.nycentral.hermes.session.helper;
          
          import java.io.Serializable;
          
          import javax.faces.component.UIComponent;
          import javax.persistence.EntityManager;
          
          import org.jboss.seam.annotations.In;
          import org.jboss.seam.annotations.Name;
          import org.jboss.seam.annotations.Transactional;
          import org.jboss.seam.annotations.faces.Converter;
          import org.jboss.seam.faces.FacesContext;
          import org.nycentral.hermes.entity.Country;
          
          @Name("countryConverter")
          @Converter(forClass = Country.class)
          public class CountryConverter implements Serializable {
               private static final long serialVersionUID = 4329423846512375269L;
               @In
               private EntityManager entityManager;
          
               public Class<?> forClass() {
                    return Country.class;
               }
          
               @Transactional
               public Object getAsObject(FacesContext context, UIComponent component,
                         String value) {
                    if (value != null) {
                         try {
                              return entityManager.find(Country.class, Long.parseLong( value ));
                         } catch ( NumberFormatException e ) {
                              return null;
                         }
                    }
                    return null;
               }
          
               public String getAsString(FacesContext context, UIComponent component,
                         Object value) {
                    if (value instanceof Country) {
                         Country c = (Country) value;
                         return c.getId().toString();
                    } else {
                         return null;
                    }
               }
          }
          



          That thing never gets called. I had a couple of breakpoints set - never was called at all. Does anyone have a clue what could be wrong with this??


          any help is really appreciated


          chris

          • 2. Re: h:selectOne returns null

            One reason that I can think of is it might be failing at validation phase. Do you see the same behavior if you fill in all required fields? I would say add a h:messages on top of your page with a4j:region around it and see the error messages. That might give you a clue. I don't see a h:form either but I guess you have it in your template.


             

            • 3. Re: h:selectOne returns null
              cmoerz.seam.ny-central.org

              I added a a4j and message segment as you suggested, but I didn't get any messages. So I guess my problem stems from some other area. To give you a better overview, I'll post a couple more things.


              First of all, here's the template, as you correctly assumed, my forms tag is in the template:


              <!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:ui="http://java.sun.com/jsf/facelets"
                  xmlns:h="http://java.sun.com/jsf/html"
                  xmlns:f="http://java.sun.com/jsf/core"
                  xmlns:rich="http://richfaces.org/rich"
                  xmlns:s="http://jboss.com/products/seam/taglib"
                  xmlns:a4j="http://richfaces.org/a4j">
              
              <ui:composition template="../layout/template.xhtml">
                   <ui:define name="body">
                        <div class="section">
                             <h1>#{company.name}</h1>
                        </div>
                        
                        <rich:panel id="contactTable">
                             <f:facet name="header">#{messages['org.nycentral.labels.ContactData']}</f:facet>
                             <a4j:region>
                                  <h:messages />
                             </a4j:region>
                             
                             <h:panelGrid columns="2">
                                  <ui:insert name="details" />
                             </h:panelGrid>
                        </rich:panel>
                        
                        <div class="actionButtons">
                             <h:form id="manipulateForm">
                                  <ui:insert name="actionButtons" />
                             </h:form>
                        </div>
                   </ui:define>
              </ui:composition>
                  
              </html>
              



              And there's my updated stateful session bean that handles the whole thing:


              package org.nycentral.hermes.session;
              
              import static javax.ejb.TransactionAttributeType.REQUIRES_NEW;
              import static javax.persistence.PersistenceContextType.EXTENDED;
              
              import java.util.List;
              
              import javax.ejb.Remove;
              import javax.ejb.Stateful;
              import javax.ejb.TransactionAttribute;
              import javax.persistence.EntityManager;
              import javax.persistence.PersistenceContext;
              
              import org.jboss.seam.ScopeType;
              import org.jboss.seam.annotations.Begin;
              import org.jboss.seam.annotations.Create;
              import org.jboss.seam.annotations.Destroy;
              import org.jboss.seam.annotations.End;
              import org.jboss.seam.annotations.In;
              import org.jboss.seam.annotations.Logger;
              import org.jboss.seam.annotations.Name;
              import org.jboss.seam.annotations.Out;
              import org.jboss.seam.annotations.Scope;
              import org.jboss.seam.annotations.security.Restrict;
              import org.jboss.seam.core.Events;
              import org.jboss.seam.log.Log;
              import org.nycentral.hermes.entity.Company;
              import org.nycentral.hermes.entity.Country;
              
              // FIXME address doesn't show country when reloading or coming back to view screen
              
              // TODO try to add a string property and just use itemValue to get the listing of countries
              
              @Stateful
              @Scope(ScopeType.CONVERSATION)
              @Name("companyEdit")
              @Restrict("#{identity.loggedIn}")
              @TransactionAttribute(REQUIRES_NEW)
              public class CompanyEditAction implements CompanyEdit {
                   //@PersistenceContext(type=EXTENDED)
                   @In
                   private EntityManager          entityManager;
              
                   @Logger
                   private Log                         log;
                   
                   @In @Out
                   private Company                    company;
                   
                   private List<Country>          countries;
                   
                   @In
                   private Events                     events;
                    
                   @SuppressWarnings("unchecked")
                   @Create
                   public void init() {
                        /*if ( this.getCountrySelector() == null ) {
                             this.setCountrySelector( (CountrySelector) Component.getInstance( CountrySelector.class )); 
                        }*/
                        
                        if ( this.getCountries() == null ) {
                             this.countries = entityManager.createQuery( "from Country" ).getResultList();
                        }
                        
                        // transfer country id into helper variable
                        log.debug( this.getClass().getSimpleName() + " initialized." );
                   }
                   
                   @Begin
                   public void editCompany(Company company) {
                        this.company = company;
                        //this.getCountrySelector().setSelection( this.company.getAddress().getCountry().getId() );
                   }
                   
                   @End
                   public void saveCompany() {
                        log.debug( "Starting {0}.saveCompany()", this.getClass().getSimpleName() );
                        log.debug( "Company.address = {0}", this.getCompany().getAddress() );
                        log.debug( "Company.address.country = {0}", this.getCompany().getAddress().getCountry() );
                        
                        //String selection = this.countrySelector.getSelection();
                        //log.debug( "Current string selection: {0}", selection );
              
                        //this.getCompany().getAddress().setCountry( this.countrySelector.getCountry() );
                        if ( this.getCompany().getAddress().getCountry() == null ) {
                             log.error( "Unable to select appropriate country entry on {0}", this.getCompany() );
                        }
                        
                        entityManager.merge( this.getCompany() );
                        events.raiseTransactionSuccessEvent("companySaved");
                   }
                   
                   public Company getCompany() {
                        return this.company;
                   }
                   
                   /*public Converter getCountryConverter() {
                        return new EntityConverter() {
                             private static final long serialVersionUID = -6412404845003367238L;
              
                             @Override
                             public String getAsString(FacesContext facesContext,
                                       UIComponent cmp, Object value) throws ConverterException {
                                  if ( value instanceof Country) {
                                       Country     c = (Country) value;
                                       
                                       return c.getId();
                                  }
                                  
                                  return value+"";
                             }
              
                             @Override
                             public Object getAsObject(FacesContext facesContext,
                                       UIComponent cmp, String value) throws ConverterException {
              
                                  return em.find( Country.class, value );
                             }
                        };          
                   }*/
              
                   @End
                   @Destroy
                   @Remove
                   public void cancel() {
                        log.debug( "{0} terminating.", this.getClass().getSimpleName() );
                   }
              
                   public List<Country> getCountries() {
                        return countries;
                   }
              
              }
              



              This bean has the local interface:


              package org.nycentral.hermes.session;
              
              import java.util.List;
              
              import javax.ejb.Local;
              
              import org.nycentral.hermes.entity.Company;
              import org.nycentral.hermes.entity.Country;
              
              @Local
              public interface CompanyEdit {
              
                   public void init();
                   public void cancel();
              
                   public void editCompany(Company company);
                   public void saveCompany();
              
                   public Company getCompany();
                   public List<Country> getCountries();
              }
              



              I don't understand why this isn't working. It's really frustrating - I've been circling around the same issue for several days now and though everything is programmed the way it is required according to all the documentation and postings, it still doesn't work. It's just beyond me.


              Anyone able to help me? I'm totally lost.


              chris

              • 4. Re: h:selectOne returns null
                cmoerz.seam.ny-central.org

                I just figured it out, I believe. If you look closely at the template, I accidentally put the forms around the buttons only. That means, all the fields - including the dropdown box weren't inside the form and therefore didn't get transmitted.


                Duuuh! Thanks anyways for the reply!


                chris