9 Replies Latest reply on Aug 11, 2009 3:53 PM by Jens Weintraut

    addToControlFromResourceBundle messages aren't <s:decorate>d

    Jens Weintraut Apprentice

      Hi,


      I'm using Seam 2.1.1.GA with ICEfaces 1.8.1 on a JBoss AS 5.1.0.GA and have a problem decorating invalid input fields.


      The fields are decorated like this:


      <ice:form id="editEntity">
      <!-- some more fields here -->
        <s:decorate id="decorate_chnglistID" template="inc/decorateField.xhtml">
          <h:inputText value="#{entity.chnglistID}" id="chnglistID" />
        </s:decorate>
      
      </ice:form>
      



      The inc/decorateField.xhtml is as follows:


      <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:s="http://jboss.com/products/seam/taglib">
                        
        <span class="#{invalid?'fieldFailed':''}">
          <ui:insert />
        </span>
      </ui:composition>
      



      Examining the source code of the generated page shows me that the decorating works. The value class of the span tag is empty.
      In the Seam component which is invoked by submitting this page calls FacesMessages.addToControlFromResourceBundle to display errors:


      @Override
      protected boolean applyChanges() {
        try {
          otherComponent.validateEntity(entity);
        } catch (MissingPropertiesException e) {
          MessageMap map = MissingPropertyMessageMap.getInstance();
      
          for(YTrackProperty property : e.getMissingProperties()) {
            facesMessages.addToControlFromResourceBundle(map.getID(property), Severity.WARN, map.getDetailMessage(property));
          }
      
          return false;
        } catch (IllegalException e) {
          MessageMap map = IllegalStateMessageMap.getInstance();
      
          for(YTrackProperty property : e.getIllegalProperties()) {
            facesMessages.addToControlFromResourceBundle(map.getID(property), Severity.ERROR, map.getDetailMessage(property));
          }
      
          return false;
        }
      
        saveEntityAndProtocolChanges();
        return true;
      }
      



      As I already said the decoration works (remember that the span tag from the file inc/decorateField.xhtml is rendered). The problem now is, that in case of an invalid field (facesMessages.addToControlFromResourceBundle is called) the class attribute of the span tag doesn't change to fieldFailed. It stays empty instead.


      I already tried to specify the id of the control hardcoded (editEntity:decorate_chnglistID:chnglistID and chnglistID), but that didn't work. The call map.getID(property) returns chnglistID, so this should work. What else can be wrong?


      Thanks in advance
      Jens

        • 1. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
          judy guglielmin Novice

          How are you setting the invalid property?  Are you sure it is being set?

          • 2. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
            Jens Weintraut Apprentice

            I'm sorry for answering so late. The notification mail was identified as spam and I was on holiday.


            The invalid property is automatically set by the decorate tag, at least Seam doc states it would do so.


            I spent the last hour on debugging what is going on when my application adds a messages to a component via the facesMessages component. I now know why my controls aren't decorated, but I don't know whether I have made a mistake or it's a Seam/ICEfaces bug.


            Let me try to explain the problem. The call on facesMessages.addToControlFromResourceBundle successfully adds a new key-value-pair in facesMessages's message map. The key of this map is represented by the component's id and the value is a single message or rather a list of messages.
            In the next step, after the application phase, Seam calls facesMessages.beforeRenderResponse() to transfer the internal messages to JSF. Remember that facesMessages is a JSF version of the StatusMessages class, which abstracts messages from a specific underlying framework, in this case JSF. In order to do this, Seam converts the id of the component which should be decorated to the matching id in the JSF view root by calling the private facesMessages.getClientId() method. This method recursively iterates over the facets and children of the view root comparing ids.


            The problem now is that the view root that BridgeFacesContext returns to the facesMessages component doesn't have a children or facet. The view root is an instance of SettableLocaleViewRoot and only knows about the viewId, the renderKitId and some attributes in the attributes and pdMap members. All the other object attributes like children, facets, etc. are uninitialized.


            Since Seam can't find a matching id in the view root for the given component id, the messages are attached to the component null, which means that the decoration can't work.


            I don't know why the view root is in such a state that it doesn't know about its children. Besides the decoration problem the application works fine. What do you think about this? Is it a mistake I made or is it a bug? Who is responsible for this bug? Should I open a JIRA issue?


            Thanks in advance
            Jens

            • 3. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
              judy guglielmin Novice

              AFAIK, the


              <s:validate>

              or


              <s:validateAll>

                must be placed around your


              <ui:insert/>

              in your decorator template.  This is what triggers the hibernate validators for the input component.  You may want to check that first (you can get an example of this from seam-gen with the edit.xhtml template).


              Second, the BridgeFacesContext does contain the ViewRoot, which contains the component tree. You may want to determine what the state is within each phase of the jsf lifecycle to see where the problem lies.  With jsf1.2, the component saves state except for at the very beginning or end of the lifecycle (with standard request scope this is restored each lifecycle).  If you are using component binding, then the scope of the backing bean may also affect the state.






              • 4. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
                Jens Weintraut Apprentice

                Actually I could remove those s:validates since I don't use the Hibernate validator. The problem I have with Hibernate validator is that I don't know if it can handle more complex validations. Other parts of the application validate an entity regarding specific combinations of inputs. That's why I wrote all the validations myself in the application code adding explanatory messages. And this should work since the decorator decorates input fields in this case.


                And I know that the BridgeFacesContext should contain the ViewRoot. So I took some time and debugged again. Now I know that the JSF lifecycle is executed (at least) two times. In the first pass the BridgeFacesContext.setViewRoot method is called in the Restore View phase. And the given ViewRoot contains the component tree. All further calls of the BridgeFacesContext.getViewRoot method return a ViewRoot with the component tree.
                But in teh second pass of the JSF lifecycle the BridgeFacesContext.setViewRoot method is called (again in the Restore View phase). And now the ViewRoot does not contain the component tree. Unfortunately, the decorator is executed in this second pass of the JSF lifecylce. Therefore it tries to transfer the messages to an empty component tree with the result that the input fields are not decorated.


                Writing this I remember such warnings in some parts of my application:


                
                [javax.enterprise.resource.webcontainer.jsf.lifecycle] (http-localhost%2F127.0.0.1-8080-4) WARNING: FacesMessage(s) have been enqueued, but may not have been displayed.
                
                

                • 5. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
                  judy guglielmin Novice

                  I have done something similar but in a single jsf lifecycle (at least I think it's similar).  But I have used the


                  <s:validate>



                  or


                  <s:validateAll>



                  and used my own validators in conjunction with hibernate.
                  If you wanted any example please ask for this on the ICEfaces forum


                  Otherwise, you could always create a simple sample application, create an ICEfaces jira and attach this sample so we could take a look at it.


                  As for those warnings, they have been fixed and will be available in ICEfaces-1.8.2 which should be released in the next month or so.  jira ICE-1900

                  • 6. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
                    Jens Weintraut Apprentice

                    Hi Judy,


                    I don't know why I didn't create a sample application earlier. However, I just created one using seam-gen, adding a decorated text field and a button. The action called as soon as one clicks the button only adds a message to the decorated text field. And as you stated, it worked.
                    But then I got an idea. I also transferred my pages.xml to this sample application. And thus I have a sample application that shows the same effect that I described. A message is shown, but there is now decoration.


                    As soon as I know, which framework (myself, Seam or ICEfaces) has to fix the problem, I will open up an issue (or bang my head against the wall). So I will demonstrate my sample application here and someone please let me know where to open an issue ;)


                    Here is the page:


                    <ice:form id="form">
                      <h:messages id="messages" layout="table" showDetail="true" />
                    
                      <s:decorate id="decorate_funnyText" template="decoratorTemplate.xhtml">
                        <h:inputText value="#{messageAdder.funnyText}" id="funnyText" />
                      </s:decorate>
                    
                      <ice:commandButton partialSubmit="true" action="#{messageAdder.addMessage}" value="Let's decorate!" />
                    </ice:form>



                    This is the decoration template:


                    <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:s="http://jboss.com/products/seam/taglib">
                    
                      <span style="#{invalid?'border:5px red solid;':''}">
                        <ui:insert />
                      </span>
                    </ui:composition>



                    This is the messageAdder component:


                    @Stateful
                    @Scope(ScopeType.CONVERSATION)
                    @Name("messageAdder")
                    public class MessageAdder implements IMessageAdder, Serializable {
                      private static final long serialVersionUID = 7626485366852012294L;
                    
                      @In
                      private FacesMessages facesMessages;
                    
                      private String funnyText;
                    
                    
                      public String addMessage() {
                        facesMessages.addToControlFromResourceBundle("funnyText", Severity.ERROR, "There is something wrong with '" + funnyText + "'.");
                        return "invalid";
                      }
                    
                      public String getFunnyText() {
                        return funnyText;
                      }
                    
                      public void setFunnyText(String funnyText) {
                        this.funnyText = funnyText;
                      }
                    
                      @Remove @Destroy
                      public void destroy() {
                      }
                    }



                    I use facesMessages.addToControlFromResourceBundle even if I use a hard coded message string since I also use this message in my application. It doesn't matter, I think.


                    And here is a part from the pages.xml:


                    <page view-id="/home.xhtml">
                      <navigation>
                        <rule if-outcome="valid">
                          <end-conversation before-redirect="true" />
                          <redirect view-id="/home.xhtml"/>
                        </rule>
                        <rule if-outcome="invalid">
                          <render view-id="/home.xhtml"/>
                        </rule>
                      </navigation>
                    </page>



                    The real interesting part is the pages.xml rule for the outcome invalid. If you remove it, the decoration works fine and only the h:messages message inside the form  is displayed. If you keep it, the decoration doesn't work anymore and there are two messages shown. One comes from the h:messages in the layout template which only shows global messages and the other comes from the h:messages inside the form.


                    I hope you can reproduce the problem. Maybe that I did something wrong with this navigation rule. But perhaps it's a bug.


                    And ... thank you for your help. It's really great.
                    Jens

                    • 7. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
                      Jens Weintraut Apprentice

                      Jens Weintraut wrote on Aug 06, 2009 14:14:


                      The real interesting part is the pages.xml rule for the outcome invalid. If you remove it, the decoration works fine and only the h:messages message inside the form  is displayed. If you keep it, the decoration doesn't work anymore and there are two messages shown. One comes from the h:messages in the layout template which only shows global messages and the other comes from the h:messages inside the form.


                      I have to add that it doesn't matter if you use render or redirect to redisplay the page.

                      • 8. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
                        Ingo Jobling Master

                        Hello Jens,


                        This is not a bug, it is correct to remove the navigation rule for invalid.  As you pointed out, this  results in the decorator working, and the message displaying.  In a nutshell, stay on the same view when there is a validation error.  A frequently used convention is to return null from the action if there was an error, otherwise, return a string such as valid


                        In general, navigation rules are for outcomes indicating success, in this case, valid


                        The valid rule redirects back to the same page, which is desirable because it avoids problems with the refresh (F5) and Back key.  Something to be aware of, though, is that the before-redirect="true" attribute of the end-conversation will result in messages (for example, a confirmation message) not being displayed.  This is because the messages are carried over to the next view by being stored in conversation scope.


                        Hope this helps ...


                        Regards,
                        Ingo

                        • 9. Re: addToControlFromResourceBundle messages aren't <s:decorate>d
                          Jens Weintraut Apprentice

                          Thanks for this explanation. I'll check all the boundaries I have.