Accessing JSF2 composite component attributes in backing component - jsf-2

I'm developing a JSF2/Primefaces app and I'm having trouble with accessing attributes defined in an interface of a composite component in this component's backing bean.
My component is defined as follows:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface componentType="testComponent">
<composite:attribute name="text" required="true" type="java.lang.String" />
</composite:interface>
<composite:implementation>
<h:outputText value="Passed text is: #{cc.attrs.text}" />
</composite:implementation>
</html>
It is stored in a file called text.xhtml located in: application/src/main/webapp/resources/my_component directory.
I use this component on another page (which is a Facelets composition element) like so:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:myc="http://java.sun.com/jsf/composite/my_component"
template="./resources/templates/template.xhtml">
<ui:define name="content">
<h:form id="products">
<myc:test id="test" text="A text" />
</h:form>
</ui:define>
</ui:composition>
The backing component class is defined as follows:
package my.application.component;
import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#FacesComponent ( value="testComponent" )
public class TestComponent extends UINamingContainer {
Logger log = LoggerFactory.getLogger(TestComponent.class);
public TestComponent() {
log.debug("TestComponent constructor");
log.debug("getAttributes().size(): " + getAttributes().size());
}
}
The component itself works as expected. The using page renders Passed text is: A text output.
Also, logger output displays log messages from TestComponent constructor, so it seems that the component xml definition is correctly bound with the TestComponent class.
Now, the problem is that the getAttributes() method invoked in TestComponent constructor always returns a zero-sized map.
If I understand this correctly, I should be able to access the text attribute declared in component's interface using a call:
getAttributes().get("text");
in the TestComponent class, but it always returns null as the attributes map is empty.
I also tried to access the text attribute using a call:
String text = FacesContext.getCurrentInstance().getApplication().
evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{cc.attrs.text}", String.class));
but it also resolves to null.
What am I doing wrong? Any tips will be much appreciated as I have no idea what to try next.
/Tukasz.

My guess is that the constructor is too early to reference those attributes.
JSF will construct an instance of the backing component first, and will then at some point give it a reference to its attributes. In a method that's called later, eg the encode method, you should have access to them.

Related

How to keep JSF flash scope parameters on page reload?

I use flash scope to pass a setting object between #viewscoped contollers. But if I make a page reload on one of them, then the flash map is empty and the setting object is not initialized. Is it possible to keep flash scope on page reload?
My source code to store/retrieve settings:
FistPage.xhtml
...
<p:commandButton value="next"
action="#{firstPageController.transferConfig}"
process="#this" />
...
FirstPageController.java
#ManagedBean(name = "firstPageController")
#ViewScoped
public class FirstPageController {
...
public String transferConfig() {
FacesContext.getCurrentInstance().getExternalContext().getFlash().put("searchConfig", searchConfig);
return "/secondPage.xhtml?faces-redirect=true";
}
...
}
SecondPage.xhtml
...
<h:outputLabel value="value">
<f:event type="preRenderComponent" listener="#{secondPageController.onPageLoad()}"/>
</h:outputLabel>
...
SecondPageController.java
#ManagedBean(name = "secondPageController")
#ViewScoped
public class SecondPageController {
...
public void onPageLoad()
{
flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
searchConfig = ((SearchFilterConfig) flash.get("searchConfig"));
flash.putNow("searchConfig", searchConfig);
flash.keep("searchConfig");
}
...
}
I use Mojarra 2.1.29
Thanks
I just did some tests in my playground project and realized it's actually possible to keep the state of the flash parameters even if you GET the page again, using {flash.keep}. That's how the JSF docs explain it:
The implementation must ensure the proper behaviour of the flash is preserved even in the case of a <navigation-case> that contains a <redirect />. The implementation must ensure the proper behavior of the flash is preserved even in the case of adjacent GET requests on the same session. This allows Faces applications to fully utilize the Post/Redirect/Get design pattern.
Here you've got a nice basic test case:
page1.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head />
<h:body>
<h:form>
<h:button id="nextButton" value="Next (button)" outcome="page2.xhtml" />
<c:set target="#{flash}" property="foo" value="bar" />
</h:form>
</h:body>
</html>
page2.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<head />
<body>foo = #{flash.keep.foo}
</body>
</html>
Just open the first page and click on the button which will redirect you to the second one. Then refresh the second page as many times as you want and you'll find the parameter persisting.
Tested in Mojarra 2.2.6

PreRenderView incrementally called on every postback

I have an issue with the order and number of executions of an f:event type="preRenderView".
During my search here I found as usual answers from BalusC in this and this post related to my problem - still it leaves two questions for me:
When I put one f:event type="preRenderView" in the template file (for managing common tasks like checks about the user state which applies for all my views) and another f:event type="preRenderView" in each view (for handling view specific initializations), I wonder why the listener method from the view is called before the one from the template.
When I put the whole <f:metadata><f:event [..] /></f:metadata> after ui:define as suggested, it gets called twice after redirecting to that page from the login page, but when I put it one level higher after ui:composition it only gets called once.
Update: Example
The following example demonstrates the behaviour above:
This is the template file template_test.xhtml, containing a listener for preRenderViewevent calling a common method in a handler for all views:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xml:lang="de" lang="de" xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<link rel="stylesheet" type="text/css" href="../resources/css/style.css" />
</h:head>
<h:body>
<f:event type="preRenderView" listener="#{testHandler.initCommon()}" />
<div id="content">
<ui:insert name="content" />
</div>
</h:body>
</html>
This is the view file test.xhtml, containing also a listener for preRenderViewevent calling a view specific method in a handler and a command button redirecting via a handler method:
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="template_test.xhtml">
<ui:define name="content">
<f:metadata>
<f:event type="preRenderView"
listener="#{testHandler.initIndividual()}"></f:event>
</f:metadata>
<h:form>
<h:commandButton value="Redirect" action="#{testHandler.redirect()}" />
</h:form>
</ui:define>
</ui:composition>
This is the handler class TestHandler.java containing the 3 methods:
package test;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class TestHandler implements Serializable {
private static final long serialVersionUID = -2785693292020668741L;
public void initCommon() {
System.out.println("Common init called.");
}
public void initIndividual() {
System.out.println("Init for individual page called.");
}
public String redirect() {
return "test/test.xhtml?faces-redirect=true";
}
}
Now, this is what I see in my tomcat log when requesting the test page:
Init for individual page called.
Common init called.
Init for individual page called.
This shows no. 1, that the event handler from the view is called before the one from the template and no. 2, that the event handler from the view is called twice.
It also shows a 3rd point (that's why I included a button with a redirect to the same page) showing what happens if the page gets requested by redirect - the individual page gets called even more times:
Init for individual page called.
Common init called.
Init for individual page called.
Init for individual page called.
Both no. 2 and 3 can be prevented by either putting the whole metadata section above the ui:define or by adding a dummy parameter to the view's metadata section which is not included in the URL:
<f:metadata>
<f:viewParam name="dummyToDenySecondRedirect" />
<f:event type="preRenderView"
listener="#{testHandler.initIndividual()}"></f:event>
</f:metadata>
Can someone tell me the reason for those cases?
I can reproduce it. This is caused by the presence of /WEB-INF/beans.xml and implicitly thus CDI. It even occurs when you switch back to standard JSF annotations while keeping the beans.xml file. This is already reported as both issue 1771 and issue 2162. However, due to absence of a concrete WAR file reproducing the issue and low votes, the Mojarra developers didn't bother to look closer at it.
I have reported it once again as issue 2719. The problem can be reproduced with a smaller example:
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<f:metadata>
<f:event type="preRenderView" listener="#{bean.preRenderView}" />
</f:metadata>
<h:head>
<title>preRenderView fail</title>
</h:head>
<h:body>
<p>On initial request, you'll see the listener being invoked twice instead of only once.</p>
<p>On every postback by the below button, you'll see the listener being invoked once more.</p>
<h:form>
<h:commandButton value="submit" />
</h:form>
<p>Note however that this increments every time you issue the postback.</p>
<p>If you remove <code>/WEB-INF/beans.xml</code> and redeploy, then the issue will disappear.</p>
</h:body>
</html>
and
package com.example;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
#ManagedBean
#RequestScoped
public class Bean {
public void preRenderView() {
System.out.println("preRenderView called");
}
}
A missing resource may also cause the listener to be called twice. I'm using MyFaces 2.2 here:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<f:metadata>
<f:event type="preRenderView" listener="#{testController.update()}" />
</f:metadata>
<h:head>
<script type="text/javascript" src="#{resource['/js/missing.js']}" />
<title>Test</title>
</h:head>
<h:body>
<p>TestController.update() is called twice.</p>
</h:body>
</html>
Obviously, you shouldn't include any missing or obsolete resources in the first place. But let's say you have (by mistake) and your listener gets called twice on and on, you have no chance to find the actual cause of that problem. JSF shouldn't do this.

JSF, Composite Component: method call with default attribute value as parameter

I am new to JSF and still learning. I tried searching for a solution to my specific problem described below but I could not find anything. If it because I was searching for the wrong things, please point me in the right direction, but hopefully it is something that hasn't been answered and an answer can benefit everyone.
The following example illustrates the problem I came across. The example is simplified to focus on the problem and to hide the complexities of the actual project in which the problem occurred.
Consider the following pages / classes:
/resources/test/custom.xhtml;
/test/CharsetProvider.java;
/test/CharsetHello.java;
/testWith.xhtml;
/testWithout.xhtml;
/resources/test/custom.xhtml
This is composite component with one attribute with a default value. The component simply takes the attribute value and passes it as an argument to the CDI bean described below in order obtain the model object used for output.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html">
<cc:interface>
<cc:attribute name="charset"
default="#{charsetProvider.defaultCharset}"
type="java.nio.charset.Charset" />
</cc:interface>
<cc:implementation>
<h:outputText value="#{charsetProvider.createCharsetHello(cc.attrs.charset).hello}"/>
</cc:implementation>
</html>
test/CharsetProvider.java
This is a CDI bean that simply contains a default value used throughout the application and has a method that creates an object used as the model for a component. The reason I use a CDI bean instead of a backing bean is because in my specific project the default value needs to be injected at runtime, but backing beans are not candidates for injection.
package test;
import java.nio.charset.Charset;
import javax.annotation.PostConstruct;
import javax.faces.bean.SessionScoped;
import javax.inject.Named;
#Named
#SessionScoped
public class CharsetProvider {
private Charset defaultCharset;
#PostConstruct
protected void postConstruct() {
this.defaultCharset = Charset.forName("UTF-8");
}
public Charset getDefaultCharset() {
return defaultCharset;
}
public Charset getCharsetForName(String name) {
return Charset.forName(name);
}
public CharsetHello createCharsetHello(Charset cs) {
return new CharsetHello(cs);
}
}
test/CharsetHello.java
This is the "model" object. It simply converts "Hello world!" to a byte array and back using the given charset.
package test;
import java.nio.charset.Charset;
public class CharsetHello {
private static final String HW = "Hello World!";
private final byte[] data;
private final Charset cs;
public CharsetHello(Charset cs) {
this.cs = cs;
this.data = CharsetHello.HW.getBytes(this.cs);
}
public String getHello() {
return new String(this.data, this.cs);
}
}
testWith.xhtml
This is a test page that uses the composite component defined above by specifying a value for the component's attribute. The page renders properly, i.e. "Hello World!" prints on the screen.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:test="http://java.sun.com/jsf/composite/test">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<test:custom charset="#{charsetProvider.getCharsetForName('UTF-16')}" />
</h:body>
</html>
testWithout.xhtml
This is a test page that does not pass a custom value to the component's attribute, intending to use the default value.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:test="http://java.sun.com/jsf/composite/test">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<test:custom />
</h:body>
</html>
The page above results in a JSF error page with the following message:
/resources/test/custom.xhtml #14,94 value="#{charsetProvider.createCharsetHello(cc.attrs.charset).hello}": Cannot convert UTF-8 of type class java.lang.String to class java.nio.charset.Charset
It seems that in the last case the default value is converted to a java.lang.String before being passed to the method.
First off, is this the expected behaviour and why?
If this is the expected behaviour, can you suggest a different implementation?
Thank you in advance!
This problem has exactly the same ground as this problem: FacesConverter forClass don't work with Composite Componet. The composite attribute value type is in Mojarra incorrectly been evaluated as java.lang.Object instead of the actual model type.
It's been reported as Mojarra issue 2568. It works in MyFaces 2.1.9.

Obtaining the clientId of the parent of a JSF2 composite component

I've got the following code:
<h:dataTable id="dt" value="#{somelist}" var="entry">
<h:column>
#{entry.title}
</h:column>
<h:column>
<h:commandLink id="lnk">
<mycomp:doSomething id="dummy" />
</h:commandLink>
</h:column>
</h:dataTable>
My composite component (mycomp:doSomething) looks like this:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
</composite:interface>
<composite:implementation >
<script type="text/javascript">
// #{component.parent.clientId}
</script>
</composite:implementation>
</html>
I would expect the output (#{component.parent.clientId}) to be something similar to this: dt:0:lnk but instead it returns dt:0:dummy i.e. the client ID of the composite component.
How do I get the ID of the real parent tag?
Use #{cc.parent.clientId} instead. All content inside composite:implementation is inside a UIPanel that is on a facelet inside the composite component base instance, which usually is a NamingContainer.
UPDATE: Checking the code, cc.parent resolves the parent composite component instead of the immediate parent. It seems to be an old implementation detail, but it is not mentioned in the spec. The solution proposed in this answer does not work :(.
See http://lists.jboss.org/pipermail/jsr-314-open-mirror/2010-February/002474.html
You can bypass the resolution of cc.parent, providing a custom component class extending UINamingContainer and adding this:
<composite:interface componentType="my.custom.ComponentBaseClass">
then add a getter like
public UIComponent getImmediateParent()
{
return getParent();
}
and finally use #{cc.immediateParent.clientId}. It should work in this way.

Can't use composite component on same page with a ui:include that relies on ui:params

I have a base page that uses a ui:include to include another page. The ui:include uses a ui:param to pass a parameter to the included page. The param is read in the PostConstruct method of the included page's controller.
This design works fine until I add a composite component to the base page; once added, the included page controller reports that the ui:param's value is null. Once I remove the composite component from the page, the ui:param is able to be read (i.e. is not null) by the included page's controller. Please help!
Files to reproduce the problem:
myPage.xhtml
<html xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:mycomponents="http://java.sun.com/jsf/composite/mycomponents">
<ui:include src="includePage.xhtml">
<ui:param name="myParam" value="myValue"/>
</ui:include>
<p/>
<mycomponents:myComponent/>
</html>
myIncludedPage.xhtml
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets">
The value of the param is: #{includedPageController.myParam}
</ui:composition>
myComponent.xhtml (placed in "resources/mycomponents")
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface/>
<composite:implementation>
I am the composite component
</composite:implementation>
</html>
IncludedPageController.java
#ManagedBean
public class IncludePageController
{
String myParam;
#PostConstruct
private void init()
{
FaceletContext faceletContext = (FaceletContext)FacesContext.getCurrentInstance().
getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
myParam = (String)faceletContext.getAttribute("myParam");
}
public String getMyParam()
{
return myParam;
}
public void setMyParam(String myParam)
{
this.myParam = myParam;
}
}
See: How do I set Managed Bean field to the value of a ui:param?. There is no standard for doing this sort of thing in the JSF API. A composite or custom component should be used rather than an include/param tag combination.

Resources