2 Replies Latest reply on Mar 3, 2008 11:46 AM by rajeshja

    Facelets - Accessing local variables via EL in custom tag handler

    rajeshja

      Hi, this is a cross post from the facelets users and dev lists. I didn't get much of a response there, and I'm beginning to feel this might be a bug in com/sun/facelets/impl/DefaultFaceletContext.java, though I'm not sure yet.


      Here's the description from the original post:


      I'm using Facelets 1.1.13 with Seam 1.2.1 under WebLogic 10, and have written a custom tag handler, to implement a tag library in my application.


      Here's what I do:


           public final class MyTagHandler extends TagHandler {
           
               private final TagAttribute value;
               // Some more attributes
            
               public MyTagHandler(TagConfig config) {
                   super(config);
                   this.value = this.getAttribute("value");
               }
           
               public void apply(FaceletContext ctx, UIComponent parent)
                       throws IOException, FacesException, FaceletException, ELException {
                  // Some processing goes here.
                  Object value = getValue(ctx);
                  // More processing
                 return;
               }
           
               public Object getValue(FaceletContext ctx) {
                   if (this.value != null) {
                       return this.value.getObject(ctx);
                   } else {
                       return null;
                   }
               }
           .
           .
           .
           }
      




      This is how I set it up in the taglib.xml:


           <facelet-taglib>
            <namespace>http://mydomain.net/tags</namespace>
            <tag>
             <tag-name>mytag</tag-name>
             <handler-class>net.mydomain.MyTagHandler</handler-class>
            </tag>
           </facelet-taglib>
      



      This is how the tag is used in the XHTML files:



           <myprefix:mytag value="somevalue">
      




      • If somevalue is a literal, this works fine.

      • If somevalue is an EL expression of the form #{customer.name} (where the bean customer has a property name)

      • a. If customer is a component defined in faces-config.xml, then it works fine

      • b. If customer is outjected (using the Out Seam annotation) to - say - the session scope, then this works fine

      • c. If customers (plural), is a component which has multiple customers, and I use a tag like c:forEach or h:dataTable to loop over each customer, like so:



                <c:forEach items="#{customers}" var="customer">
                  <myprefix:mytag value="#{customer.name}"/>
                </c:forEach>
      



           then getValue(ctx) as written above in the apply method, evaluates to null.


      If I use this.value.getValue(ctx); instead of this.value.getObject(ctx);, it's the same thing.
      If I use code like this.value.getValueExpression(ctx,
      String.class).getValue(ctx)
      instead, I get an empty string.
      If I use code like this.value.getValueExpression(ctx,
      String.class).getType(ctx)
      instead, I get the following exception:



      Feb 20, 2008 8:06:37 PM com.sun.facelets.FaceletViewHandler
      handleRenderException
      SEVERE: Error Rendering View\[/mypage.xhtml\]
      javax.el.PropertyNotFoundException: /mypage.xhtml @97,66
      value="${customer.name}": Target Unreachable, identifier 'customer'
      resolved to null
              at
      com.sun.facelets.el.TagValueExpression.getType(TagValueExpression.java:6
      2)
              at
      net.mydomain.MyTagHandler.getValue(DisplayStringParamTagHandler.java:122
      )
              at
      net.mydomain.MyTagHandler.apply(DynamicDisplayStringHandler.java:81)
              at
      com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandl
      er.java:47)
              at
      com.sun.facelets.tag.jsf.ComponentHandler.applyNextHandler(ComponentHand
      ler.java:314)
              at
      com.sun.facelets.tag.jsf.ComponentHandler.apply(ComponentHandler.java:16
      9)
              at
      com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandl
      er.java:47)
              at
      com.sun.facelets.tag.jsf.ComponentHandler.applyNextHandler(ComponentHand
      ler.java:314)
              at
      com.sun.facelets.tag.jsf.ComponentHandler.apply(ComponentHandler.java:16
      9)
              at
      com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandl
      er.java:47)
              at
      com.sun.facelets.tag.jsf.core.ViewHandler.apply(ViewHandler.java:109)
              at
      com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandl
      er.java:47)
              at
      com.sun.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:4
      9)
              at
      com.sun.facelets.tag.CompositeFaceletHandler.apply(CompositeFaceletHandl
      er.java:47)
              at
      com.sun.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:25)
              at
      com.sun.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:95)
              at
      com.sun.facelets.FaceletViewHandler.buildView(FaceletViewHandler.java:50
      3)
              at
      com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:5
      46)
              at
      org.ajax4jsf.framework.ViewHandlerWrapper.renderView(ViewHandlerWrapper.
      java:108)
              at
      org.ajax4jsf.framework.ajax.AjaxViewHandler.renderView(AjaxViewHandler.j
      ava:229)
              at
      org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderRespon
      seExecutor.java:41)
              at
      org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:132
      )
              at
      javax.faces.webapp.FacesServlet.service(FacesServlet.java:140)
              at
      weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(St
      ubSecurityHelper.java:226)
              at
      weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityH
      elper.java:124)
              at
      weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:2
      83)
              at
      weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
              at
      weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:
      42)
              at
      org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:6
      3)
              at
      org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
              at
      org.jboss.seam.web.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:4
      9)
              at
      org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:57)
      
      



      Note that code like -


                <c:forEach items="#{customers}" var="customer">
                  #{customer.name}
                </c:forEach>
      



      - works just fine.


      I'm guessing that maybe the ELContext that the customer variable is stored to, is different than the one my tag tries to use. What I can't understand is Why?, and How do I fix it?


      Any insights into this would be highly appreciated.


      Regards,


      Rajesh

        • 1. Re: Facelets - Accessing local variables via EL in custom tag handler
          keithnaas

          Remember that TagHandlers are evaluated when the component tree is built, not when the tree is rendered.


          For an example of this, you might want to look at c:forEach vs ui:repeat in Facelets.

          • 2. Re: Facelets - Accessing local variables via EL in custom tag handler
            rajeshja

            Hey thanks! I created a Component and wired that to my component and it worked - mostly.


            If the Component I write extends from UIComponentBase, things work fine.


            However the purpose of my tag is to produce some output, and so I extended from UIOutput instead, overriding getValue(). However the attributes that contain the EL expressions I need, don't seem to get retrieved from the getAttributes() method.
            For now I'm using syntax like -



                <myprefix:mytag literalvalue="someliteral">
                     <myprefix:subtag value="#{customer.name}"/>
                 </myprefix:mytag>
            



            - where mytag is backed by a component that extends UIOutput and subtag is backed by a component that extends UIComponentBase.


            That solution is obviously kludgy, but I haven't been able to figure out why it one tag but not another. Any clue?