/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2015 ForgeRock AS. */ package org.forgerock.opendj.maven; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.AbstractMojo; 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.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.LinkedList; import java.util.List; /** * Generate DocBook RefEntry source documents for command-line tools man pages. */ @Mojo(name = "generate-refentry", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) public final class GenerateRefEntriesMojo extends AbstractMojo { /** The Maven Project. */ @Parameter(property = "project", required = true, readonly = true) private MavenProject project; /** Tools for which to generate RefEntry files. */ @Parameter private List tools; /** Where to write the RefEntry files. */ @Parameter(required = true) private File outputDir; /** End of line. */ public static final String EOL = System.getProperty("line.separator"); /** * Writes a RefEntry file to the output directory for each tool. * Files names correspond to script names: {@code man-<name>.xml}. * * @throws MojoExecutionException Encountered a problem writing a file. * @throws MojoFailureException Failed to initialize effectively, * or to write one or more RefEntry files. */ @Override public void execute() throws MojoExecutionException, MojoFailureException { PrintStream out = System.out; // Set the magic property for generating DocBook XML. System.setProperty("org.forgerock.opendj.gendoc", "true"); // A Maven plugin classpath does not include project files. // Prepare a ClassLoader capable of loading the command-line tools. URLClassLoader toolsClassLoader; try { List runtimeClasspathElements = project.getRuntimeClasspathElements(); List runtimeUrls = new LinkedList(); for (String element : runtimeClasspathElements) { runtimeUrls.add(new File(element).toURI().toURL()); } toolsClassLoader = new URLClassLoader( runtimeUrls.toArray(new URL[runtimeClasspathElements.size()]), Thread.currentThread().getContextClassLoader()); } catch (DependencyResolutionRequiredException e) { throw new MojoFailureException("Failed to access the runtime classpath.", e); } catch (MalformedURLException e) { throw new MojoFailureException("Failed to add element to classpath.", e); } debugClassPathElements(toolsClassLoader); List failures = new LinkedList(); for (CommandLineTool tool : tools) { final File manPage = new File(outputDir, "man-" + tool.getName() + ".xml"); try { setSystemOut(refEntryFile(manPage.getPath())); } catch (FileNotFoundException e) { setSystemOut(out); failures.add(manPage.getPath()); throw new MojoExecutionException("Failed to write " + manPage.getPath(), e); } // Set the properties for script name and list of trailing sections. System.setProperty("com.forgerock.opendj.ldap.tools.scriptName", tool.getName()); final String xInclude = pathsToXIncludes(tool.getTrailingSectionPaths()); System.setProperty("org.forgerock.opendj.gendoc.trailing", xInclude); try { final Class toolClass = toolsClassLoader.loadClass(tool.getApplication()); final Class[] argTypes = new Class[]{String[].class}; final Method main = toolClass.getDeclaredMethod("main", argTypes); final String[] args = {"-?"}; main.invoke(null, (Object) args); } catch (ClassNotFoundException e) { failures.add(manPage.getPath()); throw new MojoExecutionException(tool.getApplication() + " not found", e); } catch (NoSuchMethodException e) { failures.add(manPage.getPath()); throw new MojoExecutionException(tool.getApplication() + " has no main method.", e); } catch (IllegalAccessException e) { failures.add(manPage.getPath()); throw new MojoExecutionException("Failed to run " + tool.getApplication() + ".main()", e); } catch (InvocationTargetException e) { failures.add(manPage.getPath()); throw new MojoExecutionException("Failed to run " + tool.getApplication() + ".main()", e); } finally { setSystemOut(out); } } final StringBuilder list = new StringBuilder(); if (!failures.isEmpty()) { for (final String failure : failures) { list.append(failure).append(EOL); } throw new MojoFailureException("Failed to write the following RefEntry files: " + list); } } /** * Logs what is on the classpath for debugging. * @param classLoader The ClassLoader with the classpath. */ private void debugClassPathElements(ClassLoader classLoader) { if (null == classLoader) { return; } getLog().debug("--------------------"); getLog().debug(classLoader.toString()); if (classLoader instanceof URLClassLoader) { final URLClassLoader ucl = (URLClassLoader) classLoader; int i = 0; for (URL url : ucl.getURLs()) { getLog().debug("url[" + (i++) + "]=" + url); } } debugClassPathElements(classLoader.getParent()); } /** * Returns a PrintStream to a file to which to write a RefEntry. * @param path Path to the file to be written. * @return PrintStream to a file to which to write a RefEntry. * @throws FileNotFoundException Failed to open the file for writing. */ private PrintStream refEntryFile(final String path) throws FileNotFoundException { return new PrintStream(new BufferedOutputStream(new FileOutputStream(path)), true); } /** * Sets the System output stream. * @param out The stream to use. */ private void setSystemOut(PrintStream out) { if (out != null) { System.setOut(out); } } /** * Translates relative paths to XML files into XInclude elements. * * @param paths Paths to XInclude'd files, relative to the RefEntry. * @return String of XInclude elements corresponding to the paths. */ private String pathsToXIncludes(final List paths) { if (paths == null) { return ""; } // Assume xmlns:xinclude="http://www.w3.org/2001/XInclude", // as in the declaration of resources/templates/refEntry.ftl. final String nameSpace = "xinclude"; final StringBuilder result = new StringBuilder(); for (String path : paths) { result.append("<").append(nameSpace).append(":include href=\"").append(path).append("\" />").append(EOL); } return result.toString(); } }