1 2 Previous Next 17 Replies Latest reply on Jul 13, 2011 2:31 AM by Martin Frey

    s:convertEntity in h:selectOneMenu produces indexes for option tags only

    Karsten Wutzke Expert

      Hello,


      I have a rarer case of using a dropdown box for navigation. I want to switch seasons on certain pages, so this is what I use:


          <form method="get" enctype="application/x-www-form-urlencoded">
            <h:selectOneMenu value="#{seasonHome.id}" id="season" onchange="this.form.submit();">
              <s:selectItems value="#{seasonListQuery.resultList}"
                             var="season"
                             label="xxxSeason #{season.startYear}"
                             noSelectionLabel="Select Season"
                             hideNoSelectionLabel="true" />
              <s:convertEntity /> 
            </h:selectOneMenu>
            &#187;&#160;Competitions
          </form>
      



      Note I can't use h:form here, as I need a HTTP GET for this, which h:form doesn't support (see jsftoolbox.com).


      seasonListQuery is a simple EntityQuery sub class which returns all current season entities from the DB:


      SELECT se FROM Season se



      Note the Season entity has implemented equals and hashCode sufficiently.


      However, all that happens when the page is rendered is this:


      <form method="get" enctype="application/x-www-form-urlencoded">
        <select id="season" name="season" size="1" onchange="this.form.submit();">
          <option value="0">xxxSeason 2011</option>
          <option value="1">xxxSeason 2003</option>
          <option value="2">xxxSeason 2002</option>
          <option value="3">xxxSeason 2001</option>
        </select>
        &raquo; Competitions
      </form>
      



      Whatever I've tried the past 2 days I always end up with the indexes in the option values. The entity converter doesn't even seem to be triggered.


      Here's what I get when omitting the s:convertEntity:


      <form method="get" enctype="application/x-www-form-urlencoded">
        <select id="season" name="season" size="1" onchange="this.form.submit();">
          <option value="com.kawoolutions.bbstats.model.Season@37b7a72b[startYear=2011]">xxxSeason 2011</option>
          <option value="com.kawoolutions.bbstats.model.Season@50a5314[startYear=2003]">xxxSeason 2003</option>
          <option value="com.kawoolutions.bbstats.model.Season@68d448a1[startYear=2002]">xxxSeason 2002</option>
          <option value="com.kawoolutions.bbstats.model.Season@48ec77cb[startYear=2001]">xxxSeason 2001</option>
        </select>
        &raquo; Competitions
      </form>
      



      What's wrong?


      Here's 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:transaction="http://jboss.com/products/seam/transaction"
                  xmlns:ui="http://jboss.com/products/seam/ui"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd
                                      http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.2.xsd 
                                      http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.2.xsd 
                                      http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.2.xsd
                                      http://jboss.com/products/seam/ui http://jboss.com/products/seam/ui-2.2.xsd">
      
        <persistence:entity-manager-factory name="emf"
                                            persistence-unit-name="MyPersistenceUnit"
                                            auto-create="true" />
      
        <persistence:managed-persistence-context name="em"
                                                 entity-manager-factory="#{emf}"
                                                 auto-create="true" />
      
        <transaction:entity-transaction entity-manager="#{em}"
                                        auto-create="true" />
        
        <ui:jpa-entity-loader entity-manager="#{em}" />
      
      </components>
      



      I've followed the examples in Seam in Action on pages 358-359 without success (Seam 2.1 config). Note I'm using Seam 2.2.2 on Tomcat 6.


      I really need to get s:convertEntity to work because I have other season-switchable pages which use entities with composite keys, so working around/manually building the select doesn't really help (I already got that).


      So why isn't this working? Is it my config? Tomcat? How do I debug this (no exceptions are thrown AFAIK)?


      Any help would be highly appreciated


      Karsten

        • 1. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
          Roger Mori Newbie

          Hi Karsten:


          Change your selectOneMenu as follows:


          <h:selectOneMenu value="#{seasonHome.instance}" id="season" onchange="this.form.submit();">
          



          Roger.

          • 2. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
            Karsten Wutzke Expert

            For whatever reason this fixes the default selection problem. Nice. :-)


            However, it does not fix the problem that for each option the value (value="0", ...) is the index and not the entity's key, as annotated by Season:


            @Entity
            @Table(name = "Seasons")
            @Name(value = "season")
            public class Season implements Serializable
            {
                @Id
                @Column(name = "start_year")
                private Integer startYear;
            
                ...
            }



            It should result in something like:


            <form method="get" enctype="application/x-www-form-urlencoded">
              <select id="season" name="season" size="1" onchange="this.form.submit();">
                <option value="2011">xxxSeason 2011</option>
                <option value="2003">xxxSeason 2003</option>
                <option value="2002">xxxSeason 2002</option>
                <option value="2001">xxxSeason 2001</option>
              </select>
              &raquo; Competitions
            </form>



            Thanks


            Karsten

            • 3. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
              Roger Mori Newbie

              Hi Kristen:


              s:convertEntity is intended for persisted instances of entities like Season and not for attributes like your Integer id. You do not need a converter for an Integer, since it is immutable.


              Also, do not use the seasonHome.setId(Object o),  but the wrapper seasonHome.setStartYear(Integer i).


              Roger.





              • 4. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                Karsten Wutzke Expert

                I've just revisited the original article on this topic...


                The part the value referenced by the itemValue attribute is passed to <s:convertEntity /> has always disturbed me. Is it mandatory to specify the itemValue="" on s:selectItems when used in conjunction with <s:convertEntity />?


                I then reinserted the s:selectItems itemValue's, which I thought would be replaced by the presence of <s:convertEntity />. It's the first time I can actually see a stacktrace when reloading the page (season.startYear is the entity's Integer @Id):


                      <h:selectOneMenu value="#{seasonHome.instance}" id="season" onchange="this.form.submit();">
                        <s:selectItems value="#{seasonListQuery.resultList}"
                                       var="season"
                                       itemValue="#{season.startYear}"
                                       label="xxxSeason #{season.startYear}"
                                       noSelectionLabel="Select Season"
                                       hideNoSelectionLabel="true" />
                        <s:convertEntity /> 
                      </h:selectOneMenu>
                



                Stack trace:


                org.jboss.seam.Entity$NotEntityException: Not an entity class: java.lang.Integer
                     at org.jboss.seam.Entity.forClass(Entity.java:159)
                     at org.jboss.seam.Entity.forBean(Entity.java:145)
                     at org.jboss.seam.persistence.PersistenceProvider.getId(PersistenceProvider.java:140)
                     at org.jboss.seam.persistence.HibernatePersistenceProvider.getId(HibernatePersistenceProvider.java:228)
                     at org.jboss.seam.framework.EntityIdentifier.<init>(EntityIdentifier.java:13)
                     at org.jboss.seam.ui.JpaEntityLoader.createIdentifier(JpaEntityLoader.java:29)
                     at org.jboss.seam.ui.AbstractEntityLoader.put(AbstractEntityLoader.java:46)
                     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
                     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
                     at java.lang.reflect.Method.invoke(Unknown Source)
                     at org.jboss.seam.util.Reflections.invoke(Reflections.java:22)
                     at org.jboss.seam.intercept.RootInvocationContext.proceed(RootInvocationContext.java:32)
                     at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:56)
                     at org.jboss.seam.transaction.RollbackInterceptor.aroundInvoke(RollbackInterceptor.java:28)
                     at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                     at org.jboss.seam.transaction.TransactionInterceptor$1.work(TransactionInterceptor.java:97)
                     at org.jboss.seam.util.Work.workInTransaction(Work.java:61)
                     at org.jboss.seam.transaction.TransactionInterceptor.aroundInvoke(TransactionInterceptor.java:91)
                     at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                     at org.jboss.seam.core.MethodContextInterceptor.aroundInvoke(MethodContextInterceptor.java:44)
                     at org.jboss.seam.intercept.SeamInvocationContext.proceed(SeamInvocationContext.java:68)
                     at org.jboss.seam.intercept.RootInterceptor.invoke(RootInterceptor.java:107)
                     at org.jboss.seam.intercept.JavaBeanInterceptor.interceptInvocation(JavaBeanInterceptor.java:185)
                     at org.jboss.seam.intercept.JavaBeanInterceptor.invoke(JavaBeanInterceptor.java:103)
                     at org.jboss.seam.ui.JpaEntityLoader_$$_javassist_seam_4.put(JpaEntityLoader_$$_javassist_seam_4.java)
                     at org.jboss.seam.ui.EntityConverter.getAsString(EntityConverter.java:65)
                     at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getFormattedValue(HtmlBasicRenderer.java:448)
                     at com.sun.faces.renderkit.html_basic.MenuRenderer.renderOption(MenuRenderer.java:480)
                     at com.sun.faces.renderkit.html_basic.MenuRenderer.renderOptions(MenuRenderer.java:772)
                     at com.sun.faces.renderkit.html_basic.MenuRenderer.renderSelect(MenuRenderer.java:832)
                     at com.sun.faces.renderkit.html_basic.MenuRenderer.encodeEnd(MenuRenderer.java:280)
                     at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:861)
                     at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:242)
                     at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:106)
                     at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:837)
                     at javax.faces.component.UIComponent.encodeAll(UIComponent.java:930)
                     at javax.faces.component.UIComponent.encodeAll(UIComponent.java:933)
                     at org.richfaces.renderkit.html.LayoutRenderer.renderLayout(LayoutRenderer.java:57)
                     at org.richfaces.renderkit.html.LayoutRenderer.doEncodeChildren(LayoutRenderer.java:45)
                     at org.ajax4jsf.renderkit.RendererBase.encodeChildren(RendererBase.java:120)
                     at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:837)
                     at org.ajax4jsf.renderkit.RendererBase.renderChild(RendererBase.java:277)
                     at org.ajax4jsf.renderkit.RendererBase.renderChildren(RendererBase.java:258)
                     at org.richfaces.renderkit.html.PageRenderer.doEncodeChildren(PageRenderer.java:265)
                     at org.richfaces.renderkit.html.PageRenderer.doEncodeChildren(PageRenderer.java:254)
                     at org.ajax4jsf.renderkit.RendererBase.encodeChildren(RendererBase.java:120)
                     at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:837)
                     at javax.faces.component.UIComponent.encodeAll(UIComponent.java:930)
                     at javax.faces.component.UIComponent.encodeAll(UIComponent.java:933)
                     at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:592)
                     at org.ajax4jsf.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:100)
                     at org.ajax4jsf.application.AjaxViewHandler.renderView(AjaxViewHandler.java:176)
                     at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:110)
                     at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
                     at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
                     at javax.faces.webapp.FacesServlet.service(FacesServlet.java:266)
                     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                     at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:206)
                     at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
                     at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:388)
                     at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:515)
                     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
                     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                     at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                     at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
                     at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
                     at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
                     at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
                     at java.lang.Thread.run(Unknown Source)
                



                I'm not sure if I have gotten any further now. I seem to be on the right way, as it would otherwise not be possible to select the default via #{seasonHome.instance}.


                What's missing now?


                Thanks


                Karsten

                • 5. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                  Karsten Wutzke Expert

                  I obviously have problem with your latest answer.


                  The problem I seem to have is that selecting another season from the dropdown box causes a GET request to be issued, so I must change the value http://...?season=2001 in the URL...


                  Is this possible at all?


                  Karsten

                  • 6. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                    Karsten Wutzke Expert

                    Sorry for asking so many questions. I'm really confused after two days of trying to understand this.


                    Karsten

                    • 7. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                      Karsten Wutzke Expert

                      Discussion http://seamframework.org/Community/EntityConverterPageScope seems to be about my problems. I'm having a hard time understanding the discussion and why s:convertEntity doesn't work. Seam is hard.


                      In the end, it looks like you have to create a custom converter for each entity type when using GET and dump the s:convertEntity idea entirely.


                      Comments welcome.


                      Karsten

                      • 8. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                        Martin Frey Newbie

                        Hi
                        I've created once a new version of an entityconverter that does not create index options. It produces classname and id strings. This was not about handling but performance.


                        But probably this will help you with your selection values too.


                        Here's the reference:


                        http://seamframework.org/Community/NewEntityConverterPOChttp://seamframework.org/Community/NewEntityConverterPOC

                        • 9. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                          Martin Frey Newbie

                          The problem with the current implementation which uses this entityidentifierstore is that this is extremely noisy. For each dropdown one store is created! Even if you render 3 times the same. I found that this is amazingly costy. Additionally we already have (in most cases) already the perfect identifierstore. This is called EntityManager ;) I dont understand the reason why it was not built like this from the beginning, since there was no feedback at this time. 

                          • 10. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                            Ingo Jobling Master

                            Agreed!  s:convertEntity is pretty much useless. 


                            Simply write your own Entity Converter, it is very easy, just two methods:  One method accepts a string) id and returns an object, and the other method does the opposite.


                            Again, forget about s:convertEntity; you will be much happier!

                            • 11. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                              Karsten Wutzke Expert

                              The way Roger stated seems to be the right way to implement the converter, using either <s:convertEntity /> and <f:converter converterId="org.jboss.seam.ui.EntityConverter" />. They seem to be exactly equivalent.


                              The problem I'm facing is not the conversion per se but how to handle the GET URL that changes from multiple URL params


                              http://localhost:8081/bbstats/group-standings.seam?round=54&group=1


                              to


                              http://localhost:8081/bbstats/group-standings.seam?round=1


                              upon selection, which is incompatible with my pages.xml of course (it results in an NPE then).


                              I was able to get an email answer from Pete Muir on this (albeit very short and I'm not sure if he really reflected on my problem):



                              You need to use the generated id in your selection. Just generate the dropdown automatically using JSF. It all works. See the ui example.

                              So according to him Seam needs to generate a list index for composite entities to work with an HTML select. I wasn't able to elaborate further on Pete's statement nor did the UI example get me any further.


                              The questions now are:


                              How do you translate this index (which is supposed to work) to the real URL of the selected entity (154, 1), that is the URL that includes both round and group params?:


                              http://localhost:8081/bbstats/group-standings.seam?round=154&group=1


                              How do you restore the selected entity after submit on the same view given an index? How do I keep the list, ideally without having to re-run the query?


                              One idea that came to mind was to go to another page upon selection which would then find the entity and redirect back to the original page, much like a redirect after POST, but I don't know if that's how it's supposed to work with Seam.


                              Can PAGE scope be of use here? Note, my project doesn't make use of conversations, only pure HTTP GET.


                              I'm completely stuck on this.


                              Karsten

                              • 12. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                                Martin Frey Newbie

                                You can always create the select items yourself with the standard jsf selectitem. there you can give a value key (you entity id) which will be rendered as the option value.


                                For the finding / loading of the entity. Suppose you are in a conversation / page scope you can always use the entitymanager to find an entity. The entitymanager will NOT call the database if it knows the entity already. (first level cache). Additionally, even if the entitymanager does not know it anymore, f.e. a call from an external page, the entity will be found if the id is known and also you have directly a managed entity.


                                This and the performance reason where the reasons to leverage on the entitymanager directly with my other approach.


                                If you know exactly which object your id is referencing you can only pass the id ofc. But i recommend using the entitymanager for entities and for normal beans you need another kind of storage.

                                • 13. Re: s:convertEntity in h:selectOneMenu produces indexes for option tags only
                                  Karsten Wutzke Expert

                                  You can always create the select items yourself with the standard jsf selectitem. there you can give a value key (you entity id) which will be rendered as the option value.

                                  Of course, but this isn't the problem. As I said before, using an entity's single-column ID isn't a problem at all. I got that working perfectly.


                                  It's just that a SELECT can't represent a composite key (multiple URL params):



                                  <form method="get" enctype="application/x-www-form-urlencoded">
                                    <select id="???" name="round" size="1" onchange="this.form.submit();">
                                      <option value="???">Group for Season 2003</option>
                                      <option value="???">Group for Season 2002</option>
                                      <option value="???" selected="selected">Group for Season 2001</option>
                                    </select>
                                    ...
                                  </form>





                                  What should be placed into the ??? above for composite keys? It's not working when you think about it. The select id is round, so adding another named group isn't in the mix. At least I haven't found out. Fakes I tried included to set the option tag values to something like value="154&group=1", but that of course produced URLs where the '&' and '=' chars are escaped with %26 and %3D, which is just data and not control chars for the browser:


                                  http://localhost:8081/bbstats/group-standings.seam?round=154%26group%3D1



                                  This didn't work either.


                                  The Seam entity converter must somehow use another value (here the list index) for composite entities, but this will be incompatible with the GET URL to the same page - as described.


                                  So, how do I get around this problem, which is essentially caused by an HTML deficiency?


                                  The only ways I see are:





                                  1. Keep the list of entities (or re-create it), then pass the index to another page, find the selected entity there and redirect its PK values back to the original page.

                                  2. Define some kind of composite replacement for the HTML select deficiency, e.g. use a string "group:154,1" for the OPTION values, parse that in another page and redirect the new PK values back to the original page.





                                  Does it really have to be that complicated? What am I missing?


                                  Karsten

                                  1 2 Previous Next