JSF 2 remove faces-config.xml Programmatic configuration
In this tutorial we’ll explore how you can remove the faces-config.xml file completely from your project in order to configure JSF programmatically. I don’t believe this is a feature that’s going to be adopted by everyone but when you need some kind of dynamic configuration of any kind, you at least have the option. This feature is available starting JSF 2.2. In order to enable the programmatic configuration, you need to do the following:
- Create a META-INF folder.
- In the META-INF folder, create a folder named services.
- In the services folder, create a empty file named javax.faces.application.ApplicationConfigurationPopulator.
- In this file, write the fully qualified class name of the class that extends the ApplicationConfigurationPopulator class.
This is the configuration needed for your ApplicationConfigurationPopulator to be called. Now we’ll be looking at how you can create a complementary faces-config file programmatically.
Project structure
Here is an overview of how your projects structure must look like. It’s not required to be in the same jar or war file. You could easily create a separate jar file and include that file in your project.
+--src
| +--main
| +--java
| +--com
| +--memorynotfound
| |--DebugFaseListener.java
| |--FacesConfig.java
| |--resources
| +--META-INF
| +--services
| |--javax.faces.application.ApplicationConfigurationPopulator
| +--webapp
| +--WEB-INF
| |--web.xml
| |--index.xhtml
pom.xml
Debug Face Listener
We created a simple debug face listener that we are going to configure programmatically.
package com.memorynotfound;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
public class DebugPhaseListener implements PhaseListener {
@Override
public void beforePhase(PhaseEvent event) {
if (event.getPhaseId() == PhaseId.RESTORE_VIEW) {
System.out.println("Processing new Request!");
}
System.out.println("before - " + event.getPhaseId());
}
@Override
public void afterPhase(PhaseEvent event) {
System.out.println("after - " + event.getPhaseId());
if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
System.out.println("Done with Request!\n");
}
}
@Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
}
JSF Programmatic Configuration
If we extend the ApplicationConfigurationPopulator
class we must override the populateApplicationConfiguration()
method which has a org.w3c.dom.Document
as argument. This document is a representation in memory of an XML document (faces-config.xml), and we can manipulate it by adding, removing, importing, or adopting nodes, elements, and text.
package com.memorynotfound;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.faces.application.ApplicationConfigurationPopulator;
import java.io.FileOutputStream;
import java.io.IOException;
public class FacesConfig extends ApplicationConfigurationPopulator {
@Override
public void populateApplicationConfiguration(Document fcDoc) {
String ns = fcDoc.getDocumentElement().getNamespaceURI();
Element lifecycleEl = fcDoc.createElementNS(ns, "lifecycle");
Element phaselistenerEl = fcDoc.createElementNS(ns, "phase-listener");
phaselistenerEl.appendChild(fcDoc.createTextNode("com.memorynotfound.DebugPhaseListener"));
lifecycleEl.appendChild(phaselistenerEl);
fcDoc.getDocumentElement().appendChild(lifecycleEl);
try {
serializeFacesConfig(fcDoc, "faces-config.xml");
} catch (IOException e) {
System.err.println("could not create faces-config");
}
}
private void serializeFacesConfig(Document document, String path) throws IOException {
FileOutputStream fos = null;
OutputFormat outFormat = new OutputFormat();
outFormat.setIndent(5);
outFormat.setLineWidth(150);
fos = new FileOutputStream(path);
XMLSerializer xmlSerializer = new XMLSerializer();
xmlSerializer.setOutputFormat(outFormat);
xmlSerializer.setOutputByteStream(fos);
xmlSerializer.serialize(document);
}
}
Content of the javax.faces.application.ApplicationConfigurationPopulator file
We must include the fully qualified class name of the class that extends the ApplicationConfigurationPopulator
class.
com.memorynotfound.FacesConfig
Simple View
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h1>JSF 2.2 Programmatic configuration</h1>
</h:body>
</html>
Debug Output
The debug face listener simple outputs which faces have passed the request.
Processing new Request!
before - RESTORE_VIEW 1
after - RESTORE_VIEW 1
before - RENDER_RESPONSE 6
after - RENDER_RESPONSE 6
Done with Request!
Demo
URL: http://localhost:8081/jsf-programmatic-configuration/