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