1 2 3 Previous Next 34 Replies Latest reply on Jun 30, 2006 12:00 PM by Jason Long

    whew! finally, a pattern for SelectItems using generics

    Patrick Angeles Novice

      To use jsf <f:selectItems> that use values backed by a persistent entity, do the following... I'm using 'State' as my persistent entity.

      First, I make State implement Lookup:

      public interface Lookup
      {
      public String getKey () ;
      public String getName () ;
      }
      


      Where getKey is simply an alias for getId. (For entities with Long id's, simply return id.toString()).

      Next I create a SEAM managed POJO called SelectItems that lives in the application context:
      @Name ("selectItems")
      @Scope (APPLICATION)
      @Intercept (ALWAYS)
      public class SelectItems
      {
      private static final Logger log = Logger.getLogger (SelectItems.class);
      
      @In (create = true)
      private StateDao stateDao ;
      
      private Map<Class<? extends Lookup>, Map<String, ? extends Lookup>>
       entityMapMap = new HashMap<Class<? extends Lookup>, Map<String, ? extends Lookup>> () ;
      
      private Map<Class<? extends Lookup>, List<SelectItem>>
       selectItemListMap = new HashMap<Class<? extends Lookup>, List<SelectItem>> () ;
      
      public SelectItems ()
      {
      }
      
      @Create
      public void onCreate ()
      {
       log.info ("Initializing SelectItems") ;
       List<State> stateList = stateDao.list () ;
       addLookup (State.class, stateList) ;
       return ;
      }
      
      private <T extends Lookup> void addLookup (Class<T> entityClass, List<T> entityList)
      {
       // add entityMap
       Map<String, T> entityMap = new HashMap <String, T> () ;
       for (T entity : entityList) {
       Lookup l = (Lookup) entity ;
       entityMap.put (l.getKey(), entity) ;
       }
       entityMapMap.put (entityClass, entityMap) ;
      
       // add selectItemList
       List<SelectItem> selectItemList = new ArrayList<SelectItem> () ;
       for (T entity : entityList) {
       Lookup l = (Lookup) entity ;
       SelectItem item = new SelectItem (entity, l.getKey(), l.getName()) ;
       selectItemList.add (item) ;
       }
       selectItemListMap.put (entityClass, selectItemList) ;
      }
      
      public <T extends Lookup> Map<String, T> getEntityMap (Class<T> entityClass)
      {
       return (Map<String, T>) entityMapMap.get (entityClass) ;
      }
      
      public <T extends Lookup> List<SelectItem> getSelectItemList (Class<T> entityClass)
      {
       return selectItemListMap.get (entityClass) ;
      }
      
      public List<SelectItem> getStates ()
      {
       return getSelectItemList (State.class) ;
      }
      
      @Destroy
      public void destroy()
      {
       // not sure if this is required for application context
       log.info ("destroyed");
      }
      
      }
      


      Note the onCreate method, which registers all the lookup entities. Add your lookup entities here as you create them. SEAM will inject the DAO objects. StateDao is a SLSB that injects its own PersistenceContext.

      SelectItems does two things: it creates a list of SelectItem objects for the drop down. It also creates an in-memory map of the lookup entities for use by a Converter class. It puts both in their respective HashMaps.

      Next we have a generic converter class:
      public abstract class LookupConverter<T extends Lookup>
       implements javax.faces.convert.Converter
      {
      
      private Class<T> entityClass ;
      
      public LookupConverter (Class<T> entityClass)
      {
       this.entityClass = entityClass ;
      }
      
      public String getAsString (FacesContext context, UIComponent component, Object object)
      {
       if (object == null)
       return null ;
      
       T lookup = (T) object ;
       return lookup.getKey () ;
      }
      
      public Object getAsObject (FacesContext context, UIComponent component, String value)
      {
       if (value == null)
       return null ;
      
       // get selectItems from application context
       Context ctx = Contexts.getApplicationContext () ;
       SelectItems selectItems = (SelectItems) ctx.get ("selectItems") ;
       if (selectItems == null) {
       // create selectItems in application context if not found
       selectItems = (SelectItems) Component.newInstance ("selectItems") ;
       ctx.set ("selectItems", selectItems) ;
       }
      
       // get the entityMap for this entityClass
       Map<String, T> entityMap = selectItems.getEntityMap (entityClass) ;
      
       if (entityMap == null) {
       // throw a non-recoverable exception
       throw new ConverterException ("Unable to get entity map for "
       + entityClass.getCanonicalName()) ;
       }
      
       T object = entityMap.get (value) ;
       if (object == null) {
       // throw a recoverable exception
       throw new ConverterException
       (new FacesMessage (component.getId(), value + " is an invalid value")) ;
       }
      
       return object ;
      }
      
      }
      


      Each entity class will have to extend this generic class as follows:

      public class StateConverter
       extends LookupConverter<State>
      {
      
      public StateConverter ()
      {
       super (State.class) ;
      }
      
      }
      


      Then you just register the converter in faces-config.xml, and use it in your view markup as follows:
       <div class="input">
       <h:selectOneMenu id="state" value="#{customer.address.state}" converter="StateConverter">
       <f:selectItems value="#{selectItems.states}" />
       </h:selectOneMenu>
       <br/><span class="errors"><h:message for="state" /></span>
       </div>
      


        • 1. Re: whew! finally, a pattern for SelectItems using generics
          ryan dewell Novice

          Very slick Patrick. Thanks for sharing this. I'll have to try it out.

          Ryan

          • 2. Re: whew! finally, a pattern for SelectItems using generics
            Steve Fisher Newbie

            Patrick,

            I'm still on an enormous learning curve here and would appreciate it if you could post a test set-up for this.

            Thanks for sharing.

            Steve

            • 3. Re: whew! finally, a pattern for SelectItems using generics
              Patrick Angeles Novice

              There's only a couple of steps missing from the original post...

              First you need to register the converter by adding this to your faces-config.xml:

               <converter>
               <converter-id>
               StateConverter
               </converter-id>
               <converter-class>
               com.inertiabev.jsf.convert.StateConverter
               </converter-class>
               </converter>
              


              The entity definition is in State.java. I had to edit the code for posting because all my entities have a somewhat deep class hierarchy. This is the simple version (but may not compile). The important thing to see is that it implements the Lookup interface:
              import java.util.*;
              import javax.persistence.* ;
              import org.hibernate.validator.* ;
              import org.hibernate.annotations.* ;
              
              @javax.persistence.Entity (access = AccessType.FIELD)
              @Cache (usage = CacheConcurrencyStrategy.READ_ONLY)
              public class State
               implements java.io.Serializable, Lookup
              {
              @Id (generate = GeneratorType.NONE)
              @Length (min = 2, max = 2)
              protected String id ;
              
              @NotNull
              @Length (min = 1, max = 50)
              protected String name ;
              
              public State ()
              {
              }
              
              public State (String id, String name)
              {
               this.id = id ;
               this.name = name ;
              }
              
              public String getId () { return this.id ; }
              public String getName () { return this.name ; }
              
              public void setId (String p) { this.id = p ; }
              public void setName (String p) { this.name = p ; }
              
              public String getKey () { return this.id ; }
              
              public String toString()
              {
               return this.id ;
              }
              
              
              }
              


              Next, I have StateDaoBean.java, coded as a SLSB, and it implements the StateDao interface. Again, the posted code is vastly simplified. In practice, I use the GenericDAO pattern for EJBs. Look at the CaveatEmptor code in CVS for guidelines. You can also search this forum, and read this blog:

              http://blog.hibernate.org/cgi-bin/blosxom.cgi/Christian%20Bauer/java/genericdao.html

              The code:
              @javax.ejb.Stateless
              @javax.ejb.Interceptor (org.jboss.seam.ejb.SeamInterceptor.class)
              @org.jboss.seam.annotations.Name ("stateDao")
              public class StateDaoBean
               implements StateDao
              {
              ...
              @PersistenceManager EntityManager entityManager ;
              
               public List<State> list ()
               {
               return listByCriteria();
               }
              
               @SuppressWarnings("unchecked")
               protected List<State> listByCriteria (Criterion... criterion)
               {
               Criteria crit = getSession().createCriteria (State.class) ;
               for (Criterion c : criterion) {
               crit.add(c);
               }
               return (List<State>) crit.list () ;
               }
              
              }
              


              Finally, the interface definition:
              public interface StateDao
              {
               ...
               public List<State> list () ;
               ...
              }
              


              Hope this helps...

              • 4. Re: whew! finally, a pattern for SelectItems using generics
                Steve Fisher Newbie

                Patrick,

                After so many years away from coding I really appreciate the extra help. I should now be able to do something with this.

                Regards

                Steve

                • 5. Re: whew! finally, a pattern for SelectItems using generics
                  Andrew Newbie

                  patrick_ibg, please post

                  SelectItem class
                  and ConverterException class

                  Thanks

                  --Andrew

                  • 6. Re: whew! finally, a pattern for SelectItems using generics
                    Patrick Angeles Novice

                    those are standard JSF classes :)

                    • 7. Re: whew! finally, a pattern for SelectItems using generics
                      Andrew Newbie

                      I tried to embed this in to my project but it seems "selectItems" is missing from application context after first creation. May be "selectItems" must be a Stateful session bean?

                      • 8. Re: whew! finally, a pattern for SelectItems using generics
                        Andrew Newbie

                        sorry I have mistaken. "srlrctItems" is in application context :-)

                        I'm changs "State" to "Price" and add @Transient public String getKey() method

                        All maps created correctly
                        But when page rendering i have an error

                        16:54:57,812 INFO [STDOUT] 25.10.2005 16:54:57 com.sun.facelets.FaceletViewHandler handleRenderException
                        SEVERE: Error Rendering View
                        java.lang.NullPointerException
                         at com.model.base.Price.getKey(Price.java:132)
                         at ejb.system.selectItems.LookupConverter.getAsString(LookupConverter.java:33)
                         at org.apache.myfaces.renderkit.RendererUtils.getConvertedStringValue(RendererUtils.java:551)
                         at org.apache.myfaces.renderkit.html.HtmlRendererUtils.getSubmittedOrSelectedValuesAsSet(HtmlRendererUtils.java:327)
                         at org.apache.myfaces.renderkit.html.HtmlRendererUtils.internalRenderSelect(HtmlRendererUtils.java:295)
                         at org.apache.myfaces.renderkit.html.HtmlRendererUtils.renderMenu(HtmlRendererUtils.java:251)
                         at org.apache.myfaces.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:54)
                         at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:331)
                         at com.sun.facelets.FaceletViewHandler.encodeRecursive(FaceletViewHandler.java:521)
                         at com.sun.facelets.FaceletViewHandler.encodeRecursive(FaceletViewHandler.java:518)
                         at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:447)
                         at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:300)
                         at javax.faces.webapp.FacesServlet.service(FacesServlet.java:95)
                         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                         at org.apache.myfaces.component.html.util.ExtensionsFilter.doFilter(ExtensionsFilter.java:122)
                         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                         at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:81)
                         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                         at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
                         at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
                         at org.jboss.web.tomcat.security.CustomPrincipalValve.invoke(CustomPrincipalValve.java:39)
                         at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:159)
                         at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:59)
                         at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
                         at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
                         at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
                         at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
                         at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856)
                         at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:744)
                         at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
                         at org.apache.tomcat.util.net.MasterSlaveWorkerThread.run(MasterSlaveWorkerThread.java:112)
                         at java.lang.Thread.run(Unknown Source)
                        16:54:57,812 INFO [STDOUT] 25.10.2005 16:54:57 com.sun.facelets.FaceletViewHandler handleRenderException
                        SEVERE: Took Type: java.io.PrintWriter


                        and blank dropdown list
                        Error in Converter class it gets null Object some way.

                        Please help me

                        -Thanks


                        • 9. Re: whew! finally, a pattern for SelectItems using generics
                          Andrew Newbie

                           

                          "Zealot" wrote:
                          sorry I have mistaken. "selectItems" is in application context :-)

                          I've changed "State" to "Price" and add @Transient public String getKey() method

                          All maps created correctly
                          But when page rendering i have an error

                          16:54:57,812 INFO [STDOUT] 25.10.2005 16:54:57 com.sun.facelets.FaceletViewHandler handleRenderException
                          SEVERE: Error Rendering View
                          java.lang.NullPointerException
                           at com.model.base.Price.getKey(Price.java:132)
                           at ejb.system.selectItems.LookupConverter.getAsString(LookupConverter.java:33)
                           at org.apache.myfaces.renderkit.RendererUtils.getConvertedStringValue(RendererUtils.java:551)
                           at org.apache.myfaces.renderkit.html.HtmlRendererUtils.getSubmittedOrSelectedValuesAsSet(HtmlRendererUtils.java:327)
                           at org.apache.myfaces.renderkit.html.HtmlRendererUtils.internalRenderSelect(HtmlRendererUtils.java:295)
                           at org.apache.myfaces.renderkit.html.HtmlRendererUtils.renderMenu(HtmlRendererUtils.java:251)
                           at org.apache.myfaces.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:54)
                           at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:331)
                           at com.sun.facelets.FaceletViewHandler.encodeRecursive(FaceletViewHandler.java:521)
                           at com.sun.facelets.FaceletViewHandler.encodeRecursive(FaceletViewHandler.java:518)
                           at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:447)
                           at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:300)
                           at javax.faces.webapp.FacesServlet.service(FacesServlet.java:95)
                           at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                           at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                           at org.apache.myfaces.component.html.util.ExtensionsFilter.doFilter(ExtensionsFilter.java:122)
                           at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                           at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                           at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:81)
                           at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                           at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                           at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
                           at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
                           at org.jboss.web.tomcat.security.CustomPrincipalValve.invoke(CustomPrincipalValve.java:39)
                           at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:159)
                           at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:59)
                           at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
                           at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
                           at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
                           at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
                           at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856)
                           at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:744)
                           at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
                           at org.apache.tomcat.util.net.MasterSlaveWorkerThread.run(MasterSlaveWorkerThread.java:112)
                           at java.lang.Thread.run(Unknown Source)
                          16:54:57,812 INFO [STDOUT] 25.10.2005 16:54:57 com.sun.facelets.FaceletViewHandler handleRenderException
                          SEVERE: Took Type: java.io.PrintWriter


                          and blank dropdown list
                          Error in Converter class it gets null Object some way.

                          Please help me

                          -Thanks


                          • 10. Re: whew! finally, a pattern for SelectItems using generics
                            Patrick Angeles Novice

                            Can you post the code for Price.java?

                            • 11. Re: whew! finally, a pattern for SelectItems using generics
                              Andrew Newbie

                              Sorry for so late reply. It was night in Russia :-)

                              Here is my Price.java

                              @Entity
                              @Inheritance(strategy=InheritanceType.JOINED)
                              @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL)
                              @Name ("price")
                              public class Price extends ClassifierItem implements Lookup
                              {
                               private static final long serialVersionUID = 3544395811791319601L;
                               private String priceName;
                               private Set<PriceNomenclature> priceNomenclature = new HashSet<PriceNomenclature>();
                               private Price relativePrice;
                               /**
                               * Is parice absolut. If no - price relative
                               */
                               private Boolean absolutPriceFlag = true;
                               /**
                               * No-arg constructor for JavaBean tools.
                               */
                               public Price() { super(); }
                               /**
                               * create absolut price
                               * @param entryCreator
                               * @param name
                               * @param itemType
                               * @param classifier
                               * @param priceName
                               */
                               public Price(Employee entryCreator, String name, Classifier classifier, String priceName) {
                               super(entryCreator, name, Price.getDocumentType(), classifier);
                               this.priceName = priceName;
                               this.absolutPriceFlag = true;
                               }
                               /**
                               * create relative price
                               * @param entryCreator
                               * @param name
                               * @param itemType
                               * @param classifier
                               * @param priceName
                               * @param relativePrice ????, ???????????? ??????? ????? ????????? ??? ????
                               */
                               public Price(Employee entryCreator, String name, Classifier classifier, String priceName, Price relativePrice) {
                               super(entryCreator, name, Price.getDocumentType(), classifier);
                               this.priceName = priceName;
                               this.absolutPriceFlag = false;
                               this.relativePrice = relativePrice;
                               }
                               /**
                               * @return Returns the priceName.
                               */
                               public String getPriceName() { return priceName; }
                               /**
                               * @param priceName The priceName to set.
                               */
                               public void setPriceName(String priceName) { this.priceName = priceName; }
                               /**
                               * @return Returns the priceNomenclature.
                               */
                               @OneToMany(mappedBy="price", cascade=CascadeType.ALL)
                               public Set<PriceNomenclature> getPriceNomenclature() { return priceNomenclature; }
                               /**
                               * @param priceNomenclature The priceNomenclature to set.
                               */
                               public void setPriceNomenclature(Set<PriceNomenclature> priceNomenclature) { this.priceNomenclature = priceNomenclature; }
                               /**
                               * @return Returns the serialVersionUID.
                               */
                               @Transient public static long getSerialVersionUID() { return serialVersionUID; }
                               /**
                               * @return Returns the documentType.
                               */
                               @Transient public static String getDocumentType() { return documentType; }
                               /**
                               * @return Returns the absolutPriceFlag.
                               */
                               public Boolean getAbsolutPriceFlag() {
                               return absolutPriceFlag;
                               }
                               /**
                               * @param absolutPriceFlag The absolutPriceFlag to set.
                               */
                               public void setAbsolutPriceFlag(Boolean absolutPriceFlag) { this.absolutPriceFlag = absolutPriceFlag; }
                               /**
                               * @return Returns the relativePrice.
                               */
                               @ManyToOne (fetch = FetchType.EAGER)
                               public Price getRelativePrice() { return relativePrice; }
                               /**
                               * @param relativePrice The relativePrice to set.
                               */
                               public void setRelativePrice(Price relativePrice) { this.relativePrice = relativePrice; }
                              
                               @Transient public String getKey () {
                               System.err.println("KEY -- " + this.getId().toString());
                               return this.getId().toString();
                               }
                              
                               @Override
                               public String toString() {
                               return this.getPriceName();
                               }
                              
                              }


                              ClassifierItem class implements Serializable

                              • 12. Re: whew! finally, a pattern for SelectItems using generics
                                Andrew Newbie

                                I found strange thing. Method getAsString from LookupConverter receive null parameter "object". I can't understand why it happen. :-(

                                • 13. Re: whew! finally, a pattern for SelectItems using generics
                                  Andrew Newbie

                                  Where must I package all this classes. In war or ejb3/par? May be problem in this?

                                  • 14. Re: whew! finally, a pattern for SelectItems using generics
                                    Patrick Angeles Novice

                                    Right now, I have my entities and seam components in ejb3.


                                    1 2 3 Previous Next