From 8a81bad1a27eb157108f63c6cc5d313a13b17be0 Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Fri, 27 Mar 2015 14:14:51 +0000
Subject: [PATCH] CR-6483 OPENDJ-1786 Automate integration of generated content

---
 opendj-maven-plugin/src/main/java/org/forgerock/opendj/maven/GenerateRefEntriesMojo.java |  221 ++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 145 insertions(+), 76 deletions(-)

diff --git a/opendj-maven-plugin/src/main/java/org/forgerock/opendj/maven/GenerateRefEntriesMojo.java b/opendj-maven-plugin/src/main/java/org/forgerock/opendj/maven/GenerateRefEntriesMojo.java
index 1b5be78..fbd3c56 100644
--- a/opendj-maven-plugin/src/main/java/org/forgerock/opendj/maven/GenerateRefEntriesMojo.java
+++ b/opendj-maven-plugin/src/main/java/org/forgerock/opendj/maven/GenerateRefEntriesMojo.java
@@ -35,18 +35,20 @@
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.project.MavenProject;
 
-import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
 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.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.MalformedURLException;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Generate DocBook RefEntry source documents for command-line tools man pages.
@@ -67,9 +69,6 @@
     @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-&lt;name>.xml}.
@@ -80,17 +79,29 @@
      */
     @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 = getBootToolsClassLoader();
+
+        if (!isOutputDirAvailable()) {
+            throw new MojoFailureException("Output directory " + outputDir.getPath() + " not available");
+        }
+
+        for (CommandLineTool tool : tools) {
+            generateManPageForTool(toolsClassLoader, tool);
+        }
+    }
+
+    /**
+     * Returns a ClassLoader capable of loading the command-line tools.
+     * @return A ClassLoader capable of loading the command-line tools.
+     * @throws MojoFailureException     Failed to build classpath.
+     */
+    private URLClassLoader getBootToolsClassLoader() throws MojoFailureException {
         URLClassLoader toolsClassLoader;
         try {
             List<String> runtimeClasspathElements = project.getRuntimeClasspathElements();
-            List<URL> runtimeUrls = new LinkedList<URL>();
+            Set<URL> runtimeUrls = new LinkedHashSet<URL>();
             for (String element : runtimeClasspathElements) {
                 runtimeUrls.add(new File(element).toURI().toURL());
             }
@@ -103,53 +114,99 @@
             throw new MojoFailureException("Failed to add element to classpath.", e);
         }
         debugClassPathElements(toolsClassLoader);
+        return toolsClassLoader;
+    }
 
-        List<String> failures = new LinkedList<String>();
-        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);
+    /**
+     * Generate a RefEntry file to the output directory for a tool.
+     * The files name corresponds to the tool name: {@code man-&lt;name>.xml}.
+     * @param toolsClassLoader          The ClassLoader to run the tool.
+     * @param tool                      The tool to run in order to generate the page.
+     * @throws MojoExecutionException   Failed to run the tool.
+     * @throws MojoFailureException     Tool did not exit successfully.
+     */
+    private void generateManPageForTool(final URLClassLoader toolsClassLoader, final CommandLineTool tool)
+            throws MojoExecutionException, MojoFailureException {
+        final File   manPage    = new File(outputDir, "man-" + tool.getName() + ".xml");
+        final String toolScript = tool.getName();
+        final String toolSects  = pathsToXIncludes(tool.getTrailingSectionPaths());
+        final String toolClass  = tool.getApplication();
+        List<String> commands   = new LinkedList<String>();
+        commands.add(getJavaCommand());
+        commands.addAll(getJavaArgs(toolScript, toolSects));
+        commands.add("-classpath");
+        commands.add(getClassPath(toolsClassLoader));
+        commands.add(toolClass);
+        commands.add(getUsageArgument(toolScript));
+        getLog().info("Writing man page: " + manPage.getPath());
+        try {
+            // Tools tend to use System.exit() so run them as separate processes.
+            ProcessBuilder builder = new ProcessBuilder(commands);
+            Process process = builder.start();
+            writeToFile(process.getInputStream(), manPage);
+            process.waitFor();
+            final int result = process.exitValue();
+            if (result != 0) {
+                throw new MojoFailureException("Failed to write page. Tool exit code: " + result);
             }
+        }  catch (InterruptedException e) {
+            throw new MojoExecutionException(toolClass + " interrupted", e);
+        } catch (IOException e) {
+            throw new MojoExecutionException(toolClass + " not found", 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);
+    /**
+     * Returns true if the output directory is available.
+     * Attempts to create the directory if it does not exist.
+     * @return True if the output directory is available.
+     */
+    private boolean isOutputDirAvailable() {
+        return outputDir != null && (outputDir.exists() && outputDir.isDirectory() || outputDir.mkdirs());
+    }
 
+    /**
+     * Returns the path to the current Java executable.
+     * @return The path to the current Java executable.
+     */
+    private String getJavaCommand() {
+        return System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
+    }
+
+    /**
+     * Returns the Java args for running a tool.
+     * @param scriptName        The name of the tool.
+     * @param trailingSections  The man page sections to Xinclude.
+     * @return The Java args for running a tool.
+     */
+    private List<String> getJavaArgs(final String scriptName, final String trailingSections) {
+        List<String> args = new LinkedList<String>();
+        args.add("-Dorg.forgerock.opendj.gendoc=true");
+        args.add("-Dorg.opends.server.ServerRoot=" + System.getProperty("java.io.tmpdir"));
+        args.add("-Dcom.forgerock.opendj.ldap.tools.scriptName=" + scriptName);
+        args.add("-Dorg.forgerock.opendj.gendoc.trailing=" + trailingSections + "");
+        return args;
+    }
+
+    /**
+     * Returns the classpath for the class loader.
+     * @param classLoader   Contains the URLs of the class path to return.
+     * @return The classpath for the class loader.
+     */
+    private String getClassPath(final URLClassLoader classLoader) {
+        final StringBuilder stringBuilder = new StringBuilder();
+        final URL[] urls = classLoader.getURLs();
+        for (int i = 0; i < urls.length; i++) {
+            if (i > 0) {
+                stringBuilder.append(File.pathSeparator);
+            }
             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);
+                stringBuilder.append(new File(urls[i].toURI()).getPath());
+            } catch (URISyntaxException e) {
+                getLog().info("Failed to add classpath element", e);
             }
         }
-
-        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);
-        }
+        return stringBuilder.toString();
     }
 
     /**
@@ -173,26 +230,6 @@
     }
 
     /**
-     * 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.
@@ -208,8 +245,40 @@
         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);
+            result.append("<").append(nameSpace).append(":include href='").append(path).append("' />");
         }
         return result.toString();
     }
+
+    /**
+     * Returns the usage argument.
+     * @param scriptName The name of the tool.
+     * @return The usage argument.
+     */
+    private String getUsageArgument(final String scriptName) {
+        return scriptName.equals("dsjavaproperties") ? "-H" : "-?";
+    }
+
+    /**
+     * Write the content of the input stream to the output file.
+     * @param input     The input stream to write.
+     * @param output    The file to write it to.
+     * @throws IOException  Failed to write the content of the input stream.
+     */
+    private void writeToFile(final InputStream input, final File output) throws IOException {
+        FileWriter writer = null;
+        try {
+            writer = new FileWriter(output);
+            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                writer.write(line);
+                writer.write(System.getProperty("line.separator"));
+            }
+        } finally {
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
 }

--
Gitblit v1.10.0