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:

  1. Create a META-INF folder.
  2. In the META-INF folder, create a folder named services.
  3. In the services folder, create a empty file named javax.faces.application.ApplicationConfigurationPopulator.
  4. 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/

jsf programmatic configuration

References

Download

You may also like...