001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015 ForgeRock AS. 015 */ 016package org.forgerock.opendj.maven; 017 018import java.io.BufferedReader; 019import java.io.BufferedWriter; 020import java.io.File; 021import java.io.FileReader; 022import java.io.FileWriter; 023import java.io.IOException; 024import java.util.LinkedList; 025import java.util.List; 026import java.util.TreeSet; 027 028import org.apache.maven.plugin.AbstractMojo; 029import org.apache.maven.plugin.MojoExecutionException; 030import org.apache.maven.plugin.MojoFailureException; 031import org.apache.maven.plugins.annotations.LifecyclePhase; 032import org.apache.maven.plugins.annotations.Mojo; 033import org.apache.maven.plugins.annotations.Parameter; 034import org.apache.maven.project.MavenProject; 035 036/** 037 * Concatenates the contents of the files in the schema directory to create a 038 * base schema that may be used during the upgrade process. Each element will 039 * also include the X-SCHEMA-FILE extension to indicate the source schema file. 040 * <p> 041 * There is a single goal that generates the base schema. 042 * <p> 043 */ 044@Mojo(name = "concat", defaultPhase = LifecyclePhase.GENERATE_SOURCES) 045public final class ConcatSchemaMojo extends AbstractMojo { 046 047 /** 048 * The Maven Project. 049 */ 050 @Parameter(property = "project", required = true, readonly = true) 051 private MavenProject project; 052 053 /** 054 * The path to the directory containing the schema files. 055 */ 056 @Parameter(required = true, defaultValue = "${basedir}/resource/schema") 057 private String schemaDirectory; 058 059 /** 060 * The directory path of the concatenated schema file to create. Must be in ${project.build.directory} 061 */ 062 @Parameter(required = true) 063 private String outputDirectory; 064 065 /** 066 * The file name of the concatenated schema file to create. 067 */ 068 @Parameter(required = true) 069 private String outputFile; 070 071 /** {@inheritDoc} */ 072 @Override 073 public void execute() throws MojoExecutionException, MojoFailureException { 074 String projectBuildDir = project.getBuild().getDirectory(); 075 String outputFilePath = outputDirectory + System.getProperty("file.separator") + outputFile; 076 077 if (!outputDirectory.contains(projectBuildDir)) { 078 String errorMsg = String.format("outputDirectory parameter (%s) must be included " 079 + "in ${project.build.directory} (%s)", outputDirectory, projectBuildDir); 080 getLog().error(errorMsg); 081 throw new MojoExecutionException(errorMsg); 082 } 083 getLog().info(String.format("Concatenating all ldif files from directory: %s", schemaDirectory)); 084 getLog().info(String.format("Concatenated file: %s", outputFilePath)); 085 086 new File(outputFilePath).getParentFile().mkdirs(); 087 088 // Get a sorted list of the files in the schema directory. 089 TreeSet<String> schemaFileNames = new TreeSet<>(); 090 for (File f : new File(schemaDirectory).listFiles()) { 091 if (f.isFile()) { 092 schemaFileNames.add(f.getName()); 093 } 094 } 095 096 // Create a set of lists that will hold the schema elements read from the files. 097 LinkedList<String> attributeTypes = new LinkedList<>(); 098 LinkedList<String> objectClasses = new LinkedList<>(); 099 LinkedList<String> nameForms = new LinkedList<>(); 100 LinkedList<String> ditContentRules = new LinkedList<>(); 101 LinkedList<String> ditStructureRules = new LinkedList<>(); 102 LinkedList<String> matchingRuleUses = new LinkedList<>(); 103 LinkedList<String> ldapSyntaxes = new LinkedList<>(); 104 int curLineNumber = 0; 105 106 // Open each of the files in order and read the elements that they contain, 107 // appending them to the appropriate lists. 108 for (String name : schemaFileNames) { 109 // Read the contents of the file into a list with one schema element per 110 // list element. 111 LinkedList<StringBuilder> lines = new LinkedList<>(); 112 try { 113 BufferedReader reader = new BufferedReader(new FileReader(new File(schemaDirectory, name))); 114 String line = reader.readLine(); 115 while (line != null) { 116 curLineNumber++; 117 if (line.length() > 0 && !line.startsWith("#")) { 118 if (line.startsWith(" ")) { 119 lines.getLast().append(line.substring(1)); 120 } else { 121 lines.add(new StringBuilder(line)); 122 } 123 } 124 line = reader.readLine(); 125 } 126 reader.close(); 127 } catch (Exception e) { 128 getLog().error(String.format( 129 "Error while reading schema file %s at line %d: %s", name, curLineNumber, e.getMessage())); 130 throw new MojoExecutionException(e.getMessage(), e); 131 } 132 133 // Iterate through each line in the list. Find the colon and get the 134 // attribute name at the beginning. If it's someting that we don't 135 // recognize, then skip it. Otherwise, add the X-SCHEMA-FILE extension 136 // and add it to the appropriate schema element list. 137 for (StringBuilder buffer : lines) { 138 // Get the line and add the X-SCHEMA-FILE extension to the end of it. 139 // All of them should end with " )" but some might have the parenthesis 140 // crammed up against the last character so deal with that as well. 141 String line = buffer.toString().trim(); 142 if (line.endsWith(" )")) { 143 line = line.substring(0, line.length() - 1) + "X-SCHEMA-FILE '" + name + "' )"; 144 } else if (line.endsWith(")")) { 145 line = line.substring(0, line.length() - 1) + " X-SCHEMA-FILE '" + name + "' )"; 146 } else { 147 continue; 148 } 149 150 String lowerLine = line.toLowerCase(); 151 if (lowerLine.startsWith("attributetypes:")) { 152 attributeTypes.add(line); 153 } else if (lowerLine.startsWith("objectclasses:")) { 154 objectClasses.add(line); 155 } else if (lowerLine.startsWith("nameforms:")) { 156 nameForms.add(line); 157 } else if (lowerLine.startsWith("ditcontentrules:")) { 158 ditContentRules.add(line); 159 } else if (lowerLine.startsWith("ditstructurerules:")) { 160 ditStructureRules.add(line); 161 } else if (lowerLine.startsWith("matchingruleuse:")) { 162 matchingRuleUses.add(line); 163 } else if (lowerLine.startsWith("ldapsyntaxes:")) { 164 ldapSyntaxes.add(line); 165 } 166 } 167 } 168 169 // Write the resulting output to the merged schema file. 170 try { 171 BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath)); 172 writer.write("dn: cn=schema"); 173 writer.newLine(); 174 writer.write("objectClass: top"); 175 writer.newLine(); 176 writer.write("objectClass: ldapSubentry"); 177 writer.newLine(); 178 writer.write("objectClass: subschema"); 179 writer.newLine(); 180 181 writeSchemaElements(ldapSyntaxes, writer); 182 writeSchemaElements(attributeTypes, writer); 183 writeSchemaElements(objectClasses, writer); 184 writeSchemaElements(nameForms, writer); 185 writeSchemaElements(ditContentRules, writer); 186 writeSchemaElements(ditStructureRules, writer); 187 writeSchemaElements(matchingRuleUses, writer); 188 189 writer.close(); 190 } catch (Exception e) { 191 getLog().error( 192 String.format("Error while writing concatenated schema file %s: %s", outputFile, e.getMessage())); 193 throw new MojoExecutionException(e.getMessage(), e); 194 } 195 } 196 197 private void writeSchemaElements(List<String> schemaElements, BufferedWriter writer) throws IOException { 198 for (String line : schemaElements) { 199 writer.write(line); 200 writer.newLine(); 201 } 202 } 203 204}