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