/* * 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 2013 ForgeRock AS. */ package org.forgerock.opendj.maven; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FilenameUtils; import org.apache.maven.model.Dependency; import org.apache.maven.model.Plugin; import org.apache.maven.plugin.MojoExecutionException; import org.twdata.maven.mojoexecutor.MojoExecutor; // @Checkstyle:off /** * Generate configuration classes from XML definition files for OpenDJ server. *

* There is a single goal that generate java sources, manifest files, I18N messages * and cli/ldap profiles. * * @goal generate * @phase generate-sources * @requiresDependencyResolution compile+runtime */ // @Checkstyle:on public class OpendjConfigMojo extends AbstractBuildMojo { /** * Package path in which artefacts are generated. *

* This relative path is used to locate xml definition files and to locate * generated artefacts. * * @parameter default-value="org/forgerock/opendj/admin" * @required */ private String packagePath; /** * Root directory where definitions of configuration as xml files are * located. * * @parameter default-value="${basedir}/src/main/resources/definitions" * @required */ private String xmlDefinitionsRootDir; /** * Directory containing stylesheets (xsl files) to generate code and other * artefacts. * * @parameter default-value="${basedir}/src/main/resources/stylesheets" * @required */ private String xslDir; /** * Root directory containing generated sources for configuration. * * @parameter default-value="${project.build.directory}/generated-sources/admin" * @required */ private String generatedSourcesRootDir; /** * Temporary directory containing generated manifests. *

* There is one manifest file generated by component. It is the * responsability of project that use this plugin to concatenate all * generated files into a single file. * * @parameter default-value="${project.build.directory}/tmp" * @required */ private String generatedManifestsTempDir; /** * Root directory containing generated messages for configuration. * * @parameter default-value="${basedir}/src/main/resources/admin/messages" * @required */ private String generatedMessagesRootDir; /** * Root directory containing generated profiles for configuration. * * @parameter default-value="${project.build.outputDirectory}/admin/profiles" * @required */ private String generatedProfilesRootDir; /** * Returns the package path in which artefacts are generated. *

* The package path is a relative path. * * @return the package path */ public final String getPackagePath() { return packagePath; } /** * Returns the root directory for xml configuration files. * * @return {@code xmlDefinitionsRootDir} */ public final File getXmlDefinitionsRootDirectory() { return new File(xmlDefinitionsRootDir); } /** * Returns the directory containing xml configuration files. * * @return the directory containing xml configuration files */ public final File getXmlDefinitionsDirectory() { return new File(getXmlDefinitionsRootDirectory(), packagePath); } /** * Returns the directory containing xsl files to generate code. * * @return {@code xslDir} */ public final File getXslDirectory() { return new File(xslDir); } /** * Returns the temporary directory containing generated manifests. * * @return {@code manifestsTempDir} */ public final String getGeneratedManifestsTempDirectory() { return generatedManifestsTempDir; } /** * Returns the directory containing generated sources for configuration. * * @return {@code generatedSourcesDir} */ public final File getGeneratedSourcesDirectory() { return new File(generatedSourcesRootDir, getPackagePath()); } /** * Returns the directory containing generated messages for configuration. * * @return the directory containing generated messages */ public final File getGeneratedMessagesDirectory() { return new File(generatedMessagesRootDir, getPackagePath() + "/" + "meta"); } /** * Returns the directory containing generated profile for configuration. * * @param profileType * The type of profile (ldap or cli) * * @return the directory containing generated profiles for configuration */ public final File getGeneratedProfilesDirectory(String profileType) { return new File(generatedProfilesRootDir, profileType + "/" + getPackagePath() + "/" + "meta"); } /** * Version of xml-maven-plugin to use. * * @parameter default-value="1.0" * @required */ private String xmlMavenPluginVersion; /** * Returns the version of xml-maven-plugin. * * @return {@code xmlMavenPluginVersion} */ public final String getXmlMavenPluginVersion() { return xmlMavenPluginVersion; } /** * {@inheritDoc} */ @Override public final void execute() throws MojoExecutionException { //checkDirectories(); Executor exec = new Executor(); getLog().info("Validate XML definition files"); exec.validateXmlDefinitionsFiles(); getLog().info("Generate meta, server and client APIs for components..."); exec.generateSourcesAndManifests(); getLog().info("Generate I18N messages and profiles..."); exec.generateMessagesAndProfiles(); } void checkDirectories() throws MojoExecutionException { if (getPackagePath() == null) { throw new MojoExecutionException(" must be set."); } if (getXmlDefinitionsDirectory() == null || !getXmlDefinitionsDirectory().exists()) { throw new MojoExecutionException(" and must be set and concatenation" + "of the two (with / separator) must correspond to an existing directory."); } if (getXslDirectory() == null || !getXslDirectory().exists()) { throw new MojoExecutionException(" must be set and must correspond to an existing directory."); } } /** * Execute xml maven plugin with several executions and configurations. */ private class Executor extends MojoExecutor { private static final String SAXON_LIBRARY_VERSION = "6.5.3"; private static final String FILE_EXTENSION_MAPPER = "org.codehaus.plexus.components.io.filemappers.FileExtensionMapper"; private static final String REGEXP_FILE_MAPPER = "org.codehaus.plexus.components.io.filemappers.RegExpFileMapper"; private static final String CLIENT = "client"; private static final String SERVER = "server"; private static final String META = "meta"; private static final String PROPERTIES_FILE_REPLACEMENT_PATTERN = "$1CfgDefn.properties"; /** Pattern to identify a xml definition file for java package. */ private static final String XML_CONFIG_PACKAGE_FILE_PATTERN = "^Package\\.xml$"; /** Pattern to identify a xml definition file for java class. */ private static final String XML_CONFIG_FILE_PATTERN = "^([^/]+)Configuration\\.xml$"; /** * Generate sources for configuration. */ void generateSourcesAndManifests() throws MojoExecutionException { executeMojo( xmlPlugin(), goal("transform"), configuration( element(name("transformationSets"), transformationSetForClasses(META, "$1CfgDefn.java"), transformationSetForPackage(META), transformationSetForClasses(SERVER, "$1Cfg.java"), transformationSetForPackage(SERVER), transformationSetForClasses(CLIENT, "$1CfgClient.java"), transformationSetForPackage(CLIENT), transformationSetForManifests() )), executionEnvironment(getProject(), getSession(), getPluginManager())); } /** * Validate xml configuration files. */ void validateXmlDefinitionsFiles() throws MojoExecutionException { executeMojo( xmlPlugin(), goal("validate"), configuration( element(name("validationSets"), element(name("validationSet"), directoryElement(), element(name("systemId"), filePath(getXslDirectory()) + "/" + "admin.xsd"))), element(name("catalogs"), element(name("catalog"), filePath(getXslDirectory()) + "/" + "catalog.xml"))), executionEnvironment(getProject(), getSession(), getPluginManager())); } /** * Generate I18N messages and LDAP/CLI profiles for configuration. */ void generateMessagesAndProfiles() throws MojoExecutionException { executeMojo(xmlPlugin(), goal("transform"), configuration( element(name("transformationSets"), transformationSetForResource(filePath(getGeneratedMessagesDirectory()), "messagesMO.xsl"), transformationSetForResource(filePath(getGeneratedProfilesDirectory("ldap")), "ldapMOProfile.xsl"), transformationSetForResource(filePath(getGeneratedProfilesDirectory("cli")), "cliMOProfile.xsl") )), executionEnvironment(getProject(), getSession(), getPluginManager())); } private Plugin xmlPlugin() { Dependency xsltLibrary = new Dependency(); xsltLibrary.setGroupId("saxon"); xsltLibrary.setArtifactId("saxon"); xsltLibrary.setVersion(SAXON_LIBRARY_VERSION); List deps = new ArrayList(); deps.add(xsltLibrary); return plugin(groupId("org.codehaus.mojo"), artifactId("xml-maven-plugin"), version(getXmlMavenPluginVersion()), deps); } private MojoExecutor.Element transformationSetForClasses(String type, String patternReplacement) { String xslFileName = type + "MO.xsl"; return transformationSetForSource(type, xslFileName, packageElement(false), XML_CONFIG_FILE_PATTERN, patternReplacement, baseDirParameter()); } private MojoExecutor.Element transformationSetForPackage(String type) { return transformationSetForSource(type, "package-info.xsl", packageElement(true), XML_CONFIG_PACKAGE_FILE_PATTERN, "package-info.java", typeParameter(type)); } /** * Returns a transformation set element for source code. */ private MojoExecutor.Element transformationSetForSource(String finalOutputDir, String xslName, MojoExecutor.Element packageElement, String pattern, String patternReplacement, MojoExecutor.Element parameter) { return element(name("transformationSet"), directoryElement(), outputDirElement(filePath(getGeneratedSourcesDirectory()) + "/" + finalOutputDir), styleSheetElement(xslName), packageElement, element(name("fileMappers"), element(name("fileMapper"), attribute("implementation", REGEXP_FILE_MAPPER), element(name("pattern"), pattern), element(name("replacement"), patternReplacement))), element(name("parameters"), parameter)); } /** * Returns a transformation set element for manifests. */ private MojoExecutor.Element transformationSetForManifests() { return element(name("transformationSet"), directoryElement(), outputDirElement(getGeneratedManifestsTempDirectory()), styleSheetElement("manifestMO.xsl"), packageElement(false), element(name("fileMappers"), element(name("fileMapper"), attribute("implementation", FILE_EXTENSION_MAPPER), element(name("targetExtension"), ".manifest")))); } /** * Returns a transformation set element for source code. */ private MojoExecutor.Element transformationSetForResource(String outputDir, String xslName) { return element(name("transformationSet"), directoryElement(), outputDirElement(outputDir), styleSheetElement(xslName), packageElement(false), element(name("fileMappers"), element(name("fileMapper"), attribute("implementation", REGEXP_FILE_MAPPER), element(name("pattern"), XML_CONFIG_FILE_PATTERN), element(name("replacement"), PROPERTIES_FILE_REPLACEMENT_PATTERN))), element(name("parameters"), baseDirParameter())); } private Element outputDirElement(String outputDir) { return element(name("outputDir"), outputDir); } private Element styleSheetElement(String xslName) { return element(name("stylesheet"), filePath(getXslDirectory()) + "/" + xslName); } private Element directoryElement() { return element(name("dir"), filePath(getXmlDefinitionsDirectory())); } private MojoExecutor.Element baseDirParameter() { return element(name("parameter"), element(name("name"), "base-dir"), element(name("value"), filePath(getXmlDefinitionsRootDirectory()))); } private MojoExecutor.Element typeParameter(String value) { return element(name("parameter"), element(name("name"), "type"), element(name("value"), value)); } /** Package element either include "package.xml" file or exclude it. */ private MojoExecutor.Element packageElement(boolean include) { String tag = include ? "include" : "exclude"; String pluralTag = tag + "s"; return element(name(pluralTag), element(name(tag), "Package.xml")); } /** Returns the string path from the provided file. */ private String filePath(File file) { return FilenameUtils.separatorsToUnix(file.getPath()); } } }