blob: df2242057b8a1ec875e36e7f5940b79e7d7a1ce1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.buildtools.maven2.plugin.faces;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.myfaces.buildtools.maven2.plugin.faces.util.XIncludeFilter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.net.URL;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* @version $Id$
* @requiresDependencyResolution compile
* @goal generate-faces-config
* @phase generate-resources
*/
public class GenerateFacesConfigMojo extends AbstractFacesMojo
{
/**
* Execute the Mojo.
*/
public void execute() throws MojoExecutionException
{
try
{
// always add resources directory to project resource root
addResourceRoot(project, targetDirectory.getCanonicalPath());
File targetFile = new File(targetDirectory, targetPath);
URL[] index = readIndex(project);
if (!force && !isModifiedSince(index, targetFile.lastModified()))
{
getLog().info("Nothing to generate - " + targetPath + " is up to date");
}
else
{
List configURLs = getMasterConfigs(project);
File configFile = new File(configDirectory, configPath);
if (configFile.exists())
{
configURLs = new LinkedList(configURLs);
// base config goes first
configURLs.add(0, configFile.toURL());
}
if (!configURLs.isEmpty())
{
URL baseURL = GenerateFacesConfigMojo.class.getResource("resources/faces-config.xml");
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document document = docBuilder.parse(baseURL.openStream());
Element root = document.getDocumentElement();
// embed each <xi:include> element
for (Iterator i = configURLs.iterator(); i.hasNext(); )
{
URL url = (URL)i.next();
Element include = document.createElementNS("http://www.w3.org/2001/XInclude",
"xi:include");
include.setAttribute("href", url.toExternalForm());
include.setAttribute("xpointer", "/faces-config/*");
root.insertBefore(include, root.getFirstChild());
}
// Note: use a direct DOM->SAX reader
// rather than transforming to string
TransformerFactory transFactory = TransformerFactory.newInstance();
Transformer identity = transFactory.newTransformer();
ByteArrayOutputStream out = new ByteArrayOutputStream();
identity.transform(new DOMSource(document), new StreamResult(out));
InputStream mergedStream = new ByteArrayInputStream(out.toByteArray());
// expand all the xi:include elements
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
saxFactory.setNamespaceAware(true);
saxFactory.setValidating(false);
SAXParser saxParser = saxFactory.newSAXParser();
XMLReader mergedReader = saxParser.getXMLReader();
mergedReader = new XIncludeFilter(mergedReader, baseURL);
mergedReader.setEntityResolver(new EntityResolver()
{
public InputSource resolveEntity(
String publicId,
String systemId)
{
return new InputSource(new ByteArrayInputStream(new byte[0]));
}
});
InputSource mergedInput = new InputSource(mergedStream);
Source mergedSource = new SAXSource(mergedReader, mergedInput);
// Transform the combined faces-config.xml file to resolve
// component-supertype, inject the DTD and ensure that
// metadata is represented compatibly with JSF 1.1 syntax.
targetFile.delete();
targetFile.getParentFile().mkdirs();
File tmpFile = null;
OutputStream resultStream;
if (transformStylesheet != null)
{
tmpFile = File.createTempFile("generate-faces-config", null);
resultStream = new FileOutputStream(tmpFile);
}
else
{
resultStream = new FileOutputStream(targetFile);
}
Result mergedResult = new StreamResult(resultStream);
URL xslURL;
if (_is12())
xslURL = getClass().getResource("resources/transform12.xsl");
else
xslURL = getClass().getResource("resources/transform.xsl");
InputStream xsl = xslURL.openStream();
StreamSource xslSource = new StreamSource(xsl);
Transformer transformer = transFactory.newTransformer(xslSource);
transformer.setParameter("packageContains", packageContains);
transformer.setParameter("typePrefix", typePrefix);
transformer.setParameter("removeRenderers",
removeRenderers ? "true" : "false");
transformer.setParameter("converterPackageContains", getParameter(converterPackageContains, packageContains));
transformer.setParameter("validatorPackageContains", getParameter(validatorPackageContains, packageContains));
transformer.transform(mergedSource, mergedResult);
resultStream.close();
// OK, if there's a transformSylesheet, we've written
// the output to a temporary file, now transform it again.
// =-=FIXME AdamWiner: no, this is not the smartest and
// fastest way to do it. But after finding out
// that XMLFilter apparently doesn't support XSL parameters,
// and running into a bizarre "illegal processing instruction:
// saxon:warning" error message when using chained handlers, I got
// tired of trying to be smart. This is good enough.
if (transformStylesheet != null)
{
StreamSource tmpSource = new StreamSource(tmpFile);
StreamSource transformSource = new StreamSource(
new File(configDirectory, transformStylesheet));
Transformer finalTransform =
transFactory.newTransformer(transformSource);
FileOutputStream finalOut = new FileOutputStream(targetFile);
Result finalResult =
new StreamResult(finalOut);
finalTransform.transform(tmpSource, finalResult);
finalOut.close();
tmpFile.delete();
}
targetFile.setReadOnly();
getLog().info("Generated " + targetPath);
}
}
}
catch (SAXException e)
{
throw new MojoExecutionException("Error during generation", e);
}
catch (TransformerException e)
{
throw new MojoExecutionException("Error during generation", e);
}
catch (ParserConfigurationException e)
{
throw new MojoExecutionException("Error during generation", e);
}
catch (IOException e)
{
throw new MojoExecutionException("Error during generation", e);
}
}
private boolean _is12()
{
return "1.2".equals(jsfVersion) || "12".equals(jsfVersion);
}
private String getParameter(String paramName, String defaultValue)
{
String param;
if (paramName.length() > 0)
{
param = paramName;
}
else
{
param = defaultValue;
}
return param;
}
/**
* @parameter expression="${project}"
* @readonly
*/
private MavenProject project;
/**
* @parameter expression="src/main/conf"
* @required
*/
private File configDirectory;
/**
* @parameter
*/
private String configPath = "META-INF/faces-config-base.xml";
/**
* @parameter
*/
private String packageContains = "";
/**
* @parameter
*/
private String converterPackageContains = "";
/**
* @parameter
*/
private String validatorPackageContains = "";
/**
* @parameter expression="${project.build.directory}/maven-faces-plugin/main/resources"
* @required
*/
private File targetDirectory;
/**
* @parameter
*/
private String targetPath = "META-INF/faces-config.xml";
/**
* @parameter
*/
private boolean force;
/**
* @parameter
*/
private String typePrefix = "";
/**
* Name of an XSLT stylesheet in src/main/conf that will be applied
* as the final step. This can be used to implement any final fixup
* of the output.
* @parameter
*/
private String transformStylesheet;
/**
* @parameter
*/
private boolean removeRenderers;
/**
* @parameter
*/
private String jsfVersion;
}