5 Replies Latest reply on Jun 22, 2010 8:58 AM by subaochen

    PAGE scoped Injection and outjection?

    subaochen
      Hi all,

      I have a strange problem: can not inject two components simultaneously. I have tried many times with many kinds of methods to find out why, and searched google and even baidu, but can not work it out. So somebody can give me any hints? For simplicity, I have built a test project, you can just follow the steps below to reproduce my problem:

      1 ./seam setup. use hsql,and create-drop database when deploy.
      2 ./seam create-project
      3 import the project into eclipse
      3 I have create two entity bean and their corresponding Home and Entity Class with JBoss toos, the code is as below:

      People.java:

      @Entity
      public class People implements Serializable
      {
          // seam-gen attributes (you should probably edit these)
          private Long id;
          private Integer version;
          private String name;

          // add additional entity attributes

          // seam-gen attribute getters/setters with annotations (you probably should edit)

          @Id @GeneratedValue
          public Long getId() {
              return id;
          }

          public void setId(Long id) {
              this.id = id;
          }

          @Version
          public Integer getVersion() {
              return version;
          }

          private void setVersion(Integer version) {
              this.version = version;
          }

          @Length(max = 20)
          public String getName() {
              return name;
          }

          public void setName(String name) {
              this.name = name;
          }

           @Override
           public String toString() {
                return "People [id=" + id + ", name=" + name + "]";
           }

      }


      Friend.java:

      @Entity
      public class Friend implements Serializable
      {
          // seam-gen attributes (you should probably edit these)
          private Long id;
          private Integer version;
          private String name;

          // add additional entity attributes

          // seam-gen attribute getters/setters with annotations (you probably should edit)

          @Id @GeneratedValue
          public Long getId() {
              return id;
          }

          public void setId(Long id) {
              this.id = id;
          }

          @Version
          public Integer getVersion() {
              return version;
          }

          private void setVersion(Integer version) {
              this.version = version;
          }

          @Length(max = 20)
          public String getName() {
              return name;
          }

          public void setName(String name) {
              this.name = name;
          }

           @Override
           public String toString() {
                return "Friend [id=" + id + ", name=" + name + "]";
           }

      }


      PeopleList.java:

      @Name("peopleList")
      public class PeopleList extends EntityQuery<People>
      {
           @Out(scope=ScopeType.PAGE)
           private People selectedUser = new People();
           
           private static final String EJBQL = "select people from People people";

           private static final String[] RESTRICTIONS = {
                     "lower(people.name) like lower(concat(#{PeopleList.people.name},'%'))",};


           private People people = new People();

           public PeopleList() {
                setEjbql(EJBQL);
                setRestrictionExpressionStrings(Arrays.asList(RESTRICTIONS));
                setMaxResults(25);
           }

           public People getPeople() {
                return people;
           }
           
           
           public void takeSelection(People selectedUser){
                this.selectedUser=selectedUser;
           }

      }



      PeopleHome.java:

      @Name("peopleHome")
      public class PeopleHome extends EntityHome<People>
      {

          @In(required = false, scope = ScopeType.PAGE)
          private People selectedUser;

        
           @In(required = false, scope = ScopeType.PAGE)
           private List<Friend> selectedFriends;
         
          @Logger
          private Log log;
         

          @Override @Begin
          public void create() {
              super.create();
          }

          public void savePeople(){
               info("selected people =" + selectedUser);
               info("selected friends=" + selectedFriends);
          }

      }



      FriendList.java:

      @Name("friendList")
      public class FriendList extends EntityQuery<Friend>
      {
           private Map<Friend, Boolean> searchedFriends = new HashMap<Friend, Boolean>(0);
           
           @Out(scope = ScopeType.PAGE)
           private List<Friend> selectedFriends = new ArrayList<Friend>(0);     
           
           private static final String EJBQL = "select friend from Friend friend";

           private static final String[] RESTRICTIONS = {
                     "lower(friend.name) like lower(concat(#{FriendList.friend.name},'%'))",};


           private Friend friend = new Friend();

           public FriendList() {
                setEjbql(EJBQL);
                setRestrictionExpressionStrings(Arrays.asList(RESTRICTIONS));
                setMaxResults(25);
           }

           public Friend getFriend() {
                return friend;
           }
           
           @Override
           public List<Friend> getResultList(){
                List<Friend> friends = super.getResultList();
                
                // initialize searchedFriends, set default to FALSE
                if (searchedFriends == null)
                     searchedFriends = new HashMap<Friend, Boolean>(0);
                
                for(Friend f:friends){
                     searchedFriends.put(f, Boolean.FALSE);
                }
                
                return friends;
           }

           
           
           public void selectFriends() {
                for (Friend f : getSearchedFriends().keySet()) {
                     if (getSearchedFriends().get(f) == true){
                          selectedFriends.add(f);
                     }
                }     
           }

           public void setSearchedFriends(Map<Friend, Boolean> searchedFriends) {
                this.searchedFriends = searchedFriends;
           }

           public Map<Friend, Boolean> getSearchedFriends() {
                return searchedFriends;
           }

      }



      FriendHome.java:

      @Name("friendHome")
      public class FriendHome extends EntityHome<Friend>
      {
          @RequestParameter Long friendId;

          @Override
          public Object getId()
          {
              if (friendId == null)
              {
                  return super.getId();
              }
              else
              {
                  return friendId;
              }
          }

          @Override @Begin
          public void create() {
              super.create();
          }

      }


      4 I have create 3 xhtml file as below:

      home.xhtml:

      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
          xmlns:s="http://jboss.com/products/seam/taglib"
          xmlns:ui="http://java.sun.com/jsf/facelets"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:a="http://richfaces.org/a4j"
          xmlns:rich="http://richfaces.org/rich"
          template="layout/template.xhtml">

          <ui:define name="body">

              <h1>Welcome to Seam!</h1>
              <rich:panel>
                  <h:form>
                      <h:inputText value="#{selectedUser.name}" id="userSelection"/>
                     
                      <a:commandLink 
                             value="click to select user"
                        oncomplete="#{rich:component('findPeople')}.show()"/>
                      <br/>
                    
                     
                      <a:commandLink 
                             value="click to select friends"
                        oncomplete="#{rich:component('findFriend')}.show()"/>                 
                      <br/>
                     
                      <a:outputPanel id="myFriends">
                      <rich:dataTable
                            var="_friend"
                          value="#{selectedFriends.toArray()}"
                       rendered="#{selectedFriends.size() > 0}">
                         <f:facet name="header">my friend list</f:facet>

                          <h:column width="200">
                              <f:facet name="header">name</f:facet>
                              <h:outputText value="#{_friend.name}"/>
                          </h:column>          
                      </rich:dataTable>
                      </a:outputPanel>
                 
                      <br/>
                      <h:commandButton value="Test Button,please check the console after click here" action="#{peopleHome.savePeople}"/>    
                  </h:form>
              </rich:panel>

              <ui:include src="usersearchpanel.xhtml" >
              <ui:param name="rerenderId" value="userSelection"/>
              </ui:include>

              <ui:include src="friendsearchpanel.xhtml" >
              <ui:param name="rerenderId" value="friendSelection"/>
              </ui:include>
           
          </ui:define>
      </ui:composition>



      usersearchpanel.xhtml:

      <ui:composition  xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core"
            xmlns:a="http://richfaces.org/a4j"
            xmlns:rich="http://richfaces.org/rich"
            xmlns:s="http://jboss.com/products/seam/taglib">

          <rich:modalPanel id="findPeople" minWidth="300"   autosized="true" zindex="2000">
              <f:facet name="header">Search People</f:facet>
             
              <f:facet name="controls">
                  <h:panelGroup>
                      <h:outputText value="close" styleClass="hidelink" id="hideusersearchlink"/>
                      <rich:componentControl for="findPeople" attachTo="hideusersearchlink" operation="hide" event="onclick"/>
                  </h:panelGroup>
              </f:facet>

      <h:form>
          <rich:dataTable
                      var="_user"
                    value="#{peopleList.resultList}"
                 rendered="#{peopleList.resultList.size() > 0}">

              <h:column>
                  <f:facet name="header">name</f:facet>
                  <h:outputText value="#{_user.name}"/>
              </h:column>

              <rich:column styleClass="action">
                  <f:facet name="header">Action</f:facet>
                  <a:commandLink
                      value="choose"
                   reRender="userSelection"
                     action="#{peopleList.takeSelection(_user)}"
                 oncomplete="#{rich:component('findPeople')}.hide()"/>          
              </rich:column>
             
          </rich:dataTable>
      </h:form>

          </rich:modalPanel>
                   
      </ui:composition>



      friendsearchpanel.xhtml:

      <ui:composition  xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core"
            xmlns:a="http://richfaces.org/a4j"
            xmlns:rich="http://richfaces.org/rich"
            xmlns:s="http://jboss.com/products/seam/taglib">

          <rich:modalPanel id="findFriend" minWidth="300"   autosized="true" zindex="2000" >
              <f:facet name="header">Search People</f:facet>
             
              <f:facet name="controls">
                  <h:panelGroup>
                      <h:outputText value="close" styleClass="hidelink" id="hideFriendlink"/>
                      <rich:componentControl for="findFriend" attachTo="hideFriendlink" operation="hide" event="onclick"/>
                  </h:panelGroup>
              </f:facet>

      <h:form>
          <rich:dataTable
                      var="_user"
                    value="#{friendList.resultList}"
                 rendered="#{friendList.resultList.size() > 0}">

              <h:column>
                  <f:facet name="header">choose</f:facet>
                      <h:selectBooleanCheckbox value="#{searchedFriends[_user]}"/>
              </h:column>          

              <h:column>
                  <f:facet name="header">name</f:facet>
                  <h:outputText value="#{_user.name}"/>
              </h:column>
          </rich:dataTable>
              <div class="actionButtons">
                  <a:commandButton 
                      value="submit"
                      action="#{friendList.selectFriends}"
                      reRender="myFriends"
                      oncomplete="#{rich:component('findFriend')}.hide()"/>
              </div>   
      </h:form>

         

          </rich:modalPanel>
                   
      </ui:composition>


      5 the import-dev file:

      insert into people(name) values('tom');
      insert into people(name) values('sam');

      insert into friend(name) values('susan');
      insert into friend(name) values('jude');

      That's all! you can copy above files to the correspoding directories to deploy the test project.

      The question is: in the home.xhtml, I first select one person from usersearchpanel.xhtml, and then get two friends from friendsearchpanel.xhtml, but when click the test button which binding to PeopleHome.savePeople method, I get the following output from the console:

      INFO  [PeopleHome] selected people =People [id=null, name=tom]
      INFO  [PeopleHome] selected friends=[Friend [id=1, name=susan], Friend [id=2, name=jude]]

      That meams, the selected person is lost, and only selected friends are avaliable from savePeople method. So, why selectedUser (outjected in PeopleList.java and injected in PersonHome.java) is lost?

      More strangely, if I only select one person(don't click "select friends"), then selectedUser is available from PeopleHome.savePeople method. That is , I can not get selectedUser and selectedFriends both available from PeopleHome.savePeople method.

      Anybody would like to help me? Thanks in advance!

      Su Baochen
        • 1. Re: PAGE scoped Injection and outjection?
          amitev
          @Name("peopleList")
          public class PeopleList extends EntityQuery<People> {
           @Out(scope=ScopeType.PAGE)
           private People selectedUser = new People();
          }
          



          I don't think that's right. Every time a method of peolpleList gets called, you'll get a fresh new instance of selectedUser in the page scope.

          • 2. Re: PAGE scoped Injection and outjection?
            subaochen

            Thanks Adrian Mitev!

            You are right! And I have rewrite the code like this:

            |       @Out(required=false,scope=ScopeType.PAGE)
                    @In(required=false)
                    private People selectedUser;|

            Notice that I have added @In(required=false), because everytime the method in PeopleList was called, we should inject selectedUser(maybe outjected before) again.
                   
            Thanks,

            Su Baochen
            • 3. Re: PAGE scoped Injection and outjection?
              subaochen
              Hi all,

              But when use injected component in the page, we must check if the component is null, such as:

                  <c:choose>
                      <c:when test="#{selectedUser != null}">
                          <h:inputText value="#{selectedUser.username}" id="userSelection"/>
                      </c:when>
                      <c:otherwise>
                          <h:inputText  id="userSelection"/>
                      </c:otherwise>
                  </c:choose>

              anybody has better method to check if the component == null ? code below don't work:

              <h:inputText value="#{selectedUser == null?'':selectedUser.username}"/>

              Thanks in advance!

              Su Baochen
              • 4. Re: PAGE scoped Injection and outjection?
                kragoth

                Su Baochen wrote on Jun 20, 2010 20:58:


                Hi all,

                But when use injected component in the page, we must check if the component is null, such as:

                    <c:choose>
                        <c:when test="#{selectedUser != null}">
                            <h:inputText value="#{selectedUser.username}" id="userSelection"/>
                        </c:when>
                        <c:otherwise>
                            <h:inputText  id="userSelection"/>
                        </c:otherwise>
                    </c:choose>
                



                anybody has better method to check if the component == null ? code below don't work:

                <h:inputText value="#{selectedUser == null?'':selectedUser.username}"/>
                



                Thanks in advance!

                Su Baochen


                Use the standard JSF way of checking variables (well, at least this is the way I see it being done most of the time)


                value="#{empty selectedUser ? '' : selectedUser.username}"
                



                See if that works. (It does work as I'm using the same concept in my project so you might just need to make sure you are using correct variables etc).


                Also, please try to remember to surround your code with backtics (`) so that it is easier for us to read. :)

                • 5. Re: PAGE scoped Injection and outjection?
                  subaochen

                  Thanks Tim!


                  Yes, it works just like this:


                  <h:outputText value="#{empty selectedUser?'':selectedUser.username}"/>
                  



                  But don't work with inputText.