9 Replies Latest reply on Aug 3, 2006 12:42 PM by dbatcn

    where is <h:inputText> going?

      I realize that this is probably a n00b mistake and if it were a snake it prob'ly woulda bit me, but I hope an answer to this will help others too. I've spent a couple of days on this but cannot see what's wrong; I tried to search for similar issues but didn't find anything and I don't see what's wrong with my example compared to the examples. I've got an abstracted example here, I hope it's not too long to post, but it should be complete.

      I have a stateful session bean with scope defaulting to conversation that is backing a page with a couple of <h:inputText> fields that are bound to a couple of non-required bijected values, one being a String, the other a property of an entity bean. I'm using Seam-managed transactions and persistence context.

      My problems are:

      1. For an <h:inputText> whose value is the property of a conversation-scoped context variable managed by my SFSB, why is the property's setter never called with the value typed in? (in bqmgr.xhtml below, typing a new value for the field next to the button labeled "rename quux" and then pressing the button)
      2. For an <h:inputText> whose value is a String conversation-scoped context variable managed by my SFSB, why is the variable null (and apparently not injected from the value typed in the form). (in bqmgr.xhtml below, typing in a value for the field next to the button "New Baz Specifying Name" and then pressing the button)

      I'm using Seam with facelets and EJB3.

      As always, apologies if I've somehow missed a previous post or piece of RTFM documentation.

      Thanks,
      David

      bqmgr.xhtml

      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html
       xmlns="http://www.w3.org/1999/xhtml"
       xmlns:f="http://java.sun.com/jsf/core"
       xmlns:h="http://java.sun.com/jsf/html"
       xmlns:s="http://jboss.com/products/seam/taglib"
       xmlns:ui="http://java.sun.com/jsf/facelets"
       >
       <head>
       <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
       <title>Baz/Quux Manager</title>
       </head>
       <body>
       <div class="errors"><h:messages globalOnly="true"/></div>
       <h:form>
       <s:link linkStyle="button" value="rename quux" action="#{bqManager.commitQuux}" />
       <h:inputText value="#{quux.name}" />
       </h:form>
       <h:form>
       <h:outputText value="you have no bazs"
       rendered="#{bazList.rowCount == 0}" />
       <h:dataTable var="baz" value="#{bazList}"
       rendered="#{bazList.rowCount > 0}">
       <h:column>
       <h:outputText value="#{1+bazList.rowIndex}. "/>
       <s:link value="delete" action="#{bqManager.deleteBaz}" linkStyle="button"/>
       <h:outputText value="#{baz.name}" />
       <br/>
       </h:column>
       </h:dataTable>
       <hr/>
       <s:link value="New Baz Specifying Name" action="#{bqManager.createBazSpecifyingName}" linkStyle="button" />
       <h:inputText value="#{namefornewbaz}"/>
       <hr/>
       <s:link value="New Baz" action="#{bqManager.createBaz}" linkStyle="button" />
       </h:form>
       </body>
      </html>
      



      BQManagerBean.java

      package com.orgmob.play;
      
      import java.io.Serializable;
      import java.util.HashSet;
      import java.util.List;
      
      import javax.ejb.Remove;
      import javax.ejb.Stateful;
      import javax.persistence.EntityManager;
      
      import org.jboss.seam.annotations.Begin;
      import org.jboss.seam.annotations.Destroy;
      import org.jboss.seam.annotations.End;
      import org.jboss.seam.annotations.Factory;
      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.datamodel.DataModel;
      import org.jboss.seam.annotations.datamodel.DataModelSelection;
      import org.jboss.seam.core.FacesMessages;
      import org.jboss.seam.log.Log;
      
      @Stateful
      @Name("bqManager")
      public class BQManagerBean implements BQManager, Serializable {
      
       @Logger
       private Log log;
      
       @In(required=false) @Out(required=false)
       private Quux quux;
      
       @In(required=false) @Out(required=false)
       private String nameForNewBaz;
      
       @DataModel(value="bazList")
       private List<Baz> bazList;
      
       @DataModelSelection(value="bazList")
       @Out(required=false)
       private Baz baz;
      
       @In(create=true)
       private EntityManager orgmobDatabase;
      
       @In(create=true)
       private transient FacesMessages facesMessages;
      
       @Begin(join=true)
       @Factory("quux")
       public void findQuuxen() {
       log.debug("looking for quux objects...");
       List<Quux> quuxList = (List<Quux>)orgmobDatabase.createQuery(
       "from Quux quux order by quux.id asc").getResultList();
       log.debug("found "+quuxList.size()+" quuxs in quuxList: " + quuxList );
       if ( 0 == quuxList.size() ) {
       quux = new Quux();
       quux.setName( "quux1" );
       orgmobDatabase.persist( quux );
       orgmobDatabase.flush();
       log.debug( "created: "+quux);
       }
       else {
       quux = quuxList.get(0);
       log.debug( "found: "+quux);
       }
       }
      
       @Begin(join=true)
       @Factory("bazList")
       public void findBazs() {
       log.debug("looking for baz objects...");
       bazList = (List<Baz>)orgmobDatabase.createQuery(
       "from Baz baz order by baz.id asc").getResultList();
       log.debug("found "+bazList.size()+" bazs in bazList: " + bazList );
       }
      
       @End
       public void stop() {
       }
      
       private String newBazname() {
       // find a name not currently seen by user;
       HashSet<String> baznameS = new HashSet<String>();
       for ( Baz baz : bazList ) {
       baznameS.add( baz.getName() );
       }
       String baznamePrefix = "baz";
       String bazname;
       int attempt = 1;
       do {
       bazname = baznamePrefix + (attempt++);
       } while ( baznameS.contains( bazname ) );
       return bazname;
       }
      
       private void makeABaz( String name ) {
      
       baz = new Baz();
       baz.setName( name );
       orgmobDatabase.persist( baz );
       orgmobDatabase.flush();
      
       log.debug( "created: "+baz);
      
       findBazs(); // update bazList
       }
      
       public void createBaz() {
       makeABaz( newBazname() );
       }
      
       public void createBazSpecifyingName() {
       if ( null == nameForNewBaz ) {
       facesMessages.add("BQManagerBean.createBazSpecifyingName(): nameForNewBaz is null!");
       }
       makeABaz( nameForNewBaz );
       }
      
       public void commitQuux() {
       if ( null == quux ) {
       facesMessages.add("BQManagerBean.commitQuux() called but quux is null!");
       log.error("BQManagerBean.commitQuux() called but quux is null!");
       }
       else {
       log.debug("BQManagerBean.commitQuux(): pre-merge quux: "+quux);
       orgmobDatabase.merge( quux );
       orgmobDatabase.flush();
       }
       }
      
       public void commitBaz() {
       if ( null == baz ) {
       facesMessages.add("BQManagerBean.commitBaz() called but baz is null!");
       log.error("BQManagerBean.commitBaz() called but baz is null!");
       }
       else {
       log.debug("BQManagerBean.commitBaz(): pre-merge baz: "+baz);
       orgmobDatabase.merge( baz );
       orgmobDatabase.flush();
       }
       }
      
       public void deleteBaz() {
       if ( null == baz ) {
       facesMessages.add("BQManagerBean.deleteBaz() called but baz is null!");
       log.error("BQManagerBean.deleteBaz() called but baz is null!");
       }
       else {
       log.debug("removing "+baz+"...");
       orgmobDatabase.remove( baz );
       orgmobDatabase.flush();
       log.debug( "baz removed.");
       findBazs(); // update bazList
       }
       }
      
       @Destroy @Remove
       public void destroy() {
       }
      
      }
      



      Baz.java

      package com.orgmob.play;
      
      import java.io.Serializable;
      
      import javax.persistence.Column;
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.Id;
      import javax.persistence.Table;
      
      import org.jboss.seam.annotations.Name;
      
      @Entity
      @Name("baz")
      @Table(name="BAZS")
      public class Baz implements Serializable {
      
       private long id;
       private String name;
      
       @Id
       @Column(name="BAZ_ID")
       @GeneratedValue
       public Long getId() {
       return id;
       }
       public void setId(Long id) {
       this.id = id;
       }
      
       @Column(name="NAME")
       public String getName() {
       System.out.println("Baz.getName():returning:"+name);
       return name;
       }
       public void setName(String name) {
       System.out.println("Baz.setName():setting name to:"+name);
       this.name = name;
       }
      
       @Override
       public String toString() {
       return "Baz[" + name + "]";
       }
      
      }
      



      Quux.java

      package com.orgmob.play;
      
      import java.io.Serializable;
      
      import javax.persistence.Column;
      import javax.persistence.Entity;
      import javax.persistence.GeneratedValue;
      import javax.persistence.Id;
      import javax.persistence.Table;
      
      import org.jboss.seam.annotations.Name;
      
      @Entity
      @Name("quux")
      @Table(name="QUUXS")
      public class Quux implements Serializable {
      
       private long id;
       private String name;
      
       @Id
       @Column(name="QUUX_ID")
       @GeneratedValue
       public Long getId() {
       return id;
       }
       public void setId(Long id) {
       this.id = id;
       }
      
       @Column(name="NAME")
       public String getName() {
       System.out.println("Quux.getName():returning:"+name);
       return name;
       }
       public void setName(String name) {
       System.out.println("Quux.setName():setting name to:"+name);
       this.name = name;
       }
      
       @Override
       public String toString() {
       return "Quux[" + name + "]";
       }
      
      }
      



      BQManager.java

      package com.orgmob.play;
      
      import javax.ejb.Local;
      
      @Local
      public interface BQManager {
       public void findQuuxen();
       public void findBazs();
       public void stop();
       public void createBaz();
       public void createBazSpecifyingName();
       public void commitQuux();
       public void commitBaz();
       public void deleteBaz();
       public void destroy();
      }
      



      persistence.xml

      <persistence>
       <persistence-unit name="orgmobDB">
       <provider>org.hibernate.ejb.HibernatePersistence</provider>
       <jta-data-source>java:/DefaultDS</jta-data-source>
       <properties>
       <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
       <property name="jboss.entity.manager.factory.jndi.name"
       value="java:/EntityManagerFactories/orgmobData" />
       </properties>
       </persistence-unit>
      </persistence>
      



      components.xml

      <components>
       <component name="org.jboss.seam.core.init">
       <property name="myFacesLifecycleBug">true</property>
       <property name="jndiPattern">member/#{ejbName}/local</property>
       </component>
       <component class="org.jboss.seam.core.Ejb"
       installed="false"/>
       <component name="orgmobDatabase"
       class="org.jboss.seam.core.ManagedPersistenceContext">
       <property name="persistenceUnitJndiName">java:/EntityManagerFactories/orgmobData</property>
       </component>
      </components>
      



        • 1. Re: where is <h:inputText> going?
          peteroyle

          Hi dbatcn,

          Some quick questions. If these don't help I'll try having a closer look:

          1: Will it help to add create=true to your @In annotation on the BQManagerBean.quux property?

          2: Have you tried changing #{namefornewbaz} to #{nameForNewBaz} in bqmgr.xhtml to better match the name of the property on the SFSB?


          Pete.

          • 2. Re: where is <h:inputText> going?
            bfo81

            As far as I know there's a mistake in the documentation. The default scope is not Conversation but Event -> http://www.jboss.com/index.html?module=bb&op=viewtopic&t=86491 - but I'm not sure if this only concerns normal JavaBeans (as shown in that topic's title) or also EJBs.

            • 3. Re: where is <h:inputText> going?
              gavin.king

              "#{foo}" is not a valid value-binding expression in JSF. You need to use "#{foo.someProperty}".

              • 4. Re: where is <h:inputText> going?
                gavin.king

                 

                As far as I know there's a mistake in the documentation. The default scope is not Conversation but Event -> http://www.jboss.com/index.html?module=bb&op=viewtopic&t=86491 - but I'm not sure if this only concerns normal JavaBeans (as shown in that topic's title) or also EJBs.


                Yes this was already fixed in CVS. It applies only to JavaBeans.

                • 5. Re: where is <h:inputText> going?

                   

                  "peteroyle" wrote:
                  1: Will it help to add create=true to your @In annotation on the BQManagerBean.quux property?


                  Adding "create=true" causes:


                  javax.ejb.EJBException: Application Error: no concurrent calls on stateful beans
                  at org.jboss.ejb3.stateful.StatefulInstanceInterceptor.invoke(StatefulInstanceInterceptor.java:71)
                  at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                  at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:78)
                  at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                  at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:47)
                  at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                  at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
                  at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
                  at org.jboss.ejb3.stateful.StatefulContainer.localInvoke(StatefulContainer.java:188)
                  at org.jboss.ejb3.stateful.StatefulLocalProxy.invoke(StatefulLocalProxy.java:98)
                  at $Proxy489.findQuuxen(Unknown Source)
                  ...


                  with a similar exception on the invocation (?) of the findBazs() method.

                  If I replace the "required=false" with the "create=true", then I get similar results.

                  With the original source, if I type a new quux name and press the rename quux button, then

                  1. Quux.setName() is called with the original value of quux.name (as seen in debug log), and
                  2. BQManagerBean.commitQuux() sees a null in its class's quux property (get the faces message: "BQManagerBean.commitQuux() called but quux is null!")

                  It seems to me that some instance of quux is being set, but not the one in BQManagerBean. Is there a way for an entity EJB to find out what context it's in?

                  "peteroyle" wrote:
                  2: Have you tried changing #{namefornewbaz} to #{nameForNewBaz} in bqmgr.xhtml to better match the name of the property on the SFSB?


                  That typo was an error on my part as I was trying to create a clean abstraction to post, sorry. Unfortunately, even when I change it to:
                  <h:inputText value="#{nameForNewBaz}"/>
                  I still get "BQManagerBean.createBazSpecifyingName(): nameForNewBaz is null!", showing that the value is never injected for "nameForNewBaz". The log shows that Seam is unable to find it:


                  2006-08-02 10:08:36,423 DEBUG [org.jboss.seam.jsf.SeamVariableResolver] resolving name: nameForNewBaz
                  2006-08-02 10:08:36,423 DEBUG [org.jboss.seam.Component] seam component not found: nameForNewBaz
                  2006-08-02 10:08:36,423 DEBUG [org.jboss.seam.jsf.SeamVariableResolver] resolving name: nameForNewBaz
                  2006-08-02 10:08:36,423 DEBUG [org.jboss.seam.Component] seam component not found: nameForNewBaz
                  2006-08-02 10:08:36,423 DEBUG [org.jboss.seam.jsf.SeamVariableResolver] could not resolve name
                  2006-08-02 10:08:36,423 DEBUG [org.jboss.seam.jsf.SeamVariableResolver] could not resolve name


                  "quux" does seem to be found in the conversation context:


                  2006-08-02 10:25:38,578 DEBUG [org.jboss.seam.interceptors.ConversationInterceptor] Beginning long-running conversation
                  2006-08-02 10:25:38,578 DEBUG [org.jboss.seam.Component] instantiating Seam component: conversation
                  2006-08-02 10:25:38,593 DEBUG [org.jboss.seam.contexts.Contexts] found in conversation context: quux
                  2006-08-02 10:25:38,593 DEBUG [org.jboss.seam.jsf.SeamVariableResolver] resolving name: quux
                  2006-08-02 10:25:38,593 DEBUG [org.jboss.seam.contexts.Contexts] found in conversation context: quux
                  2006-08-02 10:25:38,593 DEBUG [org.jboss.seam.jsf.SeamVariableResolver] resolved name to seam component


                  Is it possible that there are multiple conversations and my code is in the wrong one?

                  "bfo81" wrote:
                  As far as I know there's a mistake in the documentation. The default scope is not Conversation but Event -> http://www.jboss.com/index.html?module=bb&op=viewtopic&t=86491 - but I'm not sure if this only concerns normal JavaBeans (as shown in that topic's title) or also EJBs.


                  As long as the log isn't lying, I think my components are getting conversation scope:


                  2006-08-02 10:25:12,481 INFO [org.jboss.seam.Component] Component: quux, scope: CONVERSATION, type: ENTITY_BEAN, class: com.orgmob.play.Quux
                  ...
                  2006-08-02 10:25:12,513 INFO [org.jboss.seam.Component] Component: bqManager, scope: CONVERSATION, type: STATEFUL_SESSION_BEAN, class: com.orgmob.play.BQManagerBean, JNDI: member/BQManagerBean/local
                  ...
                  2006-08-02 10:25:12,528 INFO [org.jboss.seam.Component] Component: baz, scope: CONVERSATION, type: ENTITY_BEAN, class: com.orgmob.play.Baz



                  • 6. Re: where is <h:inputText> going?

                    Gavin's responses came in while I was composing mine. Thank you for pointing out my JSF error:

                    "gavin.king@jboss.com" wrote:
                    "#{foo}" is not a valid value-binding expression in JSF. You need to use "#{foo.someProperty}".


                    I changed my interface BQManager to include:

                     public void setNameForNewBaz(String nameForNewBaz);
                     public String getNameForNewBaz();
                    


                    and my code for that property in class BQManagerBean to:

                     @In(required=false)
                     public void setNameForNewBaz(String nameForNewBaz) {
                     log.debug("BQManagerBean.setNameForNewBaz(): called with arg:"+nameForNewBaz);
                     this.nameForNewBaz = nameForNewBaz;
                     }
                     @Out(required=false)
                     public String getNameForNewBaz() {
                     log.debug("BQManagerBean.getNameForNewBaz(): returning:"+nameForNewBaz);
                     return nameForNewBaz;
                     }
                     private String nameForNewBaz;
                    


                    and the referencing part of bqmgr.xhtml to:

                     <h:inputText value="#{bqManager.nameForNewBaz}"/>
                    


                    but null is being passed to the setter multiple times as seen in this log snippet:


                    2006-08-02 11:27:34,126 DEBUG [org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener] beginning transaction prior to phase: RENDER_RESPONSE(6)
                    2006-08-02 11:27:35,018 DEBUG [com.orgmob.play.BQManagerBean] BQManagerBean.setNameForNewBaz(): called with arg:null
                    2006-08-02 11:27:35,065 DEBUG [com.orgmob.play.BQManagerBean] BQManagerBean.setNameForNewBaz(): called with arg:null
                    2006-08-02 11:27:35,081 DEBUG [com.orgmob.play.BQManagerBean] BQManagerBean.setNameForNewBaz(): called with arg:null
                    2006-08-02 11:27:35,112 DEBUG [org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener] committing transaction after phase: RENDER_RESPONSE(6)
                    2006-08-02 11:27:35,112 DEBUG [org.jboss.seam.contexts.Lifecycle] <<< End web request


                    and the problem with no value for the "quux" variable (or null being injected?) remains.

                    I could give more of the log if that would be helpful. So far as I can tell, both inputText values are vanishing.

                    -David

                    • 7. Re: where is <h:inputText> going?

                      And, if it makes a difference, here's a bit more configuration

                      faces-config.xml snippet:

                       <application>
                       <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
                       <navigation-handler>org.jboss.seam.jsf.SeamNavigationHandler</navigation-handler>
                       <state-manager>org.jboss.seam.jsf.SeamStateManager</state-manager>
                       <variable-resolver>org.jboss.seam.jsf.SeamVariableResolver</variable-resolver>
                       </application>
                       <lifecycle>
                       <phase-listener>
                       org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener
                       </phase-listener>
                       </lifecycle>
                      


                      I think this is the/a recommended way.

                      • 8. Re: where is <h:inputText> going?
                        gavin.king

                        Why do you have @In/@Out on the nameForNewBaz property? get rid of them.

                        • 9. Re: where is <h:inputText> going?

                          It was left over from when I thought my SFSB could biject a value visible to JSF. I've removed it and the code now reads:

                           @In(required=false)
                           public void setQuux(Quux quux) {
                           log.debug("BQManagerBean.setQuux(): called with arg:"+quux);
                           this.quux = quux;
                           }
                           @Out(required=false)
                           public Quux getQuux() {
                           log.debug("BQManagerBean.getQuux(): returning:"+quux);
                           return quux;
                           }
                           private Quux quux;
                          
                           public void setNameForNewBaz(String nameForNewBaz) {
                           log.debug("BQManagerBean.setNameForNewBaz(): called with arg:"+nameForNewBaz);
                           this.nameForNewBaz = nameForNewBaz;
                           }
                           public String getNameForNewBaz() {
                           log.debug("BQManagerBean.getNameForNewBaz(): returning:"+nameForNewBaz);
                           return nameForNewBaz;
                           }
                           private String nameForNewBaz;
                          


                          BQManagerBean.setNameForNewBaz() is now never called and thus BQManagerBean.createBazSpecifyingName() still detects "nameForNewBaz is null".

                          The "rename quux" action also still doesn't work as BQManagerBean.setQuux() is never called with a value typed into the <h:inputText value="#{quux.name}" /> text input. If I change the "required=false" on the @In for quux to "create=true", then bringing up the pages causes "javax.ejb.EJBException: Application Error: no concurrent calls on stateful beans".

                          So far as I can tell, values from neither inputText, whether a property of the conversation-scoped SFSB or a conversation-scoped entity EJB annotated as being bijected to/from that SFSB, are ever injected into that SFSB. I've tried looking at Seam/JSF/Facelets/MyFaces code to see if I could figure out where it's going, but I'm lost in a sea of frameworks so far. Is there no way of tracking where an input value goes in that framework set?