Skip to main content

adding composite components dynamically

2 replies [Last post]
stdarg
Offline
Joined: 2009-03-28

I'm trying to add a composite component to a page dynamically.

mypage.xhtml contains this:

MyBean.java (a managed, request scoped bean) contains this:

public void addTheComponents(ComponentSystemEvent event) {
FacesContext context = FacesContext.getCurrentInstance();
Resource componentResource = context.getApplication().getResourceHandler().createResource("mycomp.xhtml", "components");
UIComponent component = context.getApplication().createComponent(context, componentResource);
event.getComponent().getChildren().add(component);
}

/resources/components/mycomp.xhtml is just a very simple composite component with an h:outputText. It works correctly when referenced directly in mypage.xhtml.

When I load the page, addTheComponents is called, the componentResource.getURL() looks correct (it's jndi:/server/Jsf2Test/resources/components/mycomp.xhtml), but the newly created component seems to be incomplete. Looking at it in the debugger, it's got null for just about every attribute except parent (after the add()). Id and children are both null, which seems weird. Aside from that oddity, addTheComponents() completes without issue. However, when the view is rendered, I get an exception:

java.io.IOException: PENDING_I18N: Unable to find composite component root for composite component with id null and class javax.faces.component.UINamingContainer
at com.sun.faces.renderkit.html_basic.CompositeRenderer.encodeChildren(CompositeRenderer.java:61)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:844)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:285)
at com.sun.faces.renderkit.html_basic.GroupRenderer.encodeChildren(GroupRenderer.java:106)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:844)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1609)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1612)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1612)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1612)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:271)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:126)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:124)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:103)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:311)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1461)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:293)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:187)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:647)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:351)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:249)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:146)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:746)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:655)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:905)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:161)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:136)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:103)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:89)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

What do I do to dynamically add a composite component? Should I be listening for a different event and adding it there, or am I doing something else wrong?

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
janderssn
Offline
Joined: 2009-03-11

You should look at chapter 3.6 of the specification, which describes the composite component.

http://jcp.org/en/jsr/detail?id=314

The following works for simple composite component, but not with components using and similar tags...

http://forums.java.net/jive/thread.jspa?threadID=63877&tstart=75

I wish there was an easier way to work with composite components in java, but this is the only one I've found so far.

stdarg
Offline
Joined: 2009-03-28

Thanks for the information! I've been looking all over javaserverfaces.dev.java.net for documentation and have not been able to find anything useful, but the link you gave me is great.

Your code gave me a lot of ideas to search for. Have you looked at jsf-ri\src\com\sun\faces\facelets\tag\jsf\CompositeComponentTagHandler, particularly the method applyCompositeComponent? It is pretty similar to your code, which makes me think that to fully support dynamic composite components, one would need to replicate pretty much the entire class. I don't know how much of it is implementation specific and how much is pure API though.

Alternatively, I wonder if it's possible to trigger the built-in behavior by publishing an event or manually calling some of the phase handlers on the newly created but empty composite component. Otherwise it seems like too much work to be the right way.