Starting
with JSF 2.2, we can programmatically reproduce the content and tasks of faces-config.xml.
The starting point consists of a callback method, named populateApplicationConfiguration,
which gets a single argument of type org.w3c.dom.Document—this class belongs
to DOM API. Basically, a Document (tree node) is a representation in
memory of an XML document, and we can manipulate it by adding, removing,
importing, or adopting nodes, elements, and text. For each of these operations,
there are dedicated methods. For some JSF developers, this API can be something
new that should be learned; therefore, this can be a drawback of programmatic
configuration.
For now,
let's resume the dissertation from the callback method. The populateApplicationConfiguration
method is provided by a class that extends and implements the abstract class ApplicationConfigurationPopulator
found in the javax.faces.application
package. In order to tell JSF about this class, you need to:
1. Create a JAR package (for example, faces-config.jar or by
using any other name).
2. In this JAR package, create a folder named META-INF.
3. In the META-INF folder, create a folder named services.
4. In the services folder, create an empty file named javax.faces.application.ApplicationConfigurationPopulator.
5. In this file, write the fully qualified name of the class
that extends and implements the abstract class ApplicationConfigurationPopulator.
6. In the JAR root, place the class that extends and
implements the abstract class ApplicationConfigurationPopulator.
Done! Now
when you add this JAR package in your project CLASSPATH, JSF will process
it and apply the found configurations. Supposing that the class that extends
and implements the abstract class ApplicationConfigurationPopulator
is named faces.config.Initializer
(you can use any other name), then the JAR content will look like in the
following image:
When we are
working directly on a DOM tree node, we tend to make stupid mistakes, like
forgetting to add the text of an element, or placing an element in an improper
place, and so on. In order to eliminate these errors without headaches, you can
write a simple method to serialize the DOM in an XML file, which can be easily
debugged visually or using a specialized tool. The following method
accomplishes this task:
private void
serializeFacesConfig(Document document,String path) {
FileOutputStream fileOutputStream = null;
OutputFormat outputFormat = new
OutputFormat();
outputFormat.setIndent(5);
outputFormat.setLineWidth(150);
...
fileOutputStream = new FileOutputStream(path);
XMLSerializer xmlSerializer = new
XMLSerializer();
xmlSerializer.setOutputFormat(outputFormat);
xmlSerializer.setOutputByteStream((OutputStream)fileOutputStream);
xmlSerializer.serialize(document);
...
}
For example
a custom phase listener is configured in faces-config.xml as follows:
<lifecycle>
<phase-listener>beans.DebugPhaseListener</phase-listener>
</lifecycle>
The
programmatic reflection of this configuration in JSF 2.2 is as follows:
public class
Initializer extends ApplicationConfigurationPopulator {
@Override
public void populateApplicationConfiguration (Document
toPopulate) {
String ns =
toPopulate.getDocumentElement().getNamespaceURI();
Element lifecycleEl =
toPopulate.createElementNS(ns, "lifecycle");
Element phaselistenerEl = toPopulate.createElementNS(ns,
"phase-listener");
phaselistenerEl.appendChild(toPopulate.createTextNode("beans.DebugPhaseListener"));
lifecycleEl.appendChild(phaselistenerEl);
toPopulate.getDocumentElement().appendChild(lifecycleEl);
serializeFacesConfig(toPopulate,
"D://faces-config.xml");
}
...
}
Done!
That's an interesting tech demo, but it doesn't seem to be very useful to me. Basically, what you're doing is creating the XML DOM model using Java. That allows you to add "if" conditions to the faces-config.xml, but other than that, there's little extra value.
RăspundețiȘtergereIt would be much more useful to have a debuggable API - much the way Spring Boot offers. For instance, I want to be able to set a breakpoint in the line loading and / or initializing the CombinedResourcesListener. Is something like this possible?