/* * 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 java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.LinkedList; import java.util.List; 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; /** * Concatenates the contents of the files in the schema directory to create a * base schema that may be used during the upgrade process. Each element will * also include the X-SCHEMA-FILE extension to indicate the source schema file. * <p> * There is a single goal that generates the base schema. * <p> */ @Mojo(name = "concat", defaultPhase = LifecyclePhase.GENERATE_SOURCES) public final class ConcatSchemaMojo extends AbstractMojo { /** * The Maven Project. */ @Parameter(property = "project", required = true, readonly = true) private MavenProject project; /** * The path to the directory containing the schema files. */ @Parameter(required = true, defaultValue = "${basedir}/resource/schema") private String schemaDirectory; /** * The directory path of the concatenated schema file to create. Must be in ${project.build.directory} */ @Parameter(required = true) private String outputDirectory; /** * The file name of the concatenated schema file to create. */ @Parameter(required = true) private String outputFile; /** {@inheritDoc} */ @Override public void execute() throws MojoExecutionException, MojoFailureException { String projectBuildDir = project.getBuild().getDirectory(); String outputFilePath = outputDirectory + System.getProperty("file.separator") + outputFile; 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); } getLog().info(String.format("Concatenating all ldif files from directory: %s", schemaDirectory)); getLog().info(String.format("Concatenated file: %s", outputFilePath)); new File(outputFilePath).getParentFile().mkdirs(); // Get a sorted list of the files in the schema directory. TreeSet<String> schemaFileNames = new TreeSet<>(); for (File f : new File(schemaDirectory).listFiles()) { if (f.isFile()) { schemaFileNames.add(f.getName()); } } // Create a set of lists that will hold the schema elements read from the files. LinkedList<String> attributeTypes = new LinkedList<>(); LinkedList<String> objectClasses = new LinkedList<>(); LinkedList<String> nameForms = new LinkedList<>(); LinkedList<String> ditContentRules = new LinkedList<>(); LinkedList<String> ditStructureRules = new LinkedList<>(); LinkedList<String> matchingRuleUses = new LinkedList<>(); LinkedList<String> ldapSyntaxes = new LinkedList<>(); int curLineNumber = 0; // Open each of the files in order and read the elements that they contain, // appending them to the appropriate lists. for (String name : schemaFileNames) { // Read the contents of the file into a list with one schema element per // list element. LinkedList<StringBuilder> lines = new LinkedList<>(); try { BufferedReader reader = new BufferedReader(new FileReader(new File(schemaDirectory, name))); String line = reader.readLine(); while (line != null) { curLineNumber++; if (line.length() > 0 && !line.startsWith("#")) { if (line.startsWith(" ")) { lines.getLast().append(line.substring(1)); } else { lines.add(new StringBuilder(line)); } } line = reader.readLine(); } reader.close(); } catch (Exception e) { getLog().error(String.format( "Error while reading schema file %s at line %d: %s", name, curLineNumber, e.getMessage())); throw new MojoExecutionException(e.getMessage(), e); } // Iterate through each line in the list. Find the colon and get the // attribute name at the beginning. If it's someting that we don't // recognize, then skip it. Otherwise, add the X-SCHEMA-FILE extension // and add it to the appropriate schema element list. for (StringBuilder buffer : lines) { // Get the line and add the X-SCHEMA-FILE extension to the end of it. // All of them should end with " )" but some might have the parenthesis // crammed up against the last character so deal with that as well. String line = buffer.toString().trim(); if (line.endsWith(" )")) { line = line.substring(0, line.length() - 1) + "X-SCHEMA-FILE '" + name + "' )"; } else if (line.endsWith(")")) { line = line.substring(0, line.length() - 1) + " X-SCHEMA-FILE '" + name + "' )"; } else { continue; } String lowerLine = line.toLowerCase(); if (lowerLine.startsWith("attributetypes:")) { attributeTypes.add(line); } else if (lowerLine.startsWith("objectclasses:")) { objectClasses.add(line); } else if (lowerLine.startsWith("nameforms:")) { nameForms.add(line); } else if (lowerLine.startsWith("ditcontentrules:")) { ditContentRules.add(line); } else if (lowerLine.startsWith("ditstructurerules:")) { ditStructureRules.add(line); } else if (lowerLine.startsWith("matchingruleuse:")) { matchingRuleUses.add(line); } else if (lowerLine.startsWith("ldapsyntaxes:")) { ldapSyntaxes.add(line); } } } // Write the resulting output to the merged schema file. try { BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath)); writer.write("dn: cn=schema"); writer.newLine(); writer.write("objectClass: top"); writer.newLine(); writer.write("objectClass: ldapSubentry"); writer.newLine(); writer.write("objectClass: subschema"); writer.newLine(); writeSchemaElements(ldapSyntaxes, writer); writeSchemaElements(attributeTypes, writer); writeSchemaElements(objectClasses, writer); writeSchemaElements(nameForms, writer); writeSchemaElements(ditContentRules, writer); writeSchemaElements(ditStructureRules, writer); writeSchemaElements(matchingRuleUses, writer); writer.close(); } catch (Exception e) { getLog().error( String.format("Error while writing concatenated schema file %s: %s", outputFile, e.getMessage())); throw new MojoExecutionException(e.getMessage(), e); } } private void writeSchemaElements(List<String> schemaElements, BufferedWriter writer) throws IOException { for (String line : schemaElements) { writer.write(line); writer.newLine(); } } }