package org.xdi.oxauth.customization;
import com.sun.faces.config.DbfFactory;
import com.sun.faces.util.FacesLogger;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ls.LSResourceResolver;
import javax.faces.application.ApplicationConfigurationPopulator;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.logging.Level;
/**
* Created by eugeniuparvan on 5/1/17.
*/
public class FacesConfigPopulator extends ApplicationConfigurationPopulator {
/**
* <p>/faces-config/navigation-rule</p>
*/
private static final String NAVIGATION_RULE = "navigation-rule";
private static final String FACES_2_2_XSD = "/com/sun/faces/web-facesconfig_2_2.xsd";
private static final String FACES_CONFIG_PATTERN = ".*\\.navigation\\.xml$";
private static final String DEFAULT_NAVIGATION_PATH = "META-INF/navigation";
@Override
public void populateApplicationConfiguration(Document toPopulate) {
if (!Utils.isCustomPagesDirExists())
return;
try {
findAndUpdateNavigationRules(toPopulate, Utils.getCustomPagesPath());
} catch (Exception e) {
FacesLogger.CONFIG.getLogger().log(Level.SEVERE, "Can't add customized navigation rules");
}
try {
Enumeration<URL> urlEnumeration = getClass().getClassLoader().getResources(DEFAULT_NAVIGATION_PATH);
if (urlEnumeration.hasMoreElements()) {
URL url = urlEnumeration.nextElement();
findAndUpdateNavigationRules(toPopulate, url.getPath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Recursively finds all *.navigation.xml files located in custom pages directory, and adds navigation rules to
* navigation handler
*
* @param path to custom pages directory
* @throws Exception
*/
private void findAndUpdateNavigationRules(Document toPopulate, String path) throws Exception {
File file = new File(path);
RegexFileFilter regexFileFilter = new RegexFileFilter(FACES_CONFIG_PATTERN);
Collection<File> facesConfigFiles = FileUtils.listFiles(file, regexFileFilter, DirectoryFileFilter.DIRECTORY);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
for (File files : facesConfigFiles) {
String faceConfig = files.getAbsolutePath();
updateDocument(toPopulate, builder, faceConfig);
}
}
/**
* Validates *.faces-config.xml file and creates DocumentInfo class
*
* @param docBuilder
* @param faceConfig
* @return
*/
private void updateDocument(Document toPopulate, DocumentBuilder docBuilder, String faceConfig) {
try {
InputStream xml = new FileInputStream(faceConfig);
if (!isValidFacesConfig(xml))
return;
Document document = docBuilder.parse(new File(faceConfig));
Element root = toPopulate.getDocumentElement();
NodeList navigationRules = getNavigationRules(document);
for (int i = 0; i < navigationRules.getLength(); ++i) {
Node importedNode = toPopulate.importNode(navigationRules.item(i), true);
root.appendChild(importedNode);
}
} catch (Exception e) {
}
}
/**
* Validates *.faces-config.xml file
*
* @param xml
* @return
*/
private boolean isValidFacesConfig(InputStream xml) {
try {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
factory.setResourceResolver((LSResourceResolver) DbfFactory.FACES_ENTITY_RESOLVER);
InputStream xsd = this.getClass().getResourceAsStream(FACES_2_2_XSD);
Schema schema = factory.newSchema(new StreamSource(xsd));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(xml));
return true;
} catch (Exception ex) {
return false;
}
}
private NodeList getNavigationRules(Document document) {
String namespace = document.getDocumentElement()
.getNamespaceURI();
return document.getDocumentElement().getElementsByTagNameNS(namespace, NAVIGATION_RULE);
}
}