Facelets - Accessing local variables via EL in custom tag handler
rajeshja Feb 28, 2008 6:36 PMHi, 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 beancustomer
has a propertyname
) - 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,
instead, I get an empty string.
String.class).getValue(ctx)
If I use code like this.value.getValueExpression(ctx,
instead, I get the following exception:
String.class).getType(ctx)
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