1 2 Previous Next 19 Replies Latest reply on Mar 4, 2009 10:14 AM by Leon Li

    selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".

    Leon Li Newbie

      When I use s:selectItems and f:converter in a h:selectOneMenu, it can't work as expected.


      My code is as belows:
      Profile Edit Backing Bean




      @Name("profileController")
      @Scope(ScopeType.PAGE)
      @Restrict("#{identity.loggedIn}")
      public class ProfileController {
              @In
              private IProfileService profileService;
              @In
              private IWaySOService waySOService;
      
              @RequestParameter
              private Integer profileId;
      
              private Profile profile;
      
              List<Processor> processorList;
      
              @Create
              public void init() {
                      profile = profileService.getProfile(profileId);
                      processorList = waySOService.getProcessorList();
      
              }
      
              public List<Processor> getProcessorList() {
                      return processorList;
              }
      
              public Profile getProfile() {
                      return profile;
              }
      }





      Profile Edit Page:




      <h:form id="profile">
        <h:selectOneMenu id="processorSelect" value="#{profileController.profile.processor}">
          <s:selectItems value="#{profileController.processorList}"
                          var="processor" label="#{processor.name}" />
          <f:converter converterId="processorConverter" />
          <a:support event="onchange" reRender="processorName"
                                      ajaxSingle="true" />
        </h:selectOneMenu>
        <h:outputText id="processorName"
                              value="#{profileController.profile.processor.name}" />
      </h:form>





      Processor Converter:



      @Name("processorConverter")
      @BypassInterceptors
      @Converter
      public class ProcessorConverter implements javax.faces.convert.Converter {
      
              public Object getAsObject(FacesContext arg0, UIComponent arg1, String content) {
                      IWaySOService waySOService = (IWaySOService) Component.getInstance("waySOService");
                      return waySOService.getProcessor(Integer.parseInt(content));
              }
      
              public String getAsString(FacesContext arg0, UIComponent arg1, Object content) {
                      Processor processor = (Processor) content;
                      return String.valueOf(processor.getId());
              }
      }




      I just need to select a processor for a profile using drop down list.



      profile = profileService.getProfile(profileId); //This method will load profile from the database by the given profile Id
      processorList = waySOService.getProcessorList(); //This method will load all processors from the database.



      For example:


      I have a profile which has the processor B. And the processor list includs processor A, B, C.


      1.When you first enter into the profile edit page, the drop down shows processor A there, it should be processor B. Only I manually change the processor list with processor B in the first place, it will show processor B. So seems it will always show the first element in the processor list, not show the profile's processor.
      2.When I select processor A and C, it works as expected. But when I select the processor B, I got message value is not valid.


      By the way, I use seam management persistence context and has overriden the hashCode and equals method.


      Does anyone can give some advice on that?




        • 2. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
          Leon Li Newbie

          Thanks for the response.


          The generated HTML is as belows when I first get into the profile edit page:



          <html  xmlns="http://www.w3.org/1999/xhtml"><head><link rel='stylesheet' class='component' type='text/css' href='/WTGControlCenter/a4j_3_2_1-SNAPSHOTorg/richfaces/renderkit/html/css/basic_classes.xcss/DATB/eAF7sqpgb-jyGdIAFrMEaw__' /><link rel='stylesheet' class='component' type='text/css' href='/WTGControlCenter/a4j_3_2_1-SNAPSHOTorg/richfaces/renderkit/html/css/extended_classes.xcss/DATB/eAF7sqpgb-jyGdIAFrMEaw__' /><script type='text/javascript' src='/WTGControlCenter/a4j_3_2_1-SNAPSHOTorg.ajax4jsf.javascript.AjaxScript'></script></head><form id="profile" name="profile" method="post" action="/WTGControlCenter/profileEdit.seam" enctype="application/x-www-form-urlencoded">
          <input type="hidden" name="profile" value="profile" />
          <select id="profile:processorSelect" name="profile:processorSelect" size="1" onchange="A4J.AJAX.Submit('_viewRoot','profile',event,{'parameters':{'ajaxSingle':'profile:processorSelect','profile:j_id1':'profile:j_id1'} ,'actionUrl':'/WTGControlCenter/profileEdit.seam','control':this} )">     
                  <option value="204">Apriva - FD/Atlanta (Buypass)</option>
               <option value="202">Apriva - FD/Nashville</option>
               <option value="200">Apriva - FD/North (Cardnet)</option>
               <option value="203">Apriva - FD/Omaha</option>
               <option value="201">Apriva - FD/South</option>
          
               <option value="209">Apriva - Fifth Third</option>
               <option value="207">Apriva - Global East</option>
               <option value="208">Apriva - NOVA</option>
               <option value="206">Apriva - Paymentech (Tampa)</option>
               <option value="205">Apriva - TSYS (Vital)</option>
               <option value="52">ComStar</option>
          
               <option value="54">eProcessing</option>
               <option value="28">FDMS</option>
               <option value="61">Global Class A - MTT1556</option>
               <option value="303">Global Class A - MTT1581</option>
               <option value="305">Global Class A - MTT5000</option>
               <option value="51">Global East - MTT1556</option>
          
               <option value="302">Global East - MTT1581</option>
               <option value="304">Global East - MTT5000</option>
               <option value="40">Paymentech-Tampa</option>
               <option value="33">TNS Fifth3rd</option>
               <option value="43">TNS Heartland</option>
               <option value="102">TNS Omaha</option>
          
               <option value="23">TNS Synapse</option>
               <option value="1">WAY Demo</option>
               <option value="181">WAY Demo Dukpt Debit </option>
          </select><span id="profile:processorName">Apriva - FD/North (Cardnet)</span><input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id4" />
          </form></html>


          • 5. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
            Leon Li Newbie

            If I remove the equals/hashcode method in the processor entity, whatever processor I select , I get value is not value message.

            • 6. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
              Ingo Jobling Master

              Process is an Entity, right?


              The getObject method of the converter should load the entity, using the Entity Manager, for the given id.


              Let me know if you need an example.

              • 7. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                Leon Li Newbie

                Yes, the Processor is an Entity.


                I use seam management persistence context and hibernate session.


                The getProcessor method in waySOService is as belows:


                @In
                private ProcessorDao processorDao;


                public Processor getProcessor(Integer id) {
                    return processorDao.getProcessor(id);
                }



                The getProcessor method in ProcessorDao component is as belows:
                     


                public Processor getProcessor(Integer id) {
                    return (Processor) return getSession().get(Processor.class, id);
                }
                



                So I think I have already loaded the entity using the Entity Manager for the give id.


                If not, please give me an example, thanks in advance.

                • 8. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                  Orestes Dominguez Newbie

                  You have to load the entities for the selectOneMenu with the same persistence context that you are using in the converter.


                  You get this error because you are trying to retrieve an entity that wasn't loaded previously in the selectOneMenu, it was loaded in a different persistence context.

                  • 9. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                    Tim Evers Master

                    What I did to get around this problem is create my own cache (insert Map :P ) of the objects that are in the original list. So when I get the id in the getAsObject method I just grab the value out of my cache rather then looking up the database again for the object.


                    This will allow you to get around this issue.


                    You will actually see that some of the other converters in SEAM or Richfaces (or some other library I can't remember :P) follow this model.


                    They have a converter and a converter store.

                    • 10. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                      Ingo Jobling Master

                      As you describe it, everything is OK and the select should be working.  The message you are getting, 'value is not valid', is issued by JSF and simply indicates that the selected value is not found in the list of select items.  This is based on object identity.


                      All I can suggest is to double check your code.  If you are unable to pinpoint the problem, remove all intermediate code that could be the source of the problem, for example, the DAO, the implemtations of equals and hashcode, etc. 


                      Start with something simple that works.  Then add back the code, a piece at a time. 


                      Hope this helps!

                      • 11. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                        Leon Li Newbie

                        Hi,Tim:


                        I have made some change on code to construct a ProcessorConverter with the processorList in the ProfileController as belows:


                        Added code in ProfileController:



                        private ProcessorConverter processorConverter;
                        
                        public void init() {
                        ......
                        processorList = waySOService.getProcessorList();
                        processorConverter = new ProcessorConverter(processorList);
                        }
                        
                        public ProcessorConverter getProcessorConverter() {
                             return processorConverter;
                        }




                        Added code in ProcessorConverter:



                        private List<Processor> processorList;
                             
                        public ProcessorConverter() {
                                  
                        }
                             
                        public ProcessorConverter(List<Processor> processorList) {
                             this.processorList = processorList;
                        }
                             
                        public Object getAsObject(FacesContext arg0, UIComponent arg1, String content) {
                             for(Processor processor : processorList) {
                                  if(processor.getId().equals(Integer.parseInt(content))) {
                                       return processor;
                                  }
                             }
                        
                             return null;
                        }



                        So it should be a cache of the objects that are in the original list, right?


                        But it can't work either. I still have the same problem as I mentioned in the first comment.


                        • 12. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                          Leon Li Newbie

                          In addition, I haven't seen any replies concerning about the problem item 1 I mentioned in the first comment. Should I only manually add the correct processor to the first of the processor list when I first enter into the profile edit page?

                          • 13. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                            Tim Evers Master

                            Ok, I'll post my code that I use. This is my generic object converter and should work for basically all entities that have a unique way of identifying them.


                            NOTE THIS CODE IS PROBLY MORE COMPLICATED THEN IT NEEDS TO BE BUT I HAVN'T HAD TIME TO FIX IT :P


                            Here is the converter


                            package gekko.web.jsf.converter;
                            
                            import javax.faces.component.UIComponent;
                            import javax.faces.context.FacesContext;
                            import javax.faces.convert.ConverterException;
                            
                            import org.jboss.seam.ScopeType;
                            import org.jboss.seam.Seam;
                            import org.jboss.seam.annotations.Install;
                            import org.jboss.seam.annotations.Name;
                            import org.jboss.seam.annotations.Scope;
                            import org.jboss.seam.annotations.faces.Converter;
                            import org.jboss.seam.annotations.intercept.BypassInterceptors;
                            
                            @Name(ObjectConverter.SEAM_ID)
                            @Scope(ScopeType.PAGE)
                            @Install(precedence = Install.APPLICATION)
                            @Converter
                            @BypassInterceptors
                            public class ObjectConverter implements javax.faces.convert.Converter {
                                
                                public static final String SEAM_ID = "GenericObjectConverter";
                                 
                                 @Override
                                 public Object getAsObject(FacesContext context, UIComponent component,
                                           String value) throws ConverterException {
                            
                                      if (value == null || component == null) {
                                           return null;
                                      }
                                      
                                      // Get the relevant Component's objectStore
                                      ObjectConverterStore ocStore = (ObjectConverterStore)ComponentStore.instance().get(component, context);
                                      
                                      if (ocStore == null) {
                                           return null;
                                      }
                                      
                                      return ocStore.get(value);
                                 }
                            
                                 @Override
                                 public String getAsString(FacesContext context, UIComponent component,
                                           Object value) throws ConverterException {
                                      
                                      if (value == null || component == null) {
                                           return null;
                                      }
                            
                                      ObjectConverterStore ocStore = (ObjectConverterStore)ComponentStore.instance().get(component, context);
                                      
                                      if (ocStore == null) {
                                           ocStore = new ObjectConverterStore();
                                      }
                                      
                                      if(context.getApplication().getStateManager().isSavingStateInClient(context)
                                                && Seam.isEntityClass(value.getClass())) {
                                           throw new ConverterException("ObjectConverter is unable to handle entity classes when client-side " +
                                                     "state saving is enabled. Please use EntityConverter instead, or enable server-side state saving");
                                      }
                                      
                                      String key = ocStore.put(value);
                                      ComponentStore.instance().put(component, ocStore, context);
                            
                                      return key;
                                 }
                            
                            }



                            Here is the converterStore


                            package gekko.web.jsf.converter;
                            
                            import java.rmi.server.UID;
                            import java.util.HashMap;
                            import java.util.Map;
                            
                            import gekko.domain.PersistentEntity;
                            
                            public class ObjectConverterStore {
                            
                                 private final Map<String, Object> objects = new HashMap<String, Object>();
                            
                                 public String put(Object entity) {
                                      String key = null;
                                      if (entity instanceof PersistentEntity) {
                                           key = getEntityKey((PersistentEntity)entity);
                                      }
                                    if (key==null)   {
                                        key = new UID().toString();
                                      }
                                      objects.put(key, entity);
                                      return key;
                                 }
                            
                                 public Object get(String key) {
                                      return objects.get(key);
                                 }
                            
                                 private String getEntityKey(PersistentEntity entity) {
                                      if (entity.getId() == null){
                                        return  null;
                                    }
                                      return entity.getId().toString();
                                 }
                            
                            }



                            And here is my Component store... this really is just a mechanism to make it easier for me to dig up the right List of entities.... probly could be done an easier way but like I said... havn't had time to go back over this code.


                            package gekko.web.jsf.converter;
                            
                            import java.util.HashMap;
                            import java.util.Map;
                            
                            import javax.faces.component.UIComponent;
                            import javax.faces.context.FacesContext;
                            import javax.faces.convert.ConverterException;
                            
                            import org.jboss.seam.Component;
                            import org.jboss.seam.ScopeType;
                            import org.jboss.seam.annotations.Install;
                            import org.jboss.seam.annotations.Name;
                            import org.jboss.seam.annotations.Scope;
                            import org.jboss.seam.contexts.Contexts;
                            
                            import static org.jboss.seam.annotations.Install.APPLICATION;
                            
                            @Name(ComponentStore.SEAM_ID)
                            @Install(precedence = APPLICATION)
                            @Scope(ScopeType.PAGE)
                            public class ComponentStore {
                                
                                public static final String SEAM_ID = "ComponentStore";
                            
                                 private final Map<String, Object> components = new HashMap<String, Object>();
                            
                                 public void put(UIComponent component, ObjectConverterStore ocStore, FacesContext context) {
                                      components.put(getKey(component, context), ocStore);
                                 }
                            
                                 public Object get(UIComponent component, FacesContext context) {
                                      return components.get(getKey(component, context));
                                 }
                                 
                                 public String contains(Object object) {
                                      for (Map.Entry<String, Object> entry : components.entrySet()) {
                                           if (entry.getValue().equals(object)) {
                                                return entry.getKey();
                                           }
                                      }
                                      return null;
                                 }
                            
                                 public static ComponentStore instance() {
                                      if (!Contexts.isPageContextActive()) {
                                           throw new IllegalArgumentException("Page scope not active");
                                      }
                                      return (ComponentStore) Component
                                                .getInstance(ComponentStore.class);
                                 }
                                 
                                 private String getKey(UIComponent component, FacesContext context) {
                                      if (component.getId() == null) {
                                           throw new ConverterException("The component ID is null. Each component must have a unique ID");
                                      }
                                      
                                      return component.getClientId(context);
                                 }
                            }
                            



                            Now regarding your other question. If you implement it the way I have shown above then you do not need to add anything to the list of processors unless you have filtered the list. What should happen is that because of the converter it will work out which one in the list is the one that the object has selected because of it's unique identifier.


                            However, if you do filter the lists (which we do) then yes, you will need to add into the list the currently selected processor. (You should make a helper method that only adds it in if it doesn't already exist or something like that.)


                            But feel free to implement this a better way. :) The general idea of what you need to do is here though.


                            Hope this helps. I'll try keep looking at this thread to answer any questions you have about my code. (Gota find how to subscribe to a thread :P)

                            • 14. Re: selectItems can't work with converter in h:selectOneMenu, got message "value is not valid".
                              Leon Li Newbie

                              I have applied your code to my code,it still can't work.


                              The change I made is:


                              1.Modify the package.
                              2.Modify the PersistenceEntity to my Processor entity.
                              3.Modify the converter to use GenericObjectConverter.

                              1 2 Previous Next