I'm dynamically injecting some JS into all my pages, and this works fine in Mojarra, but I've found out it fails in myfaces.
My event listener is configured as:
<application>
<system-event-listener>
<system-event-listener-class>a.b.HeadResourceListener</system-event-listener-class>
<system-event-class>javax.faces.event.PostAddToViewEvent</system-event-class>
<source-class>javax.faces.component.UIOutput</source-class>
</system-event-listener>
</application>
With code looking something like:
public class HeadResourceListener implements SystemEventListener {
@Override
public boolean isListenerForSource(Object source) {
return "javax.faces.Head".equals(((UIComponent) source).getRendererType());
}
@Override
public void processEvent(SystemEvent event) {
UIComponent outputScript = new UIOutput();
outputScript.setRendererType("javax.faces.resource.Script");
UIOutput content = new UIOutput();
content.setValue("var abc='';");
outputScript.getChildren().add(content);
context.getViewRoot().addComponentResource(context, outputScript, "head");
}
}
Unfortunately, with myfaces, the rendererType of the source is never javax.faces.Head (I only found occurrences of javax.faces.resources.Script and javax.faces.resources.Stylesheet)
Is there any specific reason why the behaviour differs here? Any suggestions for another solution maybe?
EDIT
As suggested, when linking this listener to source-class , it is triggered in myfaces. However, on postback, I get duplicate id errors...
Caused by: org.apache.myfaces.view.facelets.compiler.DuplicateIdException: Component with duplicate id "j_id__v_7" found. The first component is {Component- Path : [Class: javax.faces.component.UIViewRoot,ViewId: /user/login.xhtml][Class: org.apache.myfaces.component.ComponentResourceContainer,Id: javax_faces_location_head][Class: javax.faces.component.UIOutput,Id: j_id__v_7]}
at org.apache.myfaces.view.facelets.compiler.CheckDuplicateIdFaceletUtils.createAndQueueException(CheckDuplicateIdFaceletUtils.java:148)
at [internal classes]
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:73)
at org.apache.myfaces.tomahawk.application.ResourceViewHandlerWrapper.renderView(ResourceViewHandlerWrapper.java:169)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:73)
It's a bug in MyFaces.
JSF 2.3 specification says the following in table 9.2:
TABLE 9-2 Standard HTML RenderKit Tag Library
getComponentType() getRendererType() javax.faces.Output javax.faces.Head
As per chapter 4.1.10.1 of the same specification, javax.faces.Output
maps to javax.faces.component.UIOutput
.
4.1.10.1 Component Type
The standard component type for
UIOutput
components is ājavax.faces.Output
ā.
So, the <h:head>
must be an instance of UIOutput
.
If we look back at table 9.2, the javax.faces.Output
can have multiple renderers, so you can indeed only listen on <source-class>
of javax.faces.component.UIOutput
and you'd have to manually inspect its renderer type to be javax.faces.Head
. Your HeadResourceListener
is correct.