4 Replies Latest reply on Feb 17, 2008 6:46 PM by Tom N

    Bug?  Outjected boolean changes value mid-template

    Tom N Newbie

      I am seeing very odd behavior where the value of an outjected boolean changes in the middle of a template, but no assignment was done. I think it has to do with how it is scoped.

      Expected/ Intended Behavior:
      The value, newPortfolioForm is toggled based on whether or not a form should be displayed on a page. It is triggered when the user clicks the "add" button. The field is EVENT scoped, such that if the user clicks "add" and then decides to do something other than submit the form, the form will be hidden again (i.e. the action is cancelled). However, on subsequent requests (i.e. any page refresh), it appears that the outjected value for newPortfolioForm is null and then changes back to true after a DataModel from the same SLSB is rendered.

      Here is my (simplified) template:

      <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:s="http://jboss.com/products/seam/taglib"
       xmlns:f="http://java.sun.com/jsf/core"
       xmlns:h="http://java.sun.com/jsf/html">
      
       <body>
       <s:button value='Add' title='create a new profile'
       action='#{portfolioHandler.addPortfolio}'
       rendered='#{!showPortfolioForm}' />
      
       <s:div rendered='#{showPortfolioForm == null}'>showPortfolioForm is null</s:div>
       <s:div rendered='#{showPortfolioForm}'>showPortfolioForm is true</s:div>
       #{portfolios} <br />
       <s:div rendered='#{showPortfolioForm == true}'>showPortfolioForm is TRUE!!!</s:div>
       <s:div rendered='#{showPortfolioForm == null}'>showPortfolioForm is null</s:div>
       <s:div rendered='#{showPortfolioForm == false}'>showPortfolioForm is false</s:div>
       </body>
      </html>
      


      And my SLSB:
      @Stateless @Name("portfolioHandler")
      public class PortfolioHandlerImpl implements PortfolioHandler {
      
       @DataModelSelection("portfolios")
       @In(required=false) @Out(required=false)
       private Portfolio portfolio;
      
       @Out private boolean showPortfolioForm = false;
      
       @DataModel List<Portfolio> portfolios;
      
       public void addPortfolio() {
       showPortfolioForm = true; // display the form
       }
      
       public void updatePortfolio() {
       // save method...
       }
      
       // factory and other CRUD methods here...
      }
      


      Output after initial page load (OK):
      showPortfolioForm is null
      org.jboss.seam.jsf.ListDataModel@c74f8c4
      showPortfolioForm is false
      


      Output after button click (OK - consistent state):
      showPortfolioForm is true
      org.jboss.seam.jsf.ListDataModel@1f65c446
      showPortfolioForm is TRUE!!!
      


      Output after page reload (BAD - it changed!!):
      showPortfolioForm is null
      org.jboss.seam.jsf.ListDataModel@35efd6ed
      showPortfolioForm is TRUE!!!
      


      Now, I'm not surprised that showPortfolioForm was boxed to a Boolean and is null. However -- why does the value change after #{portfolios} is referenced?!? Null and false are close enough as far as boolean logic is concerned that I didn't notice this is actually happening on the first page load too -- the value is null and then false.

      I would submit that this is a bug -- the state within the template is not consistent, even though none of my code has changed it. There is a similar effect if the scope is set to PAGE. If the value was re-initialized to false it would be OK, but why is it picking up the old value even though it is clearly out of scope?

        • 1. Re: Bug?  Outjected boolean changes value mid-template
          Pete Muir Master

          1) If you have an @Stateless bean it can't have any fields on it. read about what @Stateless *really* means (this probably explains your values changing)

          2) This is because you are outjecting into EVENT scope. So, the process is this whilst rendering the page

          Try to access boolean value, not in context, so null and so on
          Render datatable, initialized by @Factory on seam component as boolean value. component does outjection including boolean value,
          Try to access boolean value, now in context.

          • 2. Re: Bug?  Outjected boolean changes value mid-template
            Tom N Newbie

            Hi Pete -- thanks for your reply.

            "pete.muir@jboss.org" wrote:
            1) If you have an @Stateless bean it can't have any fields on it. read about what @Stateless *really* means (this probably explains your values changing)


            I understand that I can't keep state in my SLSB, but I thought the point was that you could biject stateful values into your SLSBs. See http://books.google.com/books?id=h1madTvupB8C&pg=PA162&vq=listing+6-2&sig=gedSD8Cnzsm8Oj-tDxPWs7pmvkk


            "pete.muir@jboss.org" wrote:

            2) This is because you are outjecting into EVENT scope. So, the process is this whilst rendering the page

            Try to access boolean value, not in context, so null and so on
            Render datatable, initialized by @Factory on seam component as boolean value. component does outjection including boolean value,
            Try to access boolean value, now in context.


            I can understand that it is null at the beginning of the request (since there is no @Factory for it) but if the value is not explicitly set in an action, the value should remain null. That is, I would expect Seam's bijection mechanism to say "oh this value was set on a previous request so it is out of scope now" rather than suddenly finding the value when a totally different context variable is asked for from the context.

            So I think I understand -- the SLSB is actually maintaining state even though bijection would make it seem that it is not so. It would seem to me that bijection is broken by design unless a factory is not specified for each and every bijected field. No?

            This does happen just the same if the fields are PAGE scoped -- i.e. the value is set to true, go to a different page, come back, and the same behavior (null, then old stale value) appears. I imagine it would also happen as well for a SFSB -- Say you have a session-scoped SFSB, and say an @Out(scope=PAGE) List myList. If I don't have a factory for myList, and instead initialize the list on some action. Then leave this page and come back... I'll see the list again after something else wakes up that SFSB? If that is the case I would argue that it is broken.

            More to the point, how would you get around this issue? How would you toggle a flag to show a simple form that you want to automatically reset if the user leaves the page or does something else?
            * Put an auto-action in pages.xml to reset everything? That seems annoying but doable (I have a number of pages that use this pattern)

            * Use a conversation? It seems like overkill for just one stateful flag. And if they do _anything_ else, I have to make sure that conversation ends, no? How do I make sure the conversation ends if they decide to do something totally unrelated to the conversation?

            * Create a factory that simply returns null or false in order to reset the value? I'm guessing the factory will definitely be called when the value is re-referenced after it goes out of scope!?! But it turns out in my non-simplified, real world code I actually have two flags for different forms, and then two more flags for whether or not they are new or updated domain objects. So suddenly I have to create four factories just initialize a couple of boolean fields. This is annoying.

            What if Seam had some sort of implicit "null factory" that would automatically reset bijected values that went out of scope? That would fix my problem.

            What do you think? I'm dealing with such a simple state that all of Seam's state management mechanisms are overkill. It seems to be making things harder, whereas if I was using an MVC framework, I would just put a showForm:true flag in my model, which of course is gone after the response is rendered.

            Suggestions?

            Thanks.

            • 3. Re: Bug?  Outjected boolean changes value mid-template
              Pete Muir Master

               

              "tomstrummer" wrote:
              I understand that I can't keep state in my SLSB, but I thought the point was that you could biject stateful values into your SLSBs. See http://books.google.com/books?id=h1madTvupB8C&pg=PA162&vq=listing+6-2&sig=gedSD8Cnzsm8Oj-tDxPWs7pmvkk


              Yes, correct.

              You explicitly set the value to false in your bean (boolean showPortfolioForm = false;) - so your point about it not being explicitly set is wrong.

              What do you expect Seam to do? Use telepathy to read what you intended rather than what you programmed?

              My suggestion - use a Boolean rather than a boolean, and only set it when you explicitly need to.

              • 4. Re: Bug?  Outjected boolean changes value mid-template
                Tom N Newbie

                I expect Seam to re-initialize the value when it goes out of scope.

                How does Seam know when a context variable goes out of scope? If Seam sees a @Out variable go out of scope, then it sees the bijected field with no factory... It should just ignore it and leave whatever value happened to be there? Once the value goes out of scope, I hoped it would be re-initialized to false. Ok, so I wouldn't be surprised if you couldn't keep track of the initial value, but at least set it to null.

                Using a Boolean doesn't help. And the problem is I only set the value to true when I need to (show the form). The rest of the time, i.e. after it goes out of scope, I need it to be false. But like I said, that means I need a factory just to re-initialize a page-scoped boolean. Otherwise it hangs on to the old value.

                Can you elaborate on your suggestion? My situation feels analogous to having a variable defined in a method, and the variable is getting the value it had when method was last called. (Obviously the mechanics are very different but from a user standpoint I am in the same situation.)

                I solved my problem making all of my beans that keep track of the form state stateful & page scoped.

                Like I said, think I understand why this is happening, I just don't think it's a very desirable behavior. It sounds like the answer is "don't biject a narrow-scoped field into a wide-scoped bean unless you have a factory. If you do, you'll have an inconsistent state when the template is rendered." But regardless of why it is happening, I don't think it is um, "good" behavior or desirable in any situation and suggest it be disallowed or at least document some sort of warning or output a log warning when such a thing is detected.... Is that at least possible?

                Thanks.