From be5173c6b9f258ae8a97796c718f5e61f99ceae0 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Thu, 05 Feb 2015 13:27:38 +0000
Subject: [PATCH] OPENDJ-1768 Add maven plugin to generate log reference

---
 opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/package-info.java            |   29 ++
 opendj-logref-doc-maven-plugin/pom.xml                                                        |   47 ++++
 opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/GenerateMessageFileMojo.java |  548 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 624 insertions(+), 0 deletions(-)

diff --git a/opendj-logref-doc-maven-plugin/pom.xml b/opendj-logref-doc-maven-plugin/pom.xml
new file mode 100644
index 0000000..196ffc5
--- /dev/null
+++ b/opendj-logref-doc-maven-plugin/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+  xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>opendj-project</artifactId>
+    <groupId>org.forgerock.opendj</groupId>
+    <version>3.0.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>opendj-logref-doc-maven-plugin</artifactId>
+  <version>1.0.0-SNAPSHOT</version>
+  <name>OpenDJ Documentation LogRef Maven Plugin</name>
+  <description>
+    Generation of log references for admin and intallation guides of OpenDJ project.
+  </description>
+  <packaging>maven-plugin</packaging>
+
+  <dependencies>
+    <dependency>
+        <groupId>org.forgerock.commons</groupId>
+        <artifactId>forgerock-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.forgerock.commons</groupId>
+      <artifactId>i18n-slf4j</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.twdata.maven</groupId>
+      <artifactId>mojo-executor</artifactId>
+      <version>2.2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>2.0</version>
+    </dependency>
+
+    <!-- dependencies to annotations -->
+    <dependency>
+      <groupId>org.apache.maven.plugin-tools</groupId>
+      <artifactId>maven-plugin-annotations</artifactId>
+      <version>3.4</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/GenerateMessageFileMojo.java b/opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/GenerateMessageFileMojo.java
new file mode 100644
index 0000000..9230749
--- /dev/null
+++ b/opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/GenerateMessageFileMojo.java
@@ -0,0 +1,548 @@
+/*
+ * 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 2008-2010 Sun Microsystems, Inc.
+ *      Portions Copyright 2011-2015 ForgeRock AS
+ */
+package org.forgerock.maven;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+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.project.MavenProject;
+import org.forgerock.util.Utils;
+
+/**
+ * Generates xml files containing representations of messages found in properties files.
+ * <p>
+ * There is a single goal that generates xml files.
+ * <p>
+ */
+@Mojo(defaultPhase=LifecyclePhase.PRE_SITE, name="generate-xml-messages-doc")
+public class GenerateMessageFileMojo extends AbstractMojo {
+
+    /**
+     * The Maven Project.
+     */
+    @Parameter(property="project", readonly=true, required=true)
+    private MavenProject project;
+
+    /**
+     * The path to the directory containing the message properties files.
+     */
+    @Parameter(required=true)
+    private String messagesDirectory;
+
+    /**
+     * The path to the directory where xml reference files should be written.
+     * This path must be relative to ${project.build.directory}.
+     */
+    @Parameter(required=true)
+    private String outputDirectory;
+
+    /**
+     * A list which contains all file names, the extension is not needed.
+     */
+    @Parameter(required=true)
+    private List<String> messageFileNames;
+
+    /**
+     * The path and file name of the log message reference file path which will be copied in
+     * the output directory with generated log reference files.
+     */
+    @Parameter(required=true)
+    private String logMessageReferenceFilePath;
+
+    /**
+     * If the plugin is supposed to overwrite existing generated xml files.
+     */
+    @Parameter(required=true, defaultValue="false")
+    private boolean overwrite;
+
+    /** The end-of-line character for this platform. */
+    public static final String EOL = System.getProperty("line.separator");
+
+    /**
+     * The registry filename is the result of the concatenation of the location
+     * of where the source are generated, the package name and the
+     * DESCRIPTORS_REG value.
+     */
+    private static String REGISTRY_FILE_NAME;
+
+    /**
+     * One-line descriptions for log reference categories.
+     */
+    private static HashMap<String, String> CATEGORY_DESCRIPTIONS;
+
+    static {
+        CATEGORY_DESCRIPTIONS = new HashMap<String, String>();
+        CATEGORY_DESCRIPTIONS.put("ACCESS_CONTROL", "Access Control.");
+        CATEGORY_DESCRIPTIONS.put("ADMIN", "the administration framework.");
+        CATEGORY_DESCRIPTIONS.put("ADMIN_TOOL", "the tool like the offline" + " installer and uninstaller.");
+        CATEGORY_DESCRIPTIONS.put("BACKEND", "generic backends.");
+        CATEGORY_DESCRIPTIONS.put("CONFIG", "configuration handling.");
+        CATEGORY_DESCRIPTIONS.put("CORE", "the core server.");
+        CATEGORY_DESCRIPTIONS.put("DSCONFIG", "the dsconfig administration tool.");
+        CATEGORY_DESCRIPTIONS.put("EXTENSIONS", "server extensions for example,"
+                + " extended operations, SASL mechanisms, password storage"
+                + " schemes, password validators, and so on).");
+        CATEGORY_DESCRIPTIONS.put("JEB", "the JE backend.");
+        CATEGORY_DESCRIPTIONS.put("LOG", "the server loggers.");
+        CATEGORY_DESCRIPTIONS.put("PLUGIN", "plugin processing.");
+        CATEGORY_DESCRIPTIONS.put("PROTOCOL", "connection and protocol handling" + " (for example, ASN.1 and LDAP).");
+        CATEGORY_DESCRIPTIONS.put("QUICKSETUP", "quicksetup tools.");
+        CATEGORY_DESCRIPTIONS.put("RUNTIME_INFORMATION", "the runtime" + " information.");
+        CATEGORY_DESCRIPTIONS.put("SCHEMA", "the server schema elements.");
+        CATEGORY_DESCRIPTIONS.put("SYNC", "replication.");
+        CATEGORY_DESCRIPTIONS.put("TASK", "tasks.");
+        CATEGORY_DESCRIPTIONS.put("THIRD_PARTY", "third-party (including" + " user-defined) modules.");
+        CATEGORY_DESCRIPTIONS.put("TOOLS", "tools.");
+        CATEGORY_DESCRIPTIONS.put("USER_DEFINED", "user-defined modules.");
+        CATEGORY_DESCRIPTIONS.put("UTIL", "the general server utilities.");
+        CATEGORY_DESCRIPTIONS.put("VERSION", "version information.");
+    }
+
+    private static final String DESCRIPTORS_REG = "descriptors.reg";
+
+    /** Message giving formatting rules for string keys. */
+    public static String KEY_FORM_MSG = ".\n\nOpenDJ message property keys must be of the form\n\n"
+            + "\t\'[CATEGORY]_[SEVERITY]_[DESCRIPTION]_[ORDINAL]\'\n\n";
+
+    private static final String ERROR_SEVERITY_IDENTIFIER_STRING = "ERR_";
+    private static final String ERROR_SEVERITY_PRINTABLE = "ERROR";
+
+    /**
+     * Represents a log reference entry for an individual message.
+     */
+    private static class MessageRefEntry implements Comparable<MessageRefEntry> {
+        private Integer ordinal;
+        private String xmlId;
+        private String formatString;
+
+        /**
+         * Build log reference entry for an log message.
+         *
+         * @param msgPropKey
+         * @param category
+         * @param severity
+         * @param ordinal
+         * @param formatString
+         */
+        public MessageRefEntry(final String msgPropKey, final Integer ordinal, final String formatString) {
+            this.formatString = formatString;
+            this.ordinal = ordinal;
+            xmlId = getXmlId(msgPropKey);
+        }
+
+        private String getXmlId(final String messagePropertyKey) {
+            // XML IDs must be unique, and must begin with a letter ([A-Za-z])
+            // and may be followed by any number of letters, digits ([0-9]),
+            // hyphens ("-"), underscores ("_"), colons (":"), and periods
+            // (".").
+
+            final String invalidChars = "[^A-Za-z0-9\\-_:\\.]";
+            return messagePropertyKey.replaceAll(invalidChars, "-");
+        }
+
+        /**
+         * Return a DocBook XML &lt;varlistentry&gt; of this log reference
+         * entry. This implementation copies the message string verbatim, and
+         * does not interpret format specifiers.
+         *
+         * @return DocBook XML &lt;varlistentry&gt;.
+         */
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("  <varlistentry xml:id=\"log-ref-").append(xmlId).append("\">").append(EOL);
+            if (ordinal != null) {
+                builder.append("   <term>ID: ").append(ordinal).append("</term>").append(EOL);
+            }
+            builder.append("   <listitem>").append(EOL);
+            builder.append("    <para>Severity: ").append(ERROR_SEVERITY_PRINTABLE).append("</para>").append(EOL);
+            builder.append("    <para>Message: ").append(formatString).append("</para>").append(EOL);
+            builder.append("   </listitem>").append(EOL);
+            builder.append("  </varlistentry>").append(EOL);
+
+            return builder.toString();
+        }
+
+        /**
+         * Calls {@link #toString()}.
+         */
+        public String toXML() {
+            return toString();
+        }
+
+        /**
+         * Compare message entries by unique identifier.
+         *
+         * @return See {@link java.lang.Comparable#compareTo(Object)}.
+         */
+        public int compareTo(MessageRefEntry mre) {
+            if (this.ordinal == null || mre.ordinal == null) {
+                return 0;
+            } else {
+                return this.ordinal.compareTo(mre.ordinal);
+            }
+        }
+    }
+
+    /** Represents a log reference list of messages for a category. */
+    private static class MessageRefCategory {
+        private String category;
+        private TreeSet<MessageRefEntry> messages;
+
+        MessageRefCategory(final String category, final TreeSet<MessageRefEntry> messages) {
+            this.category = category;
+            this.messages = messages;
+        }
+
+        /**
+         * Return a DocBook XML &lt;variablelist&gt; of this log reference
+         * category.
+         *
+         * @return DocBook XML &lt;variablelist&gt;
+         */
+        @Override
+        public String toString() {
+            StringBuilder entries = new StringBuilder();
+            for (MessageRefEntry entry : messages) {
+                entries.append(entry.toXML());
+            }
+            return getVariablelistHead() + entries + getVariablelistTail();
+        }
+
+        /**
+         * Calls {@link #toString()}.
+         */
+        public String toXML() {
+            return toString();
+        }
+
+        private String getXMLPreamble() {
+            DateFormat df = new SimpleDateFormat("yyyy");
+            Date now = new Date();
+            String year = df.format(now);
+
+            return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL + "<!--" + EOL + "  ! CCPL HEADER START" + EOL
+                    + "  !" + EOL + "  ! This work is licensed under the Creative Commons" + EOL
+                    + "  ! Attribution-NonCommercial-NoDerivs 3.0 Unported License." + EOL
+                    + "  ! To view a copy of this license, visit" + EOL
+                    + "  ! http://creativecommons.org/licenses/by-nc-nd/3.0/" + EOL
+                    + "  ! or send a letter to Creative Commons, 444 Castro Street," + EOL
+                    + "  ! Suite 900, Mountain View, California, 94041, USA." + EOL + "  !" + EOL
+                    + "  ! See the License for the specific language governing permissions" + EOL
+                    + "  ! and limitations under the License." + EOL + "  !" + EOL
+                    + "  ! If applicable, add the following below this CCPL HEADER, with the fields" + EOL
+                    + "  ! enclosed by brackets \"[]\" replaced with your own identifying information:" + EOL
+                    + "  !      Portions Copyright [yyyy] [name of copyright owner]" + EOL + "  !" + EOL
+                    + "  ! CCPL HEADER END" + EOL + "  !" + EOL + "  !      Copyright " + year + " ForgeRock AS"
+                    + EOL + "  !" + EOL + "-->" + EOL;
+        }
+
+        private String getBaseElementAttrs() {
+            return "xmlns='http://docbook.org/ns/docbook'" + " version='5.0' xml:lang='en'"
+                    + " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
+                    + " xsi:schemaLocation='http://docbook.org/ns/docbook"
+                    + " http://docbook.org/xml/5.0/xsd/docbook.xsd'" + " xmlns:xlink='http://www.w3.org/1999/xlink'"
+                    + " xmlns:xinclude='http://www.w3.org/2001/XInclude'";
+        }
+
+        private String getVariablelistHead() {
+            StringBuilder builder = new StringBuilder(getXMLPreamble());
+            builder.append(" <variablelist xml:id=\"log-ref-").append(this.category).append("\" ")
+                   .append(getBaseElementAttrs()).append(">").append(EOL)
+                   .append("  <title>Log Message Category: ").append(category).append("</title>").append(EOL);
+
+            return builder.toString();
+        }
+
+        private String getVariablelistTail() {
+            return " </variablelist>" + EOL;
+        }
+
+    }
+
+    private static class MessagePropertyKey implements Comparable<MessagePropertyKey> {
+        private String description;
+        private Integer ordinal;
+
+        /**
+         * Creates a message property key from a string value.
+         *
+         * @param keyString3
+         *            from properties file
+         * @return MessagePropertyKey created from string
+         */
+        public static MessagePropertyKey parseString(String key) {
+            String description;
+            Integer ordinal = null;
+
+            int li = key.lastIndexOf("_");
+            if (li != -1) {
+                description = key.substring(0, li).toUpperCase();
+            } else {
+                throw new IllegalArgumentException("Incorrectly formatted key " + key);
+            }
+
+            try {
+                String ordString = key.substring(li + 1);
+                ordinal = Integer.parseInt(ordString);
+            } catch (Exception nfe) {
+                // Ignore exception, the message has no ordinal.
+            }
+            return new MessagePropertyKey(description, ordinal);
+        }
+
+        /**
+         * Creates a parameterized instance.
+         *
+         * @param description
+         *            of this key
+         * @param ordinal
+         *            of this key
+         */
+        public MessagePropertyKey(String description, Integer ordinal) {
+            this.description = description;
+            this.ordinal = ordinal;
+        }
+
+        /**
+         * Gets the ordinal of this key.
+         *
+         * @return ordinal of this key
+         */
+        public Integer getOrdinal() {
+            return this.ordinal;
+        }
+
+        /** {@inheritDoc} */
+        public String toString() {
+            StringBuilder builder = new StringBuilder(description);
+            if (ordinal != null) {
+                builder.append("_").append(ordinal);
+            }
+
+            return builder.toString();
+        }
+
+        /** {@inheritDoc} */
+        public int compareTo(MessagePropertyKey k) {
+            if (ordinal == k.ordinal) {
+                return description.compareTo(k.description);
+            } else {
+                return ordinal.compareTo(k.ordinal);
+            }
+        }
+
+    }
+
+    /**
+     * For maven exec plugin execution. Generates for all included message files
+     * (sample.properties), a xml log ref file (log-ref-sample.xml)
+     *
+     * @throws Exception
+     */
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        String projectBuildDir = project.getBuild().getDirectory();
+
+        if (!outputDirectory.contains(projectBuildDir)) {
+          String errorMsg = String.format("outputDirectory parameter (%s) must be included "
+              + "in ${project.build.directory} (%s)", outputDirectory, projectBuildDir);
+          getLog().error(errorMsg);
+          throw new MojoExecutionException(errorMsg);
+        }
+
+        for (String messageFileName : messageFileNames) {
+            File source = new File(messagesDirectory, messageFileName + ".properties");
+            File dest = new File(outputDirectory, "log-ref-" + messageFileName.replace("_", "-") + ".xml");
+            try {
+                generateLogReferenceFile(source, dest, messageFileName.toUpperCase());
+            } catch (MojoExecutionException e) {
+                getLog().error("Impossible to generate " + dest.getAbsolutePath() + ": " + e.getMessage());
+                throw e;
+            }
+        }
+        copyLogMessageReferenceFile();
+    }
+
+
+    private void generateLogReferenceFile(File source, File dest, String globalCategory) throws MojoExecutionException {
+        PrintWriter destWriter = null;
+        try {
+            // Decide whether to generate messages based on modification times
+            // and print status messages.
+            if (!source.exists()) {
+                throw new Exception("file " + source.getName() + " does not exist");
+            }
+            if (!isOverwriteNeeded(source, dest)) {
+                return;
+            }
+
+            destWriter = new PrintWriter(dest, "UTF-8");
+            Properties properties = new Properties();
+            properties.load(new FileInputStream(source));
+            Map<MessagePropertyKey, String> errorMessages = loadErrorProperties(properties);
+            TreeSet<MessageRefEntry> messageRefEntries = new TreeSet<MessageRefEntry>();
+            Set<Integer> usedOrdinals = new HashSet<Integer>();
+
+            for (MessagePropertyKey msgKey : errorMessages.keySet()) {
+                String formatString = errorMessages.get(msgKey).replaceAll("<", "&lt;");
+                Integer ordinal = msgKey.getOrdinal();
+                if (ordinal != null && usedOrdinals.contains(ordinal)) {
+                    throw new Exception("The ordinal value \'" + ordinal + "\' in key " + msgKey
+                            + " has been previously defined in " + source + KEY_FORM_MSG);
+                }
+                usedOrdinals.add(ordinal);
+                messageRefEntries.add(new MessageRefEntry(msgKey.toString(), msgKey.getOrdinal(), formatString));
+            }
+
+            destWriter.println(messageRefEntries.isEmpty() ?
+                            "<!-- No message for this category -->"
+                          : new MessageRefCategory(globalCategory, messageRefEntries).toXML());
+            getLog().info(dest.getPath() + " has been successfully generated");
+            getLog().debug("Message Generated: " + errorMessages.size());
+        } catch (Exception e) {
+            // Delete malformed file.
+            if (dest.exists()) {
+                dest.deleteOnExit();
+            }
+            throw new MojoExecutionException(e.getMessage());
+        } finally {
+            Utils.closeSilently(destWriter);
+        }
+    }
+
+
+    private Map<MessagePropertyKey, String> loadErrorProperties(Properties properties) throws Exception {
+        Map<MessagePropertyKey, String> errorMessage = new TreeMap<MessagePropertyKey, String>();
+        for (Object propO : properties.keySet()) {
+            String propKey = propO.toString();
+            try {
+                // Document only ERROR messages.
+                if (propKey.startsWith(ERROR_SEVERITY_IDENTIFIER_STRING)) {
+                    MessagePropertyKey key = MessagePropertyKey.parseString(propKey);
+                    String formatString = properties.getProperty(propKey);
+                    errorMessage.put(key, formatString);
+                }
+            } catch (IllegalArgumentException iae) {
+                throw new Exception("invalid property key " + propKey + ": " + iae.getMessage() + KEY_FORM_MSG);
+            }
+        }
+
+        return errorMessage;
+    }
+
+
+    private boolean isOverwriteNeeded(File source, File dest) {
+        boolean needsOverwrite = this.overwrite || source.lastModified() > dest.lastModified();
+        if (dest.exists() && needsOverwrite) {
+            dest.delete();
+            getLog().info("Regenerating " + dest.getName() + " from " + source.getName());
+        } else if (dest.exists() && !needsOverwrite) {
+            // Fail fast - nothing to do.
+            getLog().info(dest.getName() + " is up to date");
+            return false;
+        } else {
+            File javaGenDir = dest.getParentFile();
+            if (!javaGenDir.exists()) {
+                javaGenDir.mkdirs();
+            }
+        }
+
+        return true;
+    }
+
+    private void copyLogMessageReferenceFile() throws MojoExecutionException {
+        File msgReferenceSourceFile = new File(logMessageReferenceFilePath);
+        File msgReferenceDestFile = new File(outputDirectory, msgReferenceSourceFile.getName());
+        if (!isOverwriteNeeded(msgReferenceSourceFile, msgReferenceDestFile)) {
+            return;
+        }
+        InputStream input = null;
+        OutputStream output = null;
+        try {
+            input = new FileInputStream(msgReferenceSourceFile);
+            output = new FileOutputStream(msgReferenceDestFile);
+            byte[] buf = new byte[1024];
+            int bytesRead;
+            while ((bytesRead = input.read(buf)) > 0) {
+                output.write(buf, 0, bytesRead);
+            }
+            getLog().info("log message reference file has been successfully generated");
+        } catch (Exception e) {
+            throw new MojoExecutionException("Impossible to copy log reference message file into output directory: "
+                    + e.getMessage());
+        } finally {
+            Utils.closeSilently(input, output);
+        }
+    }
+
+    /**
+     * Sets the file that will be generated containing declarations of messages
+     * from <code>source</code>.
+     *
+     * @param dest
+     *            File destination
+     */
+    public void checkDestJava(File dest) throws Exception {
+        File descriptorsRegFile = new File(dest.getParentFile(), DESCRIPTORS_REG);
+
+        if (REGISTRY_FILE_NAME != null) {
+            // if REGISTRY_FILE_NAME is already set, ensure that we computed the
+            // same one
+            File prevDescriptorsRegFile = new File(REGISTRY_FILE_NAME);
+            if (!prevDescriptorsRegFile.equals(descriptorsRegFile)) {
+                throw new Exception("Error processing " + dest
+                        + ": all messages must be located in the same package thus "
+                        + "name of the source file should be "
+                        + new File(prevDescriptorsRegFile.getParent(), dest.getName()));
+            }
+        } else {
+            REGISTRY_FILE_NAME = descriptorsRegFile.getCanonicalPath();
+        }
+    }
+
+}
diff --git a/opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/package-info.java b/opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/package-info.java
new file mode 100644
index 0000000..7840667
--- /dev/null
+++ b/opendj-logref-doc-maven-plugin/src/main/java/org/forgerock/maven/package-info.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/** Classes implementing the maven plugin to create a base schema that may be used during the upgrade process. */
+package org.forgerock.maven;
+

--
Gitblit v1.10.0