Skip to main content

Programmatic or dynamic construction of composite components

10 replies [Last post]
lexi
Offline
Joined: 2004-04-09
Points: 0

Hi,

I've got a question on composite components.

How is programmatic/dynamic creation of composite components supposed to work?

I've been struggling for two days on this question. I'll post my result in separate posts below.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
lexi
Offline
Joined: 2004-04-09
Points: 0

Ok, so what I've got now is more-or-less OK code which does what I need. But I hope you can imagine how frustrated I am.

Here are my questions:

1. Am I missing something? Is there a simple way to do what I want?

2. As far as I understand, this feature (programmatic creation of composite components) is foreseen in the specification. Am I right that this feature is effectively not yet implemented in Mojarra 2.0.2/2.0.3?

3. Is it going to be implemented?

4. When (well, if) it will be implemented, how is it supposed to be used? Is [b]setComponent1[/b] in one of the first posts the right code?

5. Why does Facelets has so many package-protected constructs? For instance, why a public class with a package-protected method? Why so restrictive? Is prohibiting extensions an intended strategy (like, "don't you touch our tag handlers")? The rest of Mojarra is not that restrictive, by the way.

6. Would you consider making the com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.CompositeComponentTagHandler(Resource, ComponentConfig) constructor public (or at least protected)? This would make my code much less hacky.

I hope you'll find time to view this topic and give me a couple of clues.

Thanks in advance,

Aleksei Valikov

lexi
Offline
Joined: 2004-04-09
Points: 0

Since I already was that far, I've decided to hack it via reflection. Here's the code I won't show my employer:

[pre] public CompositeComponentTagHandler(Resource ccResource,
ComponentConfig config) {
super(config);
this.ccResource = ccResource;
this.binding = config.getTag().getAttributes().get("binding");
final TagHandlerDelegate tagHandlerDelegate = this
.getTagHandlerDelegate();

// HACK
try {
ClassLoader cl = ComponentTagHandlerDelegateImpl.class
.getClassLoader();
Class createComponentDelegateClass = cl
.loadClass("com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl$CreateComponentDelegate");

Object instance = Proxy.newProxyInstance(cl,
new Class[] { createComponentDelegateClass },
new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// TODO Auto-generated method stub
return CompositeComponentTagHandler.this
.createComponent((FaceletContext) args[0]);
}
});

Method setCreateCompositeComponentDelegate = tagHandlerDelegate
.getClass().getDeclaredMethod(
"setCreateCompositeComponentDelegate",
createComponentDelegateClass);
setCreateCompositeComponentDelegate.setAccessible(true);
setCreateCompositeComponentDelegate.invoke(tagHandlerDelegate,
instance);

} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
[/pre]

What I basically did here is:
* Create an implementation of CreateComponentDelegate via reflection as proxy.
* Provide this implementation to the tagHandlerDelegate via reflection (package-protected method).

Apart from that I also had to make the tagHandlerDelegate accessible from the outside so I overridden protected method as public:

[pre]public TagHandlerDelegate getTagHandlerDelegate() {
return super.getTagHandlerDelegate();
}[/pre]

But compared to the reflected creation of invisible package-protected interface it's not even a hack.

lexi
Offline
Joined: 2004-04-09
Points: 0

The first problem is that com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler is a public class with a "friendly" constructor. This effectively means that it can't be subclassed since this constructor is never visible to me (outside the com.sun.faces.facelets.tag.jsf package).

I thought allright, let's copy-paste. I copied the code and made the constructor public.
Next problem was that the original CompositeComponentTagHandler implements the CreateComponentDelegate interface and passes it to the tagHandlerDelegate:

((ComponentTagHandlerDelegateImpl)this.getTagHandlerDelegate()).setCreateCompositeComponentDelegate(this);

The problem is that CreateComponentDelegate is a "friendly" interface. So I can't pass an instance of "my" copy-pasted CompositeComponentTagHandler to the TagHandlerDelegate since:
* I can't implement this interface, it's not visible to me.
* The target setCreateCompositeComponentDelegate is also "friendly", so I can't call it.

lexi
Offline
Joined: 2004-04-09
Points: 0

Then I though it would be nice to have some kind of CompositeComponentTagHandler where I could implement/override setAttributes or populateComponent methods to assign my values to the component, or to add facets or children programmatically. Here's what I wrote:

[pre]
public void setComponent5(UIComponent component) {
Application application = facesContext.getApplication();
// Create a resource
Resource resource = application.getResourceHandler().createResource(
"test.xhtml", "components/dynamicfaces");

// Create a "dummy" tag config
ComponentConfig tagConfig = new FakeComponentConfig(
"http://java.sun.com/jsf/composite/components/dynamicfaces",
"test", "test", UINamingContainer.COMPONENT_TYPE, null, "tag");

// Create a tag handler for the resource
CompositeComponentTagHandler tagHandler = new CompositeComponentTagHandler(
resource, tagConfig) {
@Override
public void setAttributes(FaceletContext ctx, UIComponent c) {
// Set value expressions to the component
final Application application = ctx.getFacesContext()
.getApplication();

c.setValueExpression("value", application
.getExpressionFactory().createValueExpression(
facesContext.getELContext(),
"#{stringValue.value}", String.class));

}

@Override
public void populateComponent(FaceletContext ctx, UIComponent c)
throws IOException, FacesException, ELException {
// Populate the component with facets and children
final Application application = ctx.getFacesContext()
.getApplication();

UIOutput foo = (UIOutput) application
.createComponent(HtmlOutputText.COMPONENT_TYPE);

foo.setValue("Foo");
c.getFacets().put("foo", foo);

UIOutput bar = (UIOutput) application
.createComponent(HtmlOutputText.COMPONENT_TYPE);
bar.setValue("Bar");
c.getChildren().add(bar);
}

};

try {
final FaceletContext faceletContext = (FaceletContext) facesContext
.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
tagHandler.getTagHandlerDelegate().apply(faceletContext, component);
} catch (IOException ioex) {
throw new FacesException(ioex);
}

}
[/pre]

This is already much much better. Get resource - create a tag handler for it - override component configuration methods - apply to the parent component. It's allright.

All I actually need is to be able to subclass the CompositeComponentTagHandler class. However implementing this idea was something like "mission impossible".

lexi
Offline
Joined: 2004-04-09
Points: 0

So I thought that instead of emulating CompositeComponentTagHandler I could call CompositeComponentTagHandler and let it do its job. To do this I had to get (or create, whatever) the composite library and construct a ComponentConfig programmatically (which isn't a big problem). Here's what I came up with:

[pre]public void setComponent4(UIComponent component) {
final CompositeComponentTagLibrary library = new CompositeComponentTagLibrary(
"http://java.sun.com/jsf/composite/components/dynamicfaces");
try {
TagHandler tagHandler = library
.createTagHandler(
"http://java.sun.com/jsf/composite/components/dynamicfaces",
"test",
new FakeComponentConfig(
"http://java.sun.com/jsf/composite/components/dynamicfaces",
"test", "d:test",
UINamingContainer.COMPONENT_TYPE, null,
new TagAttribute[] {
new TagAttributeImpl(new Location(
"", -1, -1), "", "id",
"id", "tag"),
new TagAttributeImpl(new Location(
"", -1, -1), "", "value",
"value",
"#{stringValue.value}")

}));
FaceletContext faceletContext = (FaceletContext) facesContext
.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);

tagHandler.apply(faceletContext, component);

} catch (FaceletException fex) {
throw new FacesException(fex);
} catch (IOException ioex) {
throw new FacesException(ioex);
}
}[/pre]

(I don't post the implementation of the FakeComponentConfig, it's quite straightforward).

So, this looks already much better since it does not mess with Facelets internals so much. Note that I had to construct an attribute for the value=#{stringValue.value} binding.

Nevertheless, the big problem with this code is that I can't add facets and children. It is OK to create a couple of attributes for known expressions (like value="#{stringValue.value}"), but it is simply not possible to construct a ComponentConfig for components which I'd like to add as children or facets.

lexi
Offline
Joined: 2004-04-09
Points: 0

After that I've read the following topics:

http://forums.java.net/jive/thread.jspa?threadID=63877
http://forums.java.net/jive/thread.jspa?messageID=384548

The solution sketched out there basically repeats the functionality of the CompositeComponentTagHandler. The user janderssn wrote that he's got the basic solution, but things like composite:insertChildren and composite:renderFacet don't work. The reason why these things did not work was that the composite component must have been pushed and poped from/to EL. I have reworked the code a bit an now here's the working version (with facets and children):

[pre]
public void setComponent3(UIComponent component) {

Application application = facesContext.getApplication();
Resource resource = application.getResourceHandler().createResource(
"test.xhtml", "components/dynamicfaces");

// Create the composite component
// See CompositeComponentTagHandler#createComponent
UIComponent compositeComponent = facesContext.getApplication()
.createComponent(facesContext, resource);

compositeComponent.pushComponentToEL(facesContext, compositeComponent);
boolean compcompPushed = false;
CompositeComponentStackManager ccStackManager = CompositeComponentStackManager
.getManager(facesContext);
compcompPushed = ccStackManager.push(compositeComponent, TreeCreation);

// Important: set the id - otherwise newly created components get
// different ids
// and "old" request values are not decoded.
compositeComponent.setId("composite");

// Populate the component with value expressions
compositeComponent.setValueExpression("value", application
.getExpressionFactory().createValueExpression(
facesContext.getELContext(), "#{stringValue.value}",
String.class));

// super.applyNextHandler

// Populate the component with facets and child components
UIOutput foo = (UIOutput) application
.createComponent(HtmlOutputText.COMPONENT_TYPE);

foo.setValue("Foo");
compositeComponent.getFacets().put("foo", foo);

UIOutput bar = (UIOutput) application
.createComponent(HtmlOutputText.COMPONENT_TYPE);
bar.setValue("Bar");
compositeComponent.getChildren().add(bar);

// applyCompositeComponent

FaceletFactory factory = (FaceletFactory) RequestStateManager.get(
facesContext, RequestStateManager.FACELET_FACTORY);

final UIComponent compositeRoot = application
.createComponent(UIPanel.COMPONENT_TYPE);
compositeRoot.setRendererType("javax.faces.Group");

// See CompositeComponentTagHandler#applyCompositeComponent(...)

try {
Facelet f = factory.getFacelet(resource.getURL());
f.apply(facesContext, compositeRoot);
compositeComponent.getFacets().put(
UIComponent.COMPOSITE_FACET_NAME, compositeRoot);
} catch (IOException ioex) {
throw new FaceletException(ioex);
}

// Add the created composite component to the tree
component.getChildren().add(compositeComponent);
compositeComponent.popComponentFromEL(facesContext);
if (compcompPushed) {
ccStackManager.pop(TreeCreation);
}
// Black magic ends here

// Not important: finally assign the component to the inner field
this.component = component;
}
[/pre]

As I said above, this code [b]does[/b] work as expected. Figuring it out was like alchemy - trying different things in different orders and so on.

My problem with this code is that it is very very complicated. This is not what client code should be. It's just black magic, nothing else.

lexi
Offline
Joined: 2004-04-09
Points: 0

Assume we have a fairly simple composite component:
[pre]
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">






[]
()


[/pre]

Using it from the Facelets page is straightforward:
[pre]






[/pre]

I'd like to create this composite component programmatically - ad I'd like to set the [b]value[/b] as well as the [b]facet foo[/b] and [b]children components[/b] programmatically as well.

Here's the first thing I tried:

[pre]
public void setComponent1(UIComponent component) throws Exception {

Application application = facesContext.getApplication();
Resource resource = application.getResourceHandler().createResource(
"test.xhtml", "components/dynamicfaces");

UIComponent compositeComponent = application.createComponent(
facesContext, resource);
component.getChildren().add(compositeComponent);

ValueExpression value = application.getExpressionFactory()
.createValueExpression(facesContext.getELContext(),
"#{stringValue.value}", String.class);

compositeComponent.setValueExpression("value", value);

UIOutput foo = (UIOutput) application
.createComponent(HtmlOutputText.COMPONENT_TYPE);

foo.setValue("Foo");
compositeComponent.getFacets().put("foo", foo);

UIOutput bar = (UIOutput) application
.createComponent(HtmlOutputText.COMPONENT_TYPE);
bar.setValue("Bar");
compositeComponent.getChildren().add(bar);

this.component = component;
}
[/pre]

This is the most straightforward thing, this is what I expect to work and this is what I think is designed in the specification. It's also what Ed Burns mentions here:

http://forums.java.net/jive/thread.jspa?messageID=339830

Very naive.

It fails with an exception with the following cause:

Unable to find composite component root for composite component with id null and class javax.faces.component.UINamingContainer

To avoid the exception I've added:

[pre]
UIComponent compositeRoot = application
.createComponent(UIPanel.COMPONENT_TYPE);
compositeRoot.setRendererType("javax.faces.Group");

compositeComponent.getFacets().put(UIComponent.COMPOSITE_FACET_NAME,
compositeRoot);
[/pre]

The exception was gone, but the component was still empty.

Infragile
Offline
Joined: 2011-03-20
Points: 0

Dear Lexi,

I think its very important to be able to manipulate Composite Components programmaticaly. Without this Composite Components are half useless.
I used your solution setComponent3()(Thank you very much) But it doesn't work when javax.faces.PROJECT_STAGE is set to DEVELOPMENT. (It took me all day to figure this out)

It throws:

javax.faces.view.facelets.TagException: /resources/components/form/pokus.xhtml @8,19 <cc:interface> Component Not Found for identifier: j_id2.getParent().
<span class="Apple-tab-span"> </span>at com.sun.faces.facelets.tag.composite.InterfaceHandler.validateComponent(InterfaceHandler.java:135)
<span class="Apple-tab-span"> </span>at com.sun.faces.facelets.tag.composite.InterfaceHandler.apply(InterfaceHandler.java:125)
<span class="Apple-tab-span"> </span>at javax.faces.view.facelets.CompositeFaceletHandler.apply(CompositeFaceletHandler.java:98)
<span class="Apple-tab-span"> </span>at com.sun.faces.facelets.compiler.NamespaceHandler.apply(NamespaceHandler.java:93)
<span class="Apple-tab-span"> </span>at com.sun.faces.facelets.compiler.EncodingHandler.apply(EncodingHandler.java:82)
<span class="Apple-tab-span"> </span>at com.sun.faces.facelets.impl.DefaultFacelet.apply(DefaultFacelet.java:152)
<span class="Apple-tab-span"> </span>at cz.boza.formcreator.formcore.Try.setComponentJ(Try.java:83)
<span class="Apple-tab-span"> </span>at cz.boza.formcreator.formcore.FormCreator.&lt;init&gt;(FormCreator.java:40)
<span class="Apple-tab-span"> </span>at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
<span class="Apple-tab-span"> </span>at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
<span class="Apple-tab-span"> </span>at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)

Its thrown at this point:
...<div>Facelet f = factory.getFacelet(componentResource.getURL());</div><div> </div><div>f.apply(context, compositeRoot); &lt;==</div><div> </div><div>...</div><div> </div><div> </div><div>If I set javax.faces.PROJECT_STAGE to PRODUCTION Exception is gone and component is successfully rendered.</div><div>Don't know what to do. It really should be much easier.</div><div>Any help is appreciated.</div><div>Thanks.</div><div> </div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div> </div></meta>
geuze
Offline
Joined: 2011-03-15
Points: 0

Dear Lexi,
I read and used the information in your post. It helped me a lot. However I have a few questions.
- Did the setComponent1 solution work in the end?
- I used the setComponent 3 solution for now, this works in that sense that my composite component is displayed in the browser. However the events which are part of the composite component do not work. Do you also have this problem? If so, how did you solve it?
I really hope that you can help.
Greets,
Marinus

rogerk
Offline
Joined: 2004-05-06
Points: 0

Please file a JIRA issue (javaserverfaces project) if you are still experiencing difficulty.

-roger