Custom tag attributes are leaking into children - jsf

Our application is using Mojarra 2.1.29-03 and we are having a problem with attributes in our custom tags as they are being copied to nested tags as well.
For example, given the following tag definition:
cc.taglib.xml
<?xml version="1.0" encoding="UTF-8" ?>
<facelet-taglib version="2.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">
<namespace>http://temp.com/jsf/customcomponents</namespace>
<tag>
<tag-name>wrapper</tag-name>
<source>wrapper.xhtml</source>
<attribute>
<description>The style class for the component</description>
<name>styleClass</name>
</attribute>
</tag>
</facelet-taglib>
wrapper.xhtml
<?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">
<ui:component xmlns:ui="http://java.sun.com/jsf/facelets">
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ui:insert />
</div>
</ui:component>
</html>
And a client page:
<?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:cc="http://temp.com/jsf/customcomponents">
<h:body>
<cc:wrapper styleClass="cc-style">
<cc:wrapper />
</cc:wrapper>
</h:body>
</html>
The result is as follows:
styleClass: cc-style
styleClass: cc-style
... so the styleClass attribute is also being applied to the inner tag even though the client page does not set it.
We have noted that we can workaround this by processing all our client pages to set styleClass="" if it is not explicitly set but this is an approach we would like to avoid (it's a very ugly solution and cannot be enforced going forward).
Is this a bug? Is there any way to work around it other than that mentioned above - preferably with the workaround in the tag rather than the client pages?
Thanks
Ivor

This is not strictly a bug, but this is indeed unintuitive and undesired behavior which should have been addressed in the JSF/Facelets spec.
The work around solution is not trivial, a custom taghandler is needed to clear out the Facelet scope. JSF utility library OmniFaces has since version 2.1 exactly such one: <o:tagAttribute> (source code here).
Usage (do note that prolog, doctype and html tags are unnecessary, this is the file in its entirety):
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
>
<o:tagAttribute name="styleClass" />
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ui:insert />
</div>
</ui:composition>
Unrelated to the concrete problem, this is not a custom component, but a tagfile. And, in JSF 2.x, the term "cc" is usually associated with composite components, which is a completely different subject altogether. To get your knowledge and terminology right (so that you don't confuse yourself or others while reading your or other's code), head to When to use <ui:include>, tag files, composite components and/or custom components?

For info we identifed an alternative approach that might be of use to others as it is slightly easier to implement and understand.
First I need to add a bit of context to the situation that we found ourselves in ... we had recently migrated from 2.1.12 to 2.1.29-03 and as part of that process we changed one of our building blocks from a composite component (which has isolated scope for its attributes) to a custom tag (which has the behaviour described above). The same behaviour regarding custom tag attributes existed in 2.1.12 but we were unaware of it so we considered the following approach instead as it would restore the behaviour we had prior to migration because we would only need to apply it to the component that we changed.
wrapper.xhtml (I've changed the namespace to 'ct' rather than 'cc')
<ui:component xmlns:ct="http://temp.com/jsf/customtags" xmlns:ui="http://java.sun.com/jsf/facelets">
<ct:tagAttribute name="styleClass" />
<div style="border: 1px solid black; margin: 5px;">
<p>styleClass: #{styleClass}</p>
<ct:eraseAttribute name="styleClass" />
<ui:insert />
</div>
</ui:component>
Where tagAttribute is the solution suggested by BalusC to prevent the tag inheriting attributes from its parents and in addition we call eraseAttribute tag before calling ui:insert to remove the attribute from scope (this obviously requires that the custom tag is finished with the attribute). This means that this one tag neither inherits nor leaks attributes and other tags could remain unchanged and maintain the same behaviour they had prior to migration.
ct.taglib.xhtml (snippet)
<tag>
<tag-name>eraseAttribute</tag-name>
<handler-class>com.temp.jsf.customtags.EraseTagAttribute</handler-class>
<attribute>
<name>name</name>
<required>true</required>
<type>java.lang.String</type>
</attribute>
</tag>
EraseTagAttribute.java
package com.temp.jsf.customtags;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagConfig;
import javax.faces.view.facelets.TagHandler;
public class EraseTagAttribute extends TagHandler {
private final String name;
public EraseTagAttribute(TagConfig config) {
super(config);
name = getRequiredAttribute("name").getValue();
}
public void apply(FaceletContext context, UIComponent parent) throws IOException {
context.getVariableMapper().setVariable(name, null);
}
}
Ultimately we did not use it however as we felt the answer provided by BalusC (which we applied to every attribute in all our custom tags) was the correct and cleaner approach even though it might have additional consequences in our very specific situation.

Related

Does the component-type name needs to be unique for custom components?

I am new to Custom components topic starting with JSF 2.2
However, I am first considering the case with JSF2.0
Consider the snippet:
#FacesComponent(value = "components.WelcomeComponent1", createTag = true)
public class WelcomeComponent extends UIComponentBase {
}
Do notice value = "components.WelcomeComponent1"
referencing it in the UI wireframe-
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:t="http://xmlns.jcp.org/jsf/component">
<h:head>
<title></title>
</h:head>
<h:body>
<t:welcomeComponent value="Welcome" to="Rafael Nadal"/>
</h:body>
</html>
Consider the second case with the same name of the class as in first case but in a different package with a different component-type name:
#FacesComponent("components.WelcomeComponent")
public class WelcomeComponent extends UIComponentBase {
// code goes here
}
Do notice the "components.WelcomeComponent"
taglib.xml in WEB_INF
<namespace>http://atp.welcome.org/welcome</namespace>
<tag>
<tag-name>welcome</tag-name>
<component>
<component-type>components.WelcomeComponent</component-type>
</component>
<attribute>
<name>id</name>
<type>java.lang.String</type>
<description>Component id</description>
<required>false</required>
</attribute>
</tag>
</facelet-taglib>
and referencing the latter -
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:t="http://atp.welcome.org/welcome">
<h:head>
<title></title>
</h:head>
<h:body>
<t:welcome value="Welcome" to=""/>
</h:body>
</html>
Both of the above examples are part of the same web app & work fine.
However if I change the component-type name in the second case to make it the same as in first case,
#FacesComponent("components.WelcomeComponent1")
I do get-
Warning: This page calls for XML namespace
http://xmlns.jcp.org/jsf/component declared with prefix t but no
taglibrary exists for that namespace
Clearly the entry/tag declared in taglib.xml is preferred.
So, the component-type name needs to be unique in each case. Right?
I know it very well that the namespace http://xmlns.jcp.org/jsf/component was introduced with JSF2.2.
Short answer is yes, it should be unique. JSF thinks of it as a name for your custom component (in the similar way as a #ManagedBean). Basically, component-type allows Application instance to create new instances of UIComponent subclasses (your and every other custom component) by using defined component-type and createComponent() method.

Saving and reusing tag definition in PrimeFaces

Let's say I have the following construct polluting the simplicity of my JSF code in many places:
<p:calendar id="decisionDate"
effect="explode"
yearRange="2000:2100"
pattern="MM/dd/yyyy"
navigator="true" display="inline"
value=""
label="Decision Date"
maxlength="10"
size="20">
<f:convertDateTime pattern="MM/dd/yyyy" />
</p:calendar>
As can be seen, this has nine (9) attributes and a nested tag. This is an awful amount of tedious detail to consume with your eye.
Is there a way I can reuse PrimeFaces tags in a similar way as CSS: to save a complex tag definition as <px:myCalendar/> with the above parameters minus the ID ones, which should be set for each instance of use nonetheless, where px would be my namespace and then each time I need to invoke it, I would just say <px:myCalendar id="uniqueCalID"/> and ... BOOM ... there goes all the repeated clutter?
POST ANSWER EDIT: Check out this tutorial
You can define composite component. It is defined with xhtml+ jsf namespaces and, but in your case it is unnesessary, backing component, which is java class, instantiated for every composite component usage.
Within composite component interface you can define attributes, which vary its behaviour. And in implementation you then can insert needed primefaces' component with some hardcoded attributes and some passed from your composite component invocation.
Consider this tutorial: https://docs.oracle.com/javaee/6/tutorial/doc/giqzr.html
Example
Composite component is resource, so we put it below /resources folder. Let's create subfolder /resources/myCompositeComponents and create xhtml file myCalendar.xhtml there. It will be our composite component. Basically, it is xhtml file with additional namespace xmlns:cc="http://java.sun.com/jsf/composite". Here is the code. You can notice two elements: <cc:interface> and <cc:implementation>. And <cc:attribute> element inside the <cc:interface> is the input of our composite component.
<?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:f="http://xmlns.jcp.org/jsf/core"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html">
<cc:interface>
<cc:attribute name="label" default="Decision Date"/>
</cc:interface>
<cc:implementation>
<h2>#{cc.clientId}</h2>
<h:outputLabel
id="Label"
value="#{cc.attrs.label}"/>
<p:calendar id="Calendar"
effect="explode"
yearRange="2000:2100"
pattern="MM/dd/yyyy"
navigator="true" display="inline"
value=""
label="Label"
maxlength="10"
size="20">
<f:convertDateTime pattern="MM/dd/yyyy" />
</p:calendar>
</cc:implementation>
</html>
Then, let's use it. To be able to declare our brand new component we will put additional namespace into the using page: xmlns:my="http://java.sun.com/jsf/composite/myCompositeComponents". The last part of the namespace uri corresponds to the folder under /resources, where composite component lives. Give it any prefix you like. Here is source code:
<?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:my="http://java.sun.com/jsf/composite/myCompositeComponents">
<h:head>
<title>Simple JSF Facelets page</title>
</h:head>
<h:body>
<my:myCalendar id="LetsUseIt" label="MyLabel"/>
</h:body>
</html>
Have a notice of attribute "label" - that very attribute, that is declared in the "interface" element.
This is quite basic usecase, though it will help in your situation. More complex scenarios include passing typed attributes and implementing backing component - java class, instantiated every time the component is used.

ERROR: tag handler class for "rich:modalPanel" (org.richfaces.taglib.ModalPanelTag) was not found on the Java Build Path

I am trying richface 4. It seems tags class are not setting on JAVA build path. I am getting this error for all rich component: "ERROR: tag handler class for * (org.richfaces.taglib.*) was not found on the Java Build Path"
For a4j component also, for all component i am getting same error "The tag handler class for "a4j:" (org.ajax4jsf.taglib.html.jsp.) was not found on the Java Build Path"
For richface4, i performed following actoin:
1) Added following jars:
annotations-4.0.0.Final.jar
cssparser-0.9.5.jar
guava-r08-gwt.jar
guava-r08.jar
jsf-api.jar
jsf-impl.jar
richfaces-components-api-4.1.0.Final.jar
richfaces-components-ui-4.1.0.Final.jar
richfaces-core-api-4.1.0.Final.jar
richfaces-core-impl-4.1.0.Final.jar
sac-1.3.jar
commons-beanutils-1.8.3.jar
commons-collections-3.2.1.jar
commons-digester-2.1-sources.jar
commons-digester-2.1.jar
commons-discovery-0.4.jar
jhighlight-1.0.jar
jsf-facelets-1.1.14.jar
web.xml is default generated and NO new element is added. As it is not required to change in RF4 (which is required in RF3.3).
JSP file is
<?xml version="1.0" encoding="ISO-8859-1" ?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:a4j="http://richfaces.org/a4j" xmlns:rich="http://richfaces.org/rich" version="2.0">
<jsp:directive.page language="java"
contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" />
<jsp:text>
<![CDATA[ <?xml version="1.0" encoding="ISO-8859-1" ?> ]]>
</jsp:text>
<jsp:text>
<![CDATA[ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> ]]>
</jsp:text>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>TESTING</title>
</head>
<body>
<f:view>
<h:form>
<a4j:commandLink
value="Opss! I forgot password"
reRender="forgetPasswordPanel"
oncomplete="#{rich:component('forgetPasswordPanel')}.show()">
</a4j:commandLink>
</h:form>
<rich:modalPanel id="forgetPasswordPanel" autosized="true" width="380">
<f:facet name="header">
<h:outputText value="Reset Password"/>
</f:facet>
</rich:modalPanel>
</f:view>
</body>
</html>
</jsp:root>
i further investigated this issue and found that taglib(tld file) doesn't contain tag-class for many rich and a4j component and richFace4.1 jar does not have corrosponding java class for them (which is in richFaces 3.3 jars).
Am i missing some jars ? What else should i do to work with richface4 ?
You seem to be trying to upgrade a RichFaces 3.3 web application to RichFaces 4.1. You need to do many more changes than only replacing the JAR files. You can find the exact migration steps in their own documentation: RichFaces 3.3.x to 4.x migration guide.
For example, JSP is been deprecated and replaced by Facelets, you'd need to rewrite your JSPs to be XHTMLs. The <rich:modalPanel> is been replaced by <rich:popupPanel>, you need to find and replace all those tags accordingly. The reRender attribute is been replaced by update attribute. Etcetera. Also that jsf-facelets-1.1.14.jar which is of Facelets 1.x should be removed from the /WEB-INF/lib. JSF 2.x libraries already ships with the right Facelets 2.x implementation bundled.
See also:
Migrating from JSF 1.2 to JSF 2.0

Private/scoped variable in JSF2/Facelets <ui:component>?

I might not be thinking correctly in terms of visual components in JSF, but I guess that's part of my question. My question is around the seeming lack of scope around variables declared within JSF <ui:component> implementations.
So, say I have /resources/comp/myPanel.xhtml:
<?xml version="1.0" encoding="UTF-8" ?>
<ui:component 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"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
</cc:interface>
<cc:implementation>
<f:loadBundle var="bundle" basename="panelOnly.bundle" />
<h:outputText value="#{bundle.myText}" />
</cc:implementation>
</ui:component>
And there is a resource bundle that gets loaded in that component, panelOnly/bundle.properties:
myText = This is a panel resource
And then I have a page that places the myPanel component, mainPage.xhtml:
<?xml version="1.0" encoding="UTF-8" ?>
<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"
xmlns:comp="http://java.sun.com/jsf/composite/comp">
<h:body>
<f:view>
<f:loadBundle basename="mainPage.bundle" var="bundle" />
<comp:myPanel />
<h:outputText value="#{bundle.myText}" />
</f:view>
</h:body>
</html>
and there is a resource bundle that gets loaded in the main page, mainPage/bundle.properties:
myText = This is a main page resource
Now, I would assume that my page should render as:
This is a panel resource
This is a main page resource
But, instead, I get:
This is a panel resource
This is a panel resource
And I assume that is because I clobbered what the "bundle" symbol refers to in my component so that when the mainPage.xhtml tries to resolve that value, it looks to the component's "bundle" object and not the original mainPage's.
My workaround to date has been to just use unique named variables within my components that would never clash with variables on my main pages. But I would prefer if there was a way to coax JSF to recognize anything declared in my component as locally scoped variables and not clobber the caller's symbols.
I think there are and other tags that one can use to make locally scoped variables under #{cc.attrs...}. If you could enumerate my local scoping options in your answer, that would be very helpful. I suspect my <f:loadBundle> is a special case, and maybe there isn't a workaround for that one as it was not designed with <ui:component> in mind.
Thanks!
P.S. I'm running Mojarra 2.1.1 (FCS 20110408)
(edited for formatting and copy and paste bugs 6/15/2011)
Unfortunately, that's how <f:loadBundle> works. It's an one-time setting for the entire view. And any subsequent <f:loadBundle> calls in the same view will just override the previous one.
Your best bet is to manage it by a backing component.
<cc:interface componentType="myPanel">
with
#FacesComponent(value="myPanel")
public class MyPanel extends UIComponentBase implements NamingContainer {
private ResourceBundle bundle;
public MyPanel() {
bundle = ResourceBundle.getBundle("panelOnly.bundle",
FacesContext.getCurrentInstance().getViewRoot().getLocale());
}
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
public ResourceBundle getBundle() {
return bundle;
}
}
which can be used as
<cc:implementation>
<h:outputText value="#{cc.bundle.myText}" />
</cc:implementation>

Facelet Custom Component - prevent rendering

After reading this answer, I am still stumped. I agree that JSTL should be avoided and understand how its evaluation occurs in the wrong phase. However, per the documentation on the facelets development site, it appears that <ui:fragment> tags only supports two attributes, id and binding. So, even if some implementation support rendered, it seems like you would be tempting fate to make use of it. The other suggestion was to use <h:panelGroup>, however, that inserts a <div> element in the response which could cause undesirable side effects (like changing your content from inline to block). Does anyone know a way around this? In particular, I am attempting the following:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:ice="http://www.icesoft.com/icefaces/component"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition>
<ice:selectOneListbox binding="#{binding}" rendered="#{modeExisting}">
<f:selectItems
value="#{binding.allTagsSelectItems}" />
</ice:selectOneListbox>
<ice:inputText binding="#{binding.name}" />
<ice:inputText binding="#{binding.description}" />
</ui:composition>
</html>
Which is basically a listbox used to select an element with a name and description which when selected will allow the user to edit them. I could put an <ice:panelGroup> around the block, and use the rendered attribute of it, but again, there could be side effects of injecting that additional div. Is there any way to make this work?
Also, it may be worth mentioning that I am using the above custom component paired with this facelet-taglib:
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://www.mitre.org/asias/jsf</namespace>
<tag>
<tag-name>configurationTagEditor</tag-name>
<source>../component/configurationTagEditor.xhtml</source>
</tag>
<tag>
<tag-name>configurationTagSelector</tag-name>
<source>../component/configurationTagSelector.xhtml</source>
</tag>
<tag>
<tag-name>configurationTagRegexTable</tag-name>
<source>../component/configurationTagRegexTable.xhtml</source>
</tag>
</facelet-taglib>
To allow me to use this in my jsf xhtml:
...
<ice:panelTab label="Existing" styleClass="configurationTagsExisting">
<m:configurationTagEditor tag="#{configuration.existingTag}" />
</ice:panelTab>
...
The other suggestion was to use <h:panelGroup>, however, that inserts a <div> element in the response
The <h:panelGroup> doesn't render a <div> by default. It only renders that if you add layout="block". For all other HTML attributes (like id, styleClass, etc), it only renders a <span>. If no layout attribute is present and all other HTML attributes are absent, it renders nothing.

Resources