Java tutorial
package org.codehaus.mojo.xml; /* * 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. */ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.xml.transform.Source; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import org.apache.maven.model.Resource; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.codehaus.mojo.xml.transformer.NameValuePair; import org.codehaus.mojo.xml.transformer.TransformationSet; import org.codehaus.plexus.components.io.filemappers.FileMapper; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; import org.xml.sax.InputSource; /** * The TransformMojo is used for transforming a set of files using a common stylesheet. */ @Mojo(defaultPhase = LifecyclePhase.GENERATE_RESOURCES, name = "transform", threadSafe = true) public class TransformMojo extends AbstractXmlMojo { /** * Specifies one or more sets of files, which are being transformed. See * <a href="transformation.html">Transforming XML Files</a> */ @Parameter private TransformationSet[] transformationSets; /** * Whether creating the transformed files should be forced. */ @Parameter(property = "xml.forceCreation", defaultValue = "false") private boolean forceCreation; /** * Transformer factory use. By default, the systems default transformer factory is used. <b>If you use this feature * you must use at least jdk 1.6</b> */ @Parameter(property = "xml.transformerFactory") private String transformerFactory; private void setFeature(TransformerFactory pTransformerFactory, String name, Boolean value) throws MojoExecutionException { // Try to use the method setFeature, which isn't available until JAXP 1.3 Method m; try { m = pTransformerFactory.getClass().getMethod("setFeature", new Class[] { String.class, boolean.class }); } catch (NoSuchMethodException e) { m = null; } if (m == null) { // Not available, try to use setAttribute pTransformerFactory.setAttribute(name, value); } else { try { m.invoke(pTransformerFactory, new Object[] { name, value }); } catch (IllegalAccessException e) { throw new MojoExecutionException(e.getMessage(), e); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); throw new MojoExecutionException(t.getMessage(), t); } } } private Templates getTemplate(Resolver pResolver, Source stylesheet, TransformationSet transformationSet) throws MojoExecutionException, MojoFailureException { TransformerFactory tf = getTransformerFactory(); if (pResolver != null) { tf.setURIResolver(pResolver); } NameValuePair[] features = transformationSet.getFeatures(); if (features != null) { for (int i = 0; i < features.length; i++) { final NameValuePair feature = features[i]; final String name = feature.getName(); if (name == null || name.length() == 0) { throw new MojoFailureException("A features name is missing or empty."); } final String value = feature.getValue(); if (value == null) { throw new MojoFailureException("No value specified for feature " + name); } setFeature(tf, name, Boolean.valueOf(value)); } } NameValuePair[] attributes = transformationSet.getAttributes(); if (attributes != null) { for (int i = 0; i < attributes.length; i++) { final NameValuePair attribute = attributes[i]; final String name = attribute.getName(); if (name == null || name.length() == 0) { throw new MojoFailureException("An attributes name is missing or empty."); } final String value = attribute.getValue(); if (value == null) { throw new MojoFailureException("No value specified for attribute " + name); } tf.setAttribute(name, value); } } try { return tf.newTemplates(stylesheet); } catch (TransformerConfigurationException e) { throw new MojoExecutionException("Failed to parse stylesheet " + stylesheet + ": " + e.getMessage(), e); } } /** * Creates a new instance of {@link TransformerFactory}. */ private TransformerFactory getTransformerFactory() throws MojoFailureException, MojoExecutionException { if (transformerFactory == null) { return TransformerFactory.newInstance(); } try { return newTransformerFactory(transformerFactory, Thread.currentThread().getContextClassLoader()); } catch (NoSuchMethodException exception) { throw new MojoFailureException("JDK6 required when using transformerFactory parameter"); } catch (IllegalAccessException exception) { throw new MojoExecutionException("Cannot instantiate transformer factory", exception); } catch (InvocationTargetException exception) { throw new MojoExecutionException("Cannot instantiate transformer factory", exception); } } // public for use by unit test public static TransformerFactory newTransformerFactory(String factoryClassName, ClassLoader classLoader) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { // use reflection to avoid JAXP 1.4 (and hence JDK6) requirement Class<?>[] methodTypes = new Class[] { String.class, ClassLoader.class }; Method method = TransformerFactory.class.getDeclaredMethod("newInstance", methodTypes); Object[] methodArgs = new Object[] { factoryClassName, classLoader }; return (TransformerFactory) method.invoke(null, methodArgs); } private File getFile(File pDir, String pFile) { if (new File(pFile).isAbsolute()) { throw new IllegalStateException("Output/Input file names must not be absolute."); } return new File(pDir, pFile); } private File getDir(File pDir) { if (pDir == null) { return getBasedir(); } return asAbsoluteFile(pDir); } private void addToClasspath(File pOutputDir) { MavenProject project = getProject(); for (Iterator<Resource> iter = project.getResources().iterator(); iter.hasNext();) { Resource resource = iter.next(); if (resource.getDirectory().equals(pOutputDir)) { return; } } Resource resource = new Resource(); resource.setDirectory(pOutputDir.getPath()); resource.setFiltering(false); project.addResource(resource); } private File getOutputDir(File pOutputDir) { if (pOutputDir == null) { MavenProject project = getProject(); String dir = project.getBuild().getDirectory(); if (dir == null) { throw new IllegalStateException("The projects build directory is null."); } dir += "/generated-resources/xml/xslt"; return asAbsoluteFile(new File(dir)); } return asAbsoluteFile(pOutputDir); } private static String getAllExMsgs(Throwable ex, boolean includeExName) { StringBuffer sb = new StringBuffer((includeExName ? ex.toString() : ex.getLocalizedMessage())); while ((ex = ex.getCause()) != null) { sb.append("\nCaused by: " + ex.toString()); } return sb.toString(); } /** * @param files the fileNames or URLs to scan their lastModified timestamp. * @param oldest if true, returns the latest modificationDate of all files, otherwise returns the earliest. * @return the older or younger last modification timestamp of all files. */ protected long findLastModified(List<?> files, boolean oldest) { long timeStamp = (oldest ? Long.MIN_VALUE : Long.MAX_VALUE); for (Iterator<?> it = files.iterator(); it.hasNext();) { Object no = it.next(); if (no != null) { long fileModifTime; if (no instanceof File) { fileModifTime = ((File) no).lastModified(); } else // either URL or filePath { String sdep = no.toString(); try { URL url = new URL(sdep); URLConnection uCon = url.openConnection(); uCon.setUseCaches(false); fileModifTime = uCon.getLastModified(); } catch (MalformedURLException e) { fileModifTime = new File(sdep).lastModified(); } catch (IOException ex) { fileModifTime = (oldest ? Long.MIN_VALUE : Long.MAX_VALUE); getLog().warn("Skipping URL '" + no + "' from up-to-date check due to error while opening connection: " + getAllExMsgs(ex, true)); } } getLog().debug((oldest ? "Depends " : "Produces ") + no + ": " + new Date(fileModifTime)); if ((fileModifTime > timeStamp) ^ !oldest) { timeStamp = fileModifTime; } } // end if file null. } // end filesloop if (timeStamp == Long.MIN_VALUE) { // no older file found return Long.MAX_VALUE; // assume re-execution required. } else if (timeStamp == Long.MAX_VALUE) { // no younger file found return Long.MIN_VALUE; // assume re-execution required. } return timeStamp; } /** * @return true to indicate results are up-to-date, that is, when the latest from input files is earlier than the * younger from the output files (meaning no re-execution required). */ protected boolean isUpdToDate(List<?> dependsFiles, List<?> producesFiles) { // The older timeStamp of all input files; long inputTimeStamp = findLastModified(dependsFiles, true); // The younger of all destination files. long destTimeStamp = producesFiles == null ? Long.MIN_VALUE : findLastModified(producesFiles, false); getLog().debug("Depends timeStamp: " + inputTimeStamp + ", produces timestamp: " + destTimeStamp); return inputTimeStamp < destTimeStamp; } private void transform(Transformer pTransformer, File input, File output, Resolver pResolver) throws MojoExecutionException { File dir = output.getParentFile(); dir.mkdirs(); getLog().info("Transforming file: " + input.getPath()); FileOutputStream fos = null; try { final boolean transformInPlace = output.equals(input); File tmpOutput = null; if (transformInPlace) { tmpOutput = File.createTempFile("xml-maven-plugin", "xml"); tmpOutput.deleteOnExit(); fos = new FileOutputStream(tmpOutput); } else { fos = new FileOutputStream(output); } final String parentFile = input.getParent() == null ? null : input.getParentFile().toURI().toURL().toExternalForm(); pTransformer.transform(pResolver.resolve(input.toURI().toURL().toExternalForm(), parentFile), new StreamResult(fos)); fos.close(); fos = null; if (transformInPlace) { FileUtils.copyFile(tmpOutput, output); /* tmpOutput is a temporary file */ tmpOutput.delete(); } } catch (IOException e) { throw new MojoExecutionException( "Failed to create output file " + output.getPath() + ": " + e.getMessage(), e); } catch (TransformerException e) { throw new MojoExecutionException( "Failed to transform input file " + input.getPath() + ": " + e.getMessage(), e); } finally { if (fos != null) { try { fos.close(); } catch (Throwable t) { /* Ignore me */ } } } } private File getOutputFile(File targetDir, String pName, FileMapper[] pFileMappers) { String name = pName; if (pFileMappers != null) { for (int i = 0; i < pFileMappers.length; i++) { name = pFileMappers[i].getMappedFileName(name); } } return getFile(targetDir, name); } private void transform(Resolver pResolver, TransformationSet pTransformationSet) throws MojoExecutionException, MojoFailureException { String[] fileNames = getFileNames(pTransformationSet.getDir(), pTransformationSet.getIncludes(), getExcludes(pTransformationSet.getExcludes(), pTransformationSet.isSkipDefaultExcludes())); if (fileNames == null || fileNames.length == 0) { getLog().warn("No files found for transformation by stylesheet " + pTransformationSet.getStylesheet()); return; } String stylesheetName = pTransformationSet.getStylesheet(); if (stylesheetName == null) { getLog().warn("No stylesheet configured."); return; } final URL stylesheetUrl = getResource(stylesheetName); Templates template; InputStream stream = null; try { stream = stylesheetUrl.openStream(); InputSource isource = new InputSource(stream); isource.setSystemId(stylesheetUrl.toExternalForm()); template = getTemplate(pResolver, new SAXSource(isource), pTransformationSet); stream.close(); stream = null; } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); } finally { IOUtil.close(stream); } int filesTransformed = 0; File inputDir = getDir(pTransformationSet.getDir()); File outputDir = getOutputDir(pTransformationSet.getOutputDir()); for (int i = 0; i < fileNames.length; i++) { final Transformer t; File input = getFile(inputDir, fileNames[i]); File output = getOutputFile(outputDir, fileNames[i], pTransformationSet.getFileMappers()); // Perform up-to-date-check. boolean needsTransform = forceCreation; if (!needsTransform) { List<File> dependsFiles = new ArrayList<File>(); List<File> producesFiles = new ArrayList<File>(); // Depends from pom.xml file for when project configuration changes. dependsFiles.add(getProject().getFile()); if ("file".equals(stylesheetUrl.getProtocol())) { dependsFiles.add(new File(stylesheetUrl.getFile())); } List<File> catalogFiles = new ArrayList<File>(); List<URL> catalogUrls = new ArrayList<URL>(); setCatalogs(catalogFiles, catalogUrls); dependsFiles.addAll(catalogFiles); dependsFiles.add(input); File[] files = asFiles(getBasedir(), pTransformationSet.getOtherDepends()); for (int j = 0; j < files.length; j++) { dependsFiles.add(files[j]); } producesFiles.add(output); needsTransform = !isUpdToDate(dependsFiles, producesFiles); } if (!needsTransform) { getLog().debug("Skipping XSL transformation. File " + fileNames[i] + " is up-to-date."); } else { filesTransformed++; // Perform transformation. try { t = newTransformer(template, pTransformationSet); t.setURIResolver(pResolver); NameValuePair[] parameters = pTransformationSet.getParameters(); if (parameters != null) { for (int j = 0; j < parameters.length; j++) { NameValuePair key = parameters[j]; getLog().debug("Setting Parameter: " + key.getName() + "=" + key.getValue()); t.setParameter(key.getName(), key.getValue()); } } transform(t, input, output, pResolver); } catch (TransformerConfigurationException e) { throw new MojoExecutionException("Failed to create Transformer: " + e.getMessage(), e); } } } // end file loop if (filesTransformed > 0) { getLog().info("Transformed " + filesTransformed + " file(s)."); } if (pTransformationSet.isAddedToClasspath()) { addToClasspath(getOutputDir(pTransformationSet.getOutputDir())); } } private Transformer newTransformer(Templates template, TransformationSet pTransformationSet) throws TransformerConfigurationException, MojoExecutionException, MojoFailureException { Transformer t = template.newTransformer(); NameValuePair[] properties = pTransformationSet.getOutputProperties(); if (properties != null) { for (int i = 0; i < properties.length; i++) { final String name = properties[i].getName(); if (name == null || "".equals(name)) { throw new MojoFailureException("Missing or empty output property name"); } final String value = properties[i].getValue(); if (value == null) { throw new MojoFailureException("Missing value for output property " + name); } try { t.setOutputProperty(name, value); } catch (IllegalArgumentException e) { throw new MojoExecutionException( "Unsupported property name or value: " + name + " => " + value + e.getMessage(), e); } } } return t; } /** * Called by Maven to run the plugin. */ public void execute() throws MojoExecutionException, MojoFailureException { if (isSkipping()) { getLog().debug("Skipping execution, as demanded by user."); return; } if (transformationSets == null || transformationSets.length == 0) { throw new MojoFailureException("No TransformationSets configured."); } checkCatalogHandling(); Object oldProxySettings = activateProxy(); try { Resolver resolver = getResolver(); for (int i = 0; i < transformationSets.length; i++) { TransformationSet transformationSet = transformationSets[i]; resolver.setXincludeAware(transformationSet.isXincludeAware()); resolver.setValidating(transformationSet.isValidating()); transform(resolver, transformationSet); } } finally { passivateProxy(oldProxySettings); } } }