JavaServer Faces 2 introduced Facelets as the default View Declaration Language. Facelets allows to create templates using XHTML and CSS that can be then used to provide a consistent look-and-feel across different pages of an application. JSF 2.2 defines Resource Library Contracts that allow facelet templates to be applied to an entire application in a reusable and interchangeable manner.
This Tip Of The Day (TOTD) will explain how you can leverage them in your web application.
The complete source code for this sample can be downloaded here. This will run on GlassFish build 72 + latest JSF 2.2.0 SNAPSHOT copied over"glassfish3/glassfish/modules/javax.faces.jar" file.
Consider the following WAR file:
index.xhtml user/index.xhtml contracts/blue/layout.css contracts/blue/template.xhtml contracts/red contracts/red/layout.css contracts/red/template.xhtml WEB-INF/faces-config.xml
The application also has two pages - "index.xhtml" and"user/index.xhtml". All contracts reside in the "contracts" directory of the WAR. All templates and resources for a contract are in their own directory. For example, the structure above has two defined contracts "blue" and "red". Each contract has a"template.xhtml" and a CSS. Each template is called as "declared template". The "template.xhtml" has <ui:insert> tags called as"declared insertion points". CSS and other resources bundled in the directory are "declared resources". The "declared template","declared insertion points", and "declared resources" together make the definition of the resource library contract. A template client needs to know the value of all three in order to use the contract.
In our case, templates have similar "ui:insert" sections and template clients will accordingly have "ui:define" sections. The difference will primarily be in the CSS. "index.xhtml" will refer to the template as:
<ui:composition template="/template.xhtml">
<ui:define name="content">
. . .
</ui:define>
</ui:composition>
The usage of the contracts is defined in "faces-config.xml" as:
<application>A contract is applied based upon the URL pattern invoked. Based upon the configuration specified here, "red" contract will be applied to"faces/index.xhtml" and "red" contract will be applied to"faces/user/index.xhtml".
<resource-library-contracts>
<contract-mapping>
<url-pattern>/user/*</url-pattern>
<contracts>blue</contracts>
</contract-mapping>
<contract-mapping>
<url-pattern>*</url-pattern>
<contracts>red</contracts>
</contract-mapping>
</resource-library-contracts>
</application>
The template of the page can be changed dynamically as well. For example consider "index.xhtml" is updated as:
<f:view contracts="#{contractsBean.contract}">The "ui:composition" is included in "f:view". An additional"contracts" attribute can bind to an EL. The value of this EL is populated from the radio button in the newly added form. Now you can choose a radio button, click on the "Apply" button and the new template will be applied to the page. The bean is very trivial:
<ui:composition template="/template.xhtml">
<ui:define name="content">
<a href="#{facesContext.externalContext.requestContextPath}/faces/user/index.xhtml">Go to</a> other contract
<p/>
Look at WEB-INF/faces-config.xml for contract configuration.
<p/><p/>
Choose a template:<br/>
<h:form>
<h:selectOneRadio value="#{contractsBean.contract}" layout="pageDirection" required="true">
<f:selectItem itemValue="red" itemLabel="red"/>
<f:selectItem itemValue="blue" itemLabel="blue"/>
</h:selectOneRadio>
<h:commandButton value="Apply" action="index" />
</h:form>
</ui:define>
</ui:composition>
</f:view>
@Named
@SessionScoped
public class ContractsBean implements Serializable {
String contract = "red";
public String getContract() {
return contract;
}
public void setContract(String contract) {
this.contract = contract;
}
}
This is a very powerful feature. Imagine providing different look-and-feel for your website and letting the user choose them, fun eh ?
Contracts may be packaged as a JAR file. Such a JAR file maybe bundled in "WEB-INF/lib" directory. Read section 2.7 for more details about the packaging requirement and a marker file that identifies the JAR to contain a contract.
In the specification ...
- Section 10.1.3 provide background on the feature.
- Section 2.7 provide formal definition of the feature.
- Section 11.4.2.1 defines how the contracts are identified during application startup.