All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.unboundidds.tools.CompareLDAPSchemas Maven / Gradle / Ivy

/*
 * Copyright 2024 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2024 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2024 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.unboundidds.tools;



import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.RootDSE;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.ldap.sdk.schema.AttributeSyntaxDefinition;
import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
import com.unboundid.ldap.sdk.schema.DITContentRuleDefinition;
import com.unboundid.ldap.sdk.schema.DITStructureRuleDefinition;
import com.unboundid.ldap.sdk.schema.MatchingRuleDefinition;
import com.unboundid.ldap.sdk.schema.MatchingRuleUseDefinition;
import com.unboundid.ldap.sdk.schema.NameFormDefinition;
import com.unboundid.ldap.sdk.schema.ObjectClassDefinition;
import com.unboundid.ldap.sdk.schema.Schema;
import com.unboundid.ldap.sdk.unboundidds.controls.
            ExtendedSchemaInfoRequestControl;
import com.unboundid.util.Debug;
import com.unboundid.util.MultiServerLDAPCommandLineTool;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.OID;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.BooleanArgument;
import com.unboundid.util.args.DNArgument;
import com.unboundid.util.args.StringArgument;

import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*;



/**
 * This class implements a command-line tool that can be used to retrieve the
 * schemas from two LDAP servers and identify any differences between them.
 * 
*
* NOTE: This class, and other classes within the * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only * supported for use against Ping Identity, UnboundID, and * Nokia/Alcatel-Lucent 8661 server products. These classes provide support * for proprietary functionality or for external specifications that are not * considered stable or mature enough to be guaranteed to work in an * interoperable way with other types of LDAP servers. *
*
* Comparisons that this tool may perform include: *
    *
  • Definitions that are present in one server but not another.
  • *
  • Corresponding definitions with the same OID but different names or sets * of names.
  • *
  • Corresponding definitions with different descriptions, obsolete state, * or sets of extensions.
  • *
  • Corresponding attribute types with differences in syntaxes, matching * rules, superior type, single-valued/multivalued behavior, usage, * collective state, or NO-USER-MODIFICATION state.
  • *
  • Corresponding object classes with differences in required or optional * attributes, superior class, or object class type.
  • *
  • Corresponding DIT content rules with differences in required, optional, * or prohibited attributes, or allowed auxiliary classes.
  • *
  • Corresponding name forms with differences in structural class, * required attributes, or optional attributes.
  • *
  • Corresponding DIT structure rules with different name form IDs or * superior rule IDs.
  • *
  • Corresponding matching rule uses with different sets of applicable * attribute types.
  • *
*/ @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class CompareLDAPSchemas extends MultiServerLDAPCommandLineTool { /** * The column at which long lines should be wrapped. */ private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; /** * The index number used to reference the first server. */ private static final int FIRST_SERVER_INDEX = 0; /** * The index number used to reference the second server. */ private static final int SECOND_SERVER_INDEX = 1; /** * The name of the command-line argument used to indicate that the tool should * not examine schema elements that have an extension with a given name and * value. */ @NotNull private static final String ARG_NAME_EXCLUDE_ELEMENTS_WITH_EXTENSION_VALUE = "excludeElementsWithExtensionValue"; /** * The name of the command-line argument used to indicate that the tool should * not examine schema elements with names matching a specified prefix. */ @NotNull private static final String ARG_NAME_EXCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX = "excludeElementsWithNameMatchingPrefix"; /** * The name of the command-line argument used to specify the DN of the first * server's subschema subentry. */ @NotNull private static final String ARG_NAME_FIRST_SCHEMA_ENTRY_DN = "firstSchemaEntryDN"; /** * The name of the command-line argument used to indicate that the tool should * use the get extended schema info request control if the server reports that * it is supported. */ @NotNull private static final String ARG_NAME_GET_EXTENDED_SCHEMA_INFO = "getExtendedSchemaInfo"; /** * The name of the command-line argument used to indicate that the tool should * ignore differences in element descriptions. */ @NotNull private static final String ARG_NAME_IGNORE_DESCRIPTIONS = "ignoreDescriptions"; /** * The name of the command-line argument used to indicate that the tool should * ignore differences in element extensions. */ @NotNull private static final String ARG_NAME_IGNORE_EXTENSIONS = "ignoreExtensions"; /** * The name of the command-line argument used to indicate that the tool should * only examine schema elements that have an extension with a given name and * value. */ @NotNull private static final String ARG_NAME_INCLUDE_ELEMENTS_WITH_EXTENSION_VALUE = "includeElementsWithExtensionValue"; /** * The name of the command-line argument used to indicate that the tool should * only examine schema elements with names matching a specified prefix. */ @NotNull private static final String ARG_NAME_INCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX = "includeElementsWithNameMatchingPrefix"; /** * The name of the command-line argument used to specify the types of schema * elements that the server should examine. */ @NotNull private static final String ARG_NAME_SCHEMA_ELEMENT_TYPE = "schemaElementType"; /** * The name of the command-line argument used to specify the DN of the second * server's subschema subentry. */ @NotNull private static final String ARG_NAME_SECOND_SCHEMA_ENTRY_DN = "secondSchemaEntryDN"; /** * The name of the schema element type value that indicates that the tool * should examine attribute syntaxes. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES = "attribute-syntaxes"; /** * The name of the schema element type value that indicates that the tool * should examine attribute types. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES = "attribute-types"; /** * The name of the schema element type value that indicates that the tool * should examine DIT content rules. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES = "dit-content-rules"; /** * The name of the schema element type value that indicates that the tool * should examine DIT structure rules. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES = "dit-structure-rules"; /** * The name of the schema element type value that indicates that the tool * should examine matching rule uses. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES = "matching-rule-uses"; /** * The name of the schema element type value that indicates that the tool * should examine matching rules. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_MATCHING_RULES = "matching-rules"; /** * The name of the schema element type value that indicates that the tool * should examine object classes. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES = "object-classes"; /** * The name of the schema element type value that indicates that the tool * should examine name forms. */ @NotNull private static final String SCHEMA_ELEMENT_TYPE_NAME_FORMS = "name-forms"; // A reference to the argument parser for this tool. @NotNull private final AtomicReference parserRef; // A reference to the completion message for this tool. @NotNull private final AtomicReference completionMessageRef; // Indicates whether to ignore differences in schema element descriptions. private boolean ignoreDescriptions; // Indicates whether to ignore differences in schema element extensions. private boolean ignoreExtensions; // Indicates whether we may include or exclude schema elements based on their // extensions. private boolean includeOrExcludeBasedOnExtensions; // Indicates whether we may include or exclude schema elements based on their // name. private boolean includeOrExcludeBasedOnName; // A list of name prefixes for schema elements to exclude from the comparison. @NotNull private final List excludeNamePrefixes; // A list of name prefixes for schema elements to include in the comparison. @NotNull private final List includeNamePrefixes; // A map of schema extension values for schema elements to exclude from the // comparison. @NotNull private final Map> excludeExtensionValues; // A map of schema extension values for schema elements to include in the // comparison. @NotNull private final Map> includeExtensionValues; // The set of schema element types to examine. @NotNull private final Set schemaElementTypes; /** * Runs this tool with the provided set of arguments, using the default * streams for standard output and standard error. * * @param args The command-line arguments to use to run this program. It * must not be {@code null}, but may be empty. */ public static void main(@NotNull final String... args) { final ResultCode resultCode = main(System.out, System.err, args); if (resultCode != ResultCode.SUCCESS) { System.exit(resultCode.intValue()); } } /** * Runs this tool with the provided set of arguments, using the provided * streams for standard output and standard error. * * @param out The output stream to use for standard output. It may be * {@code null} if standard output should be suppressed. * @param err The output stream to use for standard error. It may be * {@code null} if standard error should be suppressed. * @param args The command-line arguments to use to run this program. It * must not be {@code null}, but may be empty. * * @return The result code with information about the result of processing. * A result code of {@code SUCCESS} indicates that all processing * completed successfully and no differences were identified. A * result code of {@code COMPARE_FALSE} indicates that all processing * completed successfully, but one or more differences were * identified between the server schemas. Any other result code * indicates that some problem occurred during processing. */ @NotNull() public static ResultCode main(@Nullable final OutputStream out, @Nullable final OutputStream err, @NotNull final String... args) { final CompareLDAPSchemas tool = new CompareLDAPSchemas(out, err); return tool.runTool(args); } /** * Creates a new instance of this tool with the provided streams for standard * output and standard error. * * @param out The output stream to use for standard output. It may be * {@code null} if standard output should be suppressed. * @param err The output stream to use for standard error. It may be * {@code null} if standard error should be suppressed. */ public CompareLDAPSchemas(@Nullable final OutputStream out, @Nullable final OutputStream err) { super(out, err, new String[] { "first", "second" }, null); parserRef = new AtomicReference<>(); completionMessageRef = new AtomicReference<>(); schemaElementTypes = new HashSet<>(StaticUtils.setOf( SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES, SCHEMA_ELEMENT_TYPE_MATCHING_RULES, SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES, SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES, SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES, SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES, SCHEMA_ELEMENT_TYPE_NAME_FORMS, SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)); ignoreDescriptions = false; ignoreExtensions = false; includeNamePrefixes = new ArrayList<>(); excludeNamePrefixes = new ArrayList<>(); includeExtensionValues = new HashMap<>(); excludeExtensionValues = new HashMap<>(); } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolName() { return "compare-ldap-schemas"; } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolDescription() { return INFO_COMPARE_SCHEMA_TOOL_DESC.get(); } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolVersion() { return Version.getNumericVersionString(); } /** * {@inheritDoc} */ @Override() protected boolean includeAlternateLongIdentifiers() { return true; } /** * {@inheritDoc} */ @Override() public void addNonLDAPArguments(@NotNull final ArgumentParser parser) throws ArgumentException { parserRef.set(parser); final DNArgument firstSchemaEntryDNArg = new DNArgument(null, ARG_NAME_FIRST_SCHEMA_ENTRY_DN, false, 1, null, INFO_COMPARE_SCHEMA_ARG_DESC_FIRST_SCHEMA_ENTRY_DN.get()); firstSchemaEntryDNArg.addLongIdentifier("first-schema-entry-dn", true); firstSchemaEntryDNArg.addLongIdentifier("firstSchemaEntry", true); firstSchemaEntryDNArg.addLongIdentifier("first-schema-entry", true); firstSchemaEntryDNArg.addLongIdentifier("firstSchemaDN", true); firstSchemaEntryDNArg.addLongIdentifier("first-schema-dn", true); firstSchemaEntryDNArg.addLongIdentifier("firstSchema", true); firstSchemaEntryDNArg.addLongIdentifier("first-schema", true); parser.addArgument(firstSchemaEntryDNArg); final DNArgument secondSchemaEntryDNArg = new DNArgument(null, ARG_NAME_SECOND_SCHEMA_ENTRY_DN, false, 1, null, INFO_COMPARE_SCHEMA_ARG_DESC_SECOND_SCHEMA_ENTRY_DN.get()); secondSchemaEntryDNArg.addLongIdentifier("second-schema-entry-dn", true); secondSchemaEntryDNArg.addLongIdentifier("secondSchemaEntry", true); secondSchemaEntryDNArg.addLongIdentifier("second-schema-entry", true); secondSchemaEntryDNArg.addLongIdentifier("secondSchemaDN", true); secondSchemaEntryDNArg.addLongIdentifier("second-schema-dn", true); secondSchemaEntryDNArg.addLongIdentifier("secondSchema", true); secondSchemaEntryDNArg.addLongIdentifier("second-schema", true); parser.addArgument(secondSchemaEntryDNArg); final StringArgument schemaElementTypesArg = new StringArgument(null, ARG_NAME_SCHEMA_ELEMENT_TYPE, false, 0, INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_SCHEMA_ELEMENT_TYPE.get(), INFO_COMPARE_SCHEMA_ARG_DESC_SCHEMA_ELEMENT_TYPE.get( SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES, SCHEMA_ELEMENT_TYPE_MATCHING_RULES, SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES, SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES, SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES, SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES, SCHEMA_ELEMENT_TYPE_NAME_FORMS, SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)); schemaElementTypesArg.addLongIdentifier("schema-element-types", true); parser.addArgument(schemaElementTypesArg); final BooleanArgument getExtendedSchemaInfoArg = new BooleanArgument(null, ARG_NAME_GET_EXTENDED_SCHEMA_INFO, 1, INFO_COMPARE_SCHEMA_ARG_DESC_GET_EXTENDED_SCHEMA_INFO.get()); getExtendedSchemaInfoArg.addLongIdentifier("get-extended-schema-info", true); parser.addArgument(getExtendedSchemaInfoArg); final BooleanArgument ignoreDescriptionsArg = new BooleanArgument(null, ARG_NAME_IGNORE_DESCRIPTIONS, 1, INFO_COMPARE_SCHEMA_ARG_DESC_IGNORE_DESCRIPTIONS.get()); ignoreDescriptionsArg.addLongIdentifier("ignore-descriptions", true); ignoreDescriptionsArg.addLongIdentifier("ignoreDescription", true); ignoreDescriptionsArg.addLongIdentifier("ignore-description", true); parser.addArgument(ignoreDescriptionsArg); final BooleanArgument ignoreExtensionsArg = new BooleanArgument(null, ARG_NAME_IGNORE_EXTENSIONS, 1, INFO_COMPARE_SCHEMA_ARG_DESC_IGNORE_EXTENSIONS.get()); ignoreExtensionsArg.addLongIdentifier("ignore-extensions", true); ignoreExtensionsArg.addLongIdentifier("ignoreExtension", true); ignoreExtensionsArg.addLongIdentifier("ignore-extension", true); parser.addArgument(ignoreExtensionsArg); final StringArgument includeElementsWithNameMatchingPrefixArg = new StringArgument(null, ARG_NAME_INCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, false, 0, INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_PREFIX.get(), INFO_COMPARE_SCHEMA_ARG_DESC_INCLUDE_NAME_MATCHING_PREFIX.get()); includeElementsWithNameMatchingPrefixArg.addLongIdentifier( "include-elements-with-name-matching-prefix", true); parser.addArgument(includeElementsWithNameMatchingPrefixArg); final StringArgument excludeElementsWithNameMatchingPrefixArg = new StringArgument(null, ARG_NAME_EXCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, false, 0, INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_PREFIX.get(), INFO_COMPARE_SCHEMA_ARG_DESC_EXCLUDE_NAME_MATCHING_PREFIX.get()); excludeElementsWithNameMatchingPrefixArg.addLongIdentifier( "exclude-elements-with-name-matching-prefix", true); parser.addArgument(excludeElementsWithNameMatchingPrefixArg); final StringArgument includeElementsWithExtensionValueArg = new StringArgument(null, ARG_NAME_INCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, false, 0, INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_EXTENSION_VALUE.get(), INFO_COMPARE_SCHEMA_ARG_DESC_INCLUDE_EXTENSION_VALUE.get()); includeElementsWithExtensionValueArg.addLongIdentifier( "include-elements-with-extension-value", true); parser.addArgument(includeElementsWithExtensionValueArg); final StringArgument excludeElementsWithExtensionValueArg = new StringArgument(null, ARG_NAME_EXCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, false, 0, INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_EXTENSION_VALUE.get(), INFO_COMPARE_SCHEMA_ARG_DESC_EXCLUDE_EXTENSION_VALUE.get()); excludeElementsWithExtensionValueArg.addLongIdentifier( "exclude-elements-with-extension-value", true); parser.addArgument(excludeElementsWithExtensionValueArg); } /** * {@inheritDoc} */ @Override() public void doExtendedNonLDAPArgumentValidation() throws ArgumentException { // Identify the types of schema elements to examine. final ArgumentParser parser = parserRef.get(); final StringArgument schemaElementTypesArg = parser.getStringArgument(ARG_NAME_SCHEMA_ELEMENT_TYPE); if ((schemaElementTypesArg != null) && schemaElementTypesArg.isPresent()) { schemaElementTypes.clear(); for (final String value : schemaElementTypesArg.getValues()) { if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES); } else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_MATCHING_RULES)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_MATCHING_RULES); } else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES); } else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES); } else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES); } else if (value.equalsIgnoreCase( SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES); } else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_NAME_FORMS)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_NAME_FORMS); } else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)) { schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES); } else { throw new ArgumentException( ERR_COMPARE_SCHEMA_INVALID_SCHEMA_ELEMENT_TYPE.get(value, ARG_NAME_SCHEMA_ELEMENT_TYPE, SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES, SCHEMA_ELEMENT_TYPE_MATCHING_RULES, SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES, SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES, SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES, SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES, SCHEMA_ELEMENT_TYPE_NAME_FORMS, SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)); } } } // Determine whether to ignore schema element descriptions or extensions. final BooleanArgument ignoreDescriptionsArg = parser.getBooleanArgument(ARG_NAME_IGNORE_DESCRIPTIONS); ignoreDescriptions = ((ignoreDescriptionsArg != null) && ignoreDescriptionsArg.isPresent()); final BooleanArgument ignoreExtensionsArg = parser.getBooleanArgument(ARG_NAME_IGNORE_EXTENSIONS); ignoreExtensions = ((ignoreExtensionsArg != null) && ignoreExtensionsArg.isPresent()); // Identify the schema element name prefixes to include and exclude. getNamePrefixes(ARG_NAME_INCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, includeNamePrefixes); getNamePrefixes(ARG_NAME_EXCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, excludeNamePrefixes); includeOrExcludeBasedOnName = (! includeNamePrefixes.isEmpty()) || (! excludeNamePrefixes.isEmpty()); // Identify the schema element extension values to include and exclude. getExtensionValues(ARG_NAME_INCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, includeExtensionValues); getExtensionValues(ARG_NAME_EXCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, excludeExtensionValues); includeOrExcludeBasedOnExtensions = (! includeExtensionValues.isEmpty()) || (! excludeExtensionValues.isEmpty()); } /** * Populates the provided list with the set of schema element prefixes * contained in the specified argument. * * @param argumentName The name of the argument whose values will be used to * populate the given list. * @param prefixList The list to be updated to include the values of the * specified argument. */ private void getNamePrefixes(@NotNull final String argumentName, @NotNull final List prefixList) { prefixList.clear(); final StringArgument arg = parserRef.get().getStringArgument(argumentName); if ((arg == null) || (! arg.isPresent())) { return; } for (final String value : arg.getValues()) { prefixList.add(StaticUtils.toLowerCase(value)); } } /** * Populates the provided map with the set of schema element extension * name-value pairs contained in the specified argument. * * @param argumentName The name of the argument whose values will be used to * populate the given map. * @param extensionMap The map to be updated to include the values of the * specified argument. * * @throws ArgumentException If there is a problem with any of the values of * the specified argument. */ private void getExtensionValues( @NotNull final String argumentName, @NotNull final Map> extensionMap) throws ArgumentException { extensionMap.clear(); final StringArgument arg = parserRef.get().getStringArgument(argumentName); if ((arg == null) || (! arg.isPresent())) { return; } for (final String value : arg.getValues()) { final int equalPos = value.indexOf('='); if (equalPos < 0) { throw new ArgumentException( ERR_COMPARE_SCHEMA_EXTENSION_VALUE_NO_EQUALS.get(argumentName, value)); } final String extensionName = StaticUtils.toLowerCase(value.substring(0, equalPos)); if (extensionName.isEmpty()) { throw new ArgumentException( ERR_COMPARE_SCHEMA_EXTENSION_VALUE_EMPTY_NAME.get(argumentName, value)); } final String extensionValue = StaticUtils.toLowerCase(value.substring(equalPos + 1)); if (extensionValue.isEmpty()) { throw new ArgumentException( ERR_COMPARE_SCHEMA_EXTENSION_VALUE_EMPTY_VALUE.get(argumentName, value)); } List valueList = extensionMap.get(extensionName); if (valueList == null) { valueList = new ArrayList<>(); extensionMap.put(extensionName, valueList); } valueList.add(extensionValue); } } /** * {@inheritDoc} */ @Override() public boolean supportsInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean defaultsToInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean supportsPropertiesFile() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsOutputFile() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsDebugLogging() { return true; } /** * {@inheritDoc} */ @Override() protected boolean logToolInvocationByDefault() { return false; } /** * {@inheritDoc} */ @Override() @Nullable() protected String getToolCompletionMessage() { return completionMessageRef.get(); } /** * {@inheritDoc} */ @Override() @NotNull() public ResultCode doToolProcessing() { // Get the schemas from each of the servers. final Schema firstServerSchema; final Map firstUnparsableAttributeSyntaxes = new LinkedHashMap<>(); final Map firstUnparsableMatchingRules = new LinkedHashMap<>(); final Map firstUnparsableAttributeTypes = new LinkedHashMap<>(); final Map firstUnparsableObjectClasses = new LinkedHashMap<>(); final Map firstUnparsableDITContentRules = new LinkedHashMap<>(); final Map firstUnparsableDITStructureRules = new LinkedHashMap<>(); final Map firstUnparsableNameForms = new LinkedHashMap<>(); final Map firstUnparsableMatchingRuleUses = new LinkedHashMap<>(); try { firstServerSchema = getSchema(FIRST_SERVER_INDEX, ARG_NAME_FIRST_SCHEMA_ENTRY_DN, INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), firstUnparsableAttributeSyntaxes, firstUnparsableMatchingRules, firstUnparsableAttributeTypes, firstUnparsableObjectClasses, firstUnparsableDITContentRules, firstUnparsableDITStructureRules, firstUnparsableNameForms, firstUnparsableMatchingRuleUses); } catch (final LDAPException e) { logCompletionError(e.getMessage()); return e.getResultCode(); } final Schema secondServerSchema; final Map secondUnparsableAttributeSyntaxes = new LinkedHashMap<>(); final Map secondUnparsableMatchingRules = new LinkedHashMap<>(); final Map secondUnparsableAttributeTypes = new LinkedHashMap<>(); final Map secondUnparsableObjectClasses = new LinkedHashMap<>(); final Map secondUnparsableDITContentRules = new LinkedHashMap<>(); final Map secondUnparsableDITStructureRules = new LinkedHashMap<>(); final Map secondUnparsableNameForms = new LinkedHashMap<>(); final Map secondUnparsableMatchingRuleUses = new LinkedHashMap<>(); try { secondServerSchema = getSchema(SECOND_SERVER_INDEX, ARG_NAME_SECOND_SCHEMA_ENTRY_DN, INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), secondUnparsableAttributeSyntaxes, secondUnparsableMatchingRules, secondUnparsableAttributeTypes, secondUnparsableObjectClasses, secondUnparsableDITContentRules, secondUnparsableDITStructureRules, secondUnparsableNameForms, secondUnparsableMatchingRuleUses); } catch (final LDAPException e) { logCompletionError(e.getMessage()); return e.getResultCode(); } // Report on any unparsable schema elements. final AtomicReference resultCodeRef = new AtomicReference<>(); boolean unparsableElementsEncountered = reportUnparsableSchemaElements( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), firstUnparsableAttributeSyntaxes, firstUnparsableMatchingRules, firstUnparsableAttributeTypes, firstUnparsableObjectClasses, firstUnparsableDITContentRules, firstUnparsableDITStructureRules, firstUnparsableNameForms, firstUnparsableMatchingRuleUses); unparsableElementsEncountered |= reportUnparsableSchemaElements( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), secondUnparsableAttributeSyntaxes, secondUnparsableMatchingRules, secondUnparsableAttributeTypes, secondUnparsableObjectClasses, secondUnparsableDITContentRules, secondUnparsableDITStructureRules, secondUnparsableNameForms, secondUnparsableMatchingRuleUses); if (unparsableElementsEncountered) { resultCodeRef.set(ResultCode.INVALID_ATTRIBUTE_SYNTAX); } // Validate the different types of schema elements. final AtomicInteger numDifferences = new AtomicInteger(); if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES)) { compareAttributeSyntaxes(firstServerSchema, secondServerSchema, numDifferences); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULES)) { compareMatchingRules(firstServerSchema, secondServerSchema, numDifferences); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES)) { compareAttributeTypes(firstServerSchema, secondServerSchema, numDifferences); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES)) { compareObjectClasses(firstServerSchema, secondServerSchema, numDifferences); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES)) { compareDITContentRules(firstServerSchema, secondServerSchema, numDifferences); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES)) { compareDITStructureRules(firstServerSchema, secondServerSchema, numDifferences); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_NAME_FORMS)) { compareNameForms(firstServerSchema, secondServerSchema, numDifferences); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)) { compareMatchingRuleUses(firstServerSchema, secondServerSchema, numDifferences); } // If any errors were encountered, then return an error result code. // Otherwise, if any differences were encountered, then return a // COMPARE_FALSE result code. Otherwise, return a SUCCESS result code. final int differenceCount = numDifferences.get(); if (unparsableElementsEncountered) { switch (differenceCount) { case 0: logCompletionError( ERR_COMPARE_SCHEMA_SUMMARY_UNPARSABLE_NO_DIFFERENCES.get()); break; case 1: logCompletionError( ERR_COMPARE_SCHEMA_SUMMARY_UNPARSABLE_WITH_DIFFERENCE.get()); break; default: logCompletionError( ERR_COMPARE_SCHEMA_SUMMARY_UNPARSABLE_WITH_DIFFERENCES.get( differenceCount)); break; } } else if (differenceCount > 0) { resultCodeRef.compareAndSet(null, ResultCode.COMPARE_FALSE); if (differenceCount == 1) { logCompletionError( ERR_COMPARE_SCHEMA_SUMMARY_DIFFERENCE.get()); } else { logCompletionError( ERR_COMPARE_SCHEMA_SUMMARY_DIFFERENCES.get(differenceCount)); } } else { resultCodeRef.compareAndSet(null, ResultCode.SUCCESS); final String message = INFO_COMPARE_SCHEMA_SUMMARY_NO_DIFFERENCES.get(); completionMessageRef.compareAndSet(null, message); wrapOut(0, WRAP_COLUMN, message); } return resultCodeRef.get(); } /** * Retrieves the schema from the specified server. * * @param serverIndex * The index for the server from which to retrieve the schema. * @param schemaDNArgName * The name of the argument to use to retrieve the DN of the * subschema subentry, if specified. It must not be * {@code null}. * @param serverLabel * The label to use to refer to the server. It must not be * {@code null}. * @param unparsableAttributeSyntaxes * A map that will be updated with information about any * unparsable attribute syntax definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * @param unparsableMatchingRules * A map that will be updated with information about any * unparsable matching rule definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * @param unparsableAttributeTypes * A map that will be updated with information about any * unparsable attribute type definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * @param unparsableObjectClasses * A map that will be updated with information about any * unparsable object class definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * @param unparsableDITContentRules * A map that will be updated with information about any * unparsable DIT content rule definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * @param unparsableDITStructureRules * A map that will be updated with information about any * unparsable DIT structure rule definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * @param unparsableNameForms * A map that will be updated with information about any * unparsable name form definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * @param unparsableMatchingRuleUses * A map that will be updated with information about any * unparsable matching rule use definitions found in the schema * from the specified server. Each key will be the unparsable * definition, and the corresponding value will be the exception * caught while trying to parse it. It must not be {@code null}. * * @return The schema retrieved from the server. * * @throws LDAPException If a problem occurs while attempting to obtain the * schema. */ @NotNull() private Schema getSchema(final int serverIndex, @NotNull final String schemaDNArgName, @NotNull final String serverLabel, @NotNull final Map unparsableAttributeSyntaxes, @NotNull final Map unparsableMatchingRules, @NotNull final Map unparsableAttributeTypes, @NotNull final Map unparsableObjectClasses, @NotNull final Map unparsableDITContentRules, @NotNull final Map unparsableDITStructureRules, @NotNull final Map unparsableNameForms, @NotNull final Map unparsableMatchingRuleUses) throws LDAPException { // Establish a connection to the server. final LDAPConnection conn; try { conn = getConnection(serverIndex); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(e.getResultCode(), ERR_COMPARE_SCHEMA_CANNOT_CONNECT.get(serverLabel, e.getMessage()), e); } final ArgumentParser parser = parserRef.get(); final BooleanArgument getExtendedSchemaInfoArg = parser.getBooleanArgument(ARG_NAME_GET_EXTENDED_SCHEMA_INFO); final boolean getExtendedSchemaInfo = ((getExtendedSchemaInfoArg != null) && getExtendedSchemaInfoArg.isPresent()); try { // See if the schema entry DN was specified as an argument. If so, then // retrieve that entry and parse it as a schema entry. Otherwise, use the // default method for obtaining the schema. final String schemaEntryDN; final DNArgument schemaEntryDNArg = parser.getDNArgument(schemaDNArgName); if (schemaEntryDNArg.isPresent()) { schemaEntryDN = schemaEntryDNArg.getStringValue(); } else { final RootDSE rootDSE = conn.getRootDSE(); if (rootDSE == null) { throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_COMPARE_SCHEMA_CANNOT_GET_ROOT_DSE.get(serverLabel)); } schemaEntryDN = rootDSE.getSubschemaSubentryDN(); if (schemaEntryDN == null) { throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_COMPARE_SCHEMA_CANNOT_GET_ROOT_DSE_SCHEMA_DN.get(serverLabel, RootDSE.ATTR_SUBSCHEMA_SUBENTRY)); } } final SearchRequest searchRequest = new SearchRequest(schemaEntryDN, SearchScope.BASE, Schema.SUBSCHEMA_SUBENTRY_FILTER, Schema.SCHEMA_REQUEST_ATTRS); if (getExtendedSchemaInfo) { searchRequest.addControl(new ExtendedSchemaInfoRequestControl(false)); } final Entry schemaEntry = conn.searchForEntry(searchRequest); if (schemaEntry == null) { throw new LDAPException(ResultCode.NO_SUCH_OBJECT, ERR_COMPARE_SCHEMA_CANNOT_GET_SCHEMA_ENTRY.get( String.valueOf(schemaEntryDN), serverLabel)); } return new Schema(schemaEntry, unparsableAttributeSyntaxes, unparsableMatchingRules, unparsableAttributeTypes, unparsableObjectClasses, unparsableDITContentRules, unparsableDITStructureRules, unparsableNameForms, unparsableMatchingRuleUses); } catch (final LDAPException e) { Debug.debugException(e); throw new LDAPException(e.getResultCode(), ERR_COMPARE_SCHEMA_CANNOT_GET_SCHEMA.get(serverLabel, e.getMessage()), e); } finally { conn.close(); } } /** * Reports error messages about any unparsable elements found in a server's * schema. * * @param serverLabel * The label for the associated directory server instance. * @param unparsableAttributeSyntaxes * A map with information about any unparsable attribute syntax * definitions found in the schema. * @param unparsableMatchingRules * A map with information about any unparsable matching rule * definitions found in the schema. * @param unparsableAttributeTypes * A map with information about any unparsable attribute type * definitions found in the schema. * @param unparsableObjectClasses * A map with information about any unparsable object class * definitions found in the schema. * @param unparsableDITContentRules * A map with information about any unparsable DIT content rule * definitions found in the schema. * @param unparsableDITStructureRules * A map with information about any unparsable DIT structure rule * definitions found in the schema. * @param unparsableNameForms * A map with information about any unparsable name form * definitions found in the schema. * @param unparsableMatchingRuleUses * A map with information about any unparsable matching rule use * definitions found in the schema. * * @return {@code true} if the schema contained any unparsable elements, or * {@code false} if not. */ private boolean reportUnparsableSchemaElements( @NotNull final String serverLabel, @NotNull final Map unparsableAttributeSyntaxes, @NotNull final Map unparsableMatchingRules, @NotNull final Map unparsableAttributeTypes, @NotNull final Map unparsableObjectClasses, @NotNull final Map unparsableDITContentRules, @NotNull final Map unparsableDITStructureRules, @NotNull final Map unparsableNameForms, @NotNull final Map unparsableMatchingRuleUses) { boolean unparsableFound = false; if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableAttributeSyntaxes, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get()); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULES)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableMatchingRules, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get()); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableAttributeTypes, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get()); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableObjectClasses, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get()); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableDITContentRules, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get()); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableDITStructureRules, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get()); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_NAME_FORMS)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableNameForms, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get()); } if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)) { unparsableFound |= reportUnparsableSchemaElements(serverLabel, unparsableMatchingRuleUses, INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get()); } return unparsableFound; } /** * Reports error messages about any unparsable elements of the specified type * found in a server's schema. * * @param serverLabel The label for the associated directory server * instance. It must not be {@code null}. * @param unparsableElements The set of unparsable elements of a given type. * It must not be {@code null}, but may be empty. * @param elementTypeName The name of the schema element type. It must * not be {@code null}. * * @return {@code true} if the provided map contained information about one * or more unparsable elements, or {@code false} if not. */ private boolean reportUnparsableSchemaElements( @NotNull final String serverLabel, @NotNull final Map unparsableElements, @NotNull final String elementTypeName) { for (final Map.Entry e : unparsableElements.entrySet()) { wrapErr(0, WRAP_COLUMN, ERR_COMPARE_SCHEMA_UNPARSABLE_ELEMENT.get(elementTypeName, serverLabel, e.getValue().getMessage())); err(e.getKey()); err(); } return (! unparsableElements.isEmpty()); } /** * Compares the attribute syntax definitions contained in the provided * schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareAttributeSyntaxes( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the attribute syntax definitions from each of the schemas. final Map syntaxes1 = getAttributeSyntaxMap(firstServerSchema); final Map syntaxes2 = getAttributeSyntaxMap(secondServerSchema); // Identify syntaxes that exist in one server but not another. If any are // found, then report them and remove them from the set. Iterator> iterator = syntaxes1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! syntaxes2.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_SYNTAX.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = syntaxes2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! syntaxes1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_SYNTAX.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining syntaxes should exist in both servers. Compare them and // see if there are any differences between them. for (final OID oid : syntaxes1.keySet()) { final AttributeSyntaxDefinition d1 = syntaxes1.get(oid); final AttributeSyntaxDefinition d2 = syntaxes2.get(oid); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), oid.toString(), INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), oid.toString(), d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the attribute syntax definitions contained in the * provided schema, indexed by OID. * * @param schema The schema from which to retrieve the attribute syntaxes. * It must not be {@code null}. * * @return A map of the attribute syntax definitions contained in the * provided schema. */ @NotNull() private Map getAttributeSyntaxMap( @NotNull final Schema schema) { final Map syntaxes = new TreeMap<>(); for (final AttributeSyntaxDefinition d : schema.getAttributeSyntaxes()) { if (includeBasedOnNameAndExtensions(StaticUtils.NO_STRINGS, d.getExtensions())) { syntaxes.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); } } return syntaxes; } /** * Compares the matching rule definitions contained in the provided schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareMatchingRules( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the matching rule definitions from each of the schemas. final Map matchingRules1 = getMatchingRuleMap(firstServerSchema); final Map matchingRules2 = getMatchingRuleMap(secondServerSchema); // Identify matching rules that exist in one server but not another. If any // are found, then report them and remove them from the set. Iterator> iterator = matchingRules1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! matchingRules2.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = matchingRules2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! matchingRules1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining matching rules should exist in both servers. Compare them // and see if there are any differences between them. for (final OID oid : matchingRules1.keySet()) { final MatchingRuleDefinition d1 = matchingRules1.get(oid); final MatchingRuleDefinition d2 = matchingRules2.get(oid); final String identifier = compareNames( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), oid.toString(), d1.getNames(), d2.getNames(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SYNTAX_OID.get(), d1.getSyntaxOID(), d2.getSyntaxOID(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), d1.isObsolete(), d2.isObsolete(), numDifferences); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), identifier, d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the matching rule definitions contained in the provided * schema, indexed by OID. * * @param schema The schema from which to retrieve the matching rules. It * must not be {@code null}. * * @return A map of the matching rule definitions contained in the provided * schema. */ @NotNull() private Map getMatchingRuleMap( @NotNull final Schema schema) { final Map matchingRules = new TreeMap<>(); for (final MatchingRuleDefinition d : schema.getMatchingRules()) { if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) { matchingRules.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); } } return matchingRules; } /** * Compares the attribute type definitions contained in the provided schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareAttributeTypes( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the attribute type definitions from each of the schemas. final Map attributeTypes1 = getAttributeTypeMap(firstServerSchema); final Map attributeTypes2 = getAttributeTypeMap(secondServerSchema); // Identify attribute types that exist in one server but not another. If // any are found, then report them and remove them from the set. Iterator> iterator = attributeTypes1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! attributeTypes2.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_ATTRIBUTE_TYPE.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = attributeTypes2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! attributeTypes1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_ATTRIBUTE_TYPE.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining attribute types should exist in both servers. Compare them // and see if there are any differences between them. for (final OID oid : attributeTypes1.keySet()) { final AttributeTypeDefinition d1 = attributeTypes1.get(oid); final AttributeTypeDefinition d2 = attributeTypes2.get(oid); final String identifier = compareNames( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), oid.toString(), d1.getNames(), d2.getNames(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUPERIOR_TYPE.get(), d1.getSuperiorType(), d2.getSuperiorType(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SYNTAX_OID.get(), d1.getSyntaxOID(), d2.getSyntaxOID(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_EQUALITY_MR.get(), d1.getEqualityMatchingRule(), d2.getEqualityMatchingRule(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_ORDERING_MR.get(), d1.getOrderingMatchingRule(), d2.getOrderingMatchingRule(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUBSTRING_MR.get(), d1.getSubstringMatchingRule(), d2.getSubstringMatchingRule(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_SINGLE_VALUE.get(), d1.isSingleValued(), d2.isSingleValued(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_USAGE.get(), d1.getUsage().getName(), d2.getUsage().getName(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_NO_USER_MOD.get(), d1.isNoUserModification(), d2.isNoUserModification(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_COLLECTIVE.get(), d1.isCollective(), d2.isCollective(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), d1.isObsolete(), d2.isObsolete(), numDifferences); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), identifier, d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the attribute type definitions contained in the provided * schema, indexed by OID. * * @param schema The schema from which to retrieve the attribute types. It * must not be {@code null}. * * @return A map of the attribute type definitions contained in the provided * schema. */ @NotNull() private Map getAttributeTypeMap( @NotNull final Schema schema) { final Map attributeTypes = new TreeMap<>(); for (final AttributeTypeDefinition d : schema.getAttributeTypes()) { if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) { attributeTypes.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); } } return attributeTypes; } /** * Compares the object class definitions contained in the provided schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareObjectClasses( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the object class definitions from each of the schemas. final Map objectClasses1 = getObjectClassMap(firstServerSchema); final Map objectClasses2 = getObjectClassMap(secondServerSchema); // Identify object classes that exist in one server but not another. If // any are found, then report them and remove them from the set. Iterator> iterator = objectClasses1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! objectClasses2.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_OBJECT_CLASS.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = objectClasses2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! objectClasses1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_OBJECT_CLASS.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining object classes should exist in both servers. Compare them // and see if there are any differences between them. for (final OID oid : objectClasses1.keySet()) { final ObjectClassDefinition d1 = objectClasses1.get(oid); final ObjectClassDefinition d2 = objectClasses2.get(oid); final String identifier = compareNames( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), oid.toString(), d1.getNames(), d2.getNames(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUPERIOR_TYPE.get(), d1.getSuperiorClasses(), d2.getSuperiorClasses(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_REQUIRED_ATTRIBUTE.get(), d1.getRequiredAttributes(), d2.getRequiredAttributes(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OPTIONAL_ATTRIBUTE.get(), d1.getOptionalAttributes(), d2.getOptionalAttributes(), numDifferences); final String oc1Type = (d1.getObjectClassType() == null) ? null : d1.getObjectClassType().getName(); final String oc2Type = (d2.getObjectClassType() == null) ? null : d2.getObjectClassType().getName(); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OBJECT_CLASS_TYPE.get(), oc1Type, oc2Type, numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), d1.isObsolete(), d2.isObsolete(), numDifferences); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the object class definitions contained in the provided * schema, indexed by OID. * * @param schema The schema from which to retrieve the object classes. It * must not be {@code null}. * * @return A map of the object class definitions contained in the provided * schema. */ @NotNull() private Map getObjectClassMap( @NotNull final Schema schema) { final Map objectClasses = new TreeMap<>(); for (final ObjectClassDefinition d : schema.getObjectClasses()) { if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) { objectClasses.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); } } return objectClasses; } /** * Compares the DIT content rule definitions contained in the provided * schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareDITContentRules( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the DIT content rule definitions from each of the schemas. final Map ditContentRules1 = getDITContentRuleMap(firstServerSchema); final Map ditContentRules2 = getDITContentRuleMap(secondServerSchema); // Identify DIT content rules that exist in one server but not another. If // any are found, then report them and remove them from the set. Iterator> iterator = ditContentRules1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! ditContentRules2.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_DIT_CONTENT_RULE.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = ditContentRules2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! ditContentRules1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_DIT_CONTENT_RULE.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining DIT content rules should exist in both servers. Compare // them and see if there are any differences between them. for (final OID oid : ditContentRules1.keySet()) { final DITContentRuleDefinition d1 = ditContentRules1.get(oid); final DITContentRuleDefinition d2 = ditContentRules2.get(oid); final String identifier = compareNames( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), oid.toString(), d1.getNames(), d2.getNames(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_REQUIRED_ATTRIBUTE.get(), d1.getRequiredAttributes(), d2.getRequiredAttributes(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OPTIONAL_ATTRIBUTE.get(), d1.getOptionalAttributes(), d2.getOptionalAttributes(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_PROHIBITED_ATTRIBUTE.get(), d1.getProhibitedAttributes(), d2.getProhibitedAttributes(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_AUXILIARY_CLASS.get(), d1.getAuxiliaryClasses(), d2.getAuxiliaryClasses(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), d1.isObsolete(), d2.isObsolete(), numDifferences); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the DIT content rule definitions contained in the * provided schema, indexed by OID. * * @param schema The schema from which to retrieve the DIT content rules. * It must not be {@code null}. * * @return A map of the DIT content rule definitions contained in the * provided schema. */ @NotNull() private Map getDITContentRuleMap( @NotNull final Schema schema) { final Map ditContentRules = new TreeMap<>(); for (final DITContentRuleDefinition d : schema.getDITContentRules()) { if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) { ditContentRules.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); } } return ditContentRules; } /** * Compares the DIT structure rule definitions contained in the provided * schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareDITStructureRules( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the DIT structure rule definitions from each of the schemas. final Map ditStructureRules1 = getDITStructureRuleMap(firstServerSchema); final Map ditStructureRules2 = getDITStructureRuleMap(secondServerSchema); // Identify DIT structure rules that exist in one server but not another. // If any are found, then report them and remove them from the set. Iterator> iterator = ditStructureRules1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final Integer id = e.getKey(); if (! ditStructureRules2.containsKey(id)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_DIT_STRUCTURE_RULE.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = ditStructureRules2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final Integer oid = e.getKey(); if (! ditStructureRules1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_DIT_STRUCTURE_RULE.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining DIT structure rules should exist in both servers. Compare // them and see if there are any differences between them. for (final Integer id : ditStructureRules1.keySet()) { final DITStructureRuleDefinition d1 = ditStructureRules1.get(id); final DITStructureRuleDefinition d2 = ditStructureRules2.get(id); final String identifier = compareNames( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), id.toString(), d1.getNames(), d2.getNames(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_NAME_FORM.get(), d1.getNameFormID(), d2.getNameFormID(), numDifferences); final String[] superiorRuleIDs1 = intArrayToStringArray(d1.getSuperiorRuleIDs()); final String[] superiorRuleIDs2 = intArrayToStringArray(d2.getSuperiorRuleIDs()); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUPERIOR_RULE_ID.get(), superiorRuleIDs1, superiorRuleIDs2, numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), d1.isObsolete(), d2.isObsolete(), numDifferences); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), identifier, d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the DIT structure rule definitions contained in the * provided schema, indexed by rule ID. * * @param schema The schema from which to retrieve the DIT structure rules. * It must not be {@code null}. * * @return A map of the DIT structure rule definitions contained in the * provided schema. */ @NotNull() private Map getDITStructureRuleMap( @NotNull final Schema schema) { final Map ditStructureRules = new TreeMap<>(); for (final DITStructureRuleDefinition d : schema.getDITStructureRules()) { if (includeBasedOnNameAndExtensions(StaticUtils.NO_STRINGS, d.getExtensions())) { ditStructureRules.put(d.getRuleID(), d); } } return ditStructureRules; } /** * Converts the provided integer array to a string array in which each element * is the string representation of the corresponding element in the provided * integer array. * * @param intArray The integer array to convert to a string array. It must * not be {@code null}, but may be empty. * * @return A string array in which each element is the string representation * of the corresponding element in the provided integer array. */ @NotNull() private static String[] intArrayToStringArray(@NotNull final int[] intArray) { final String[] stringArray = new String[intArray.length]; for (int i=0; i < intArray.length; i++) { stringArray[i] = String.valueOf(intArray[i]); } return stringArray; } /** * Compares the name form definitions contained in the provided schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareNameForms( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the name form definitions from each of the schemas. final Map nameForms1 = getNameFormMap(firstServerSchema); final Map nameForms2 = getNameFormMap(secondServerSchema); // Identify name forms that exist in one server but not another. If // any are found, then report them and remove them from the set. Iterator> iterator = nameForms1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! nameForms2.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_NAME_FORM.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = nameForms2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! nameForms1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_NAME_FORM.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining name forms should exist in both servers. Compare them and // see if there are any differences between them. for (final OID oid : nameForms1.keySet()) { final NameFormDefinition d1 = nameForms1.get(oid); final NameFormDefinition d2 = nameForms2.get(oid); final String identifier = compareNames( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), oid.toString(), d1.getNames(), d2.getNames(), numDifferences); compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_STRUCTURAL_CLASS.get(), d1.getStructuralClass(), d2.getStructuralClass(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_REQUIRED_ATTRIBUTE.get(), d1.getRequiredAttributes(), d2.getRequiredAttributes(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OPTIONAL_ATTRIBUTE.get(), d1.getOptionalAttributes(), d2.getOptionalAttributes(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), d1.isObsolete(), d2.isObsolete(), numDifferences); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the name form definitions contained in the provided * schema, indexed by OID. * * @param schema The schema from which to retrieve the name forms. It must * not be {@code null}. * * @return A map of the name form definitions contained in the provided * schema. */ @NotNull() private Map getNameFormMap( @NotNull final Schema schema) { final Map nameForms = new TreeMap<>(); for (final NameFormDefinition d : schema.getNameForms()) { if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) { nameForms.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); } } return nameForms; } /** * Compares the matching rule use definitions contained in the provided * schemas. * * @param firstServerSchema The schema retrieved from the first server. It * must not be {@code null}. * @param secondServerSchema The schema retrieved from the second server. * It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareMatchingRuleUses( @NotNull final Schema firstServerSchema, @NotNull final Schema secondServerSchema, @NotNull final AtomicInteger numDifferences) { // Get the matching rule use definitions from each of the schemas. final Map matchingRuleUses1 = getMatchingRuleUseMap(firstServerSchema); final Map matchingRuleUses2 = getMatchingRuleUseMap(secondServerSchema); // Identify matching rule uses that exist in one server but not another. If // any are found, then report them and remove them from the set. Iterator> iterator = matchingRuleUses1.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! matchingRuleUses2.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE_USE.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } iterator = matchingRuleUses2.entrySet().iterator(); while (iterator.hasNext()) { final Map.Entry e = iterator.next(); final OID oid = e.getKey(); if (! matchingRuleUses1.containsKey(oid)) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE_USE.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences, e.getValue().toString()); iterator.remove(); } } // Any remaining matching rule uses should exist in both servers. Compare // them and see if there are any differences between them. for (final OID oid : matchingRuleUses1.keySet()) { final MatchingRuleUseDefinition d1 = matchingRuleUses1.get(oid); final MatchingRuleUseDefinition d2 = matchingRuleUses2.get(oid); final String identifier = compareNames( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), oid.toString(), d1.getNames(), d2.getNames(), numDifferences); compareStringArrayValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_APPLICABLE_ATTRIBUTE.get(), d1.getApplicableAttributeTypes(), d2.getApplicableAttributeTypes(), numDifferences); compareBooleanValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), d1.isObsolete(), d2.isObsolete(), numDifferences); if (! ignoreDescriptions) { compareStringValues( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), d1.getDescription(), d2.getDescription(), numDifferences); } compareExtensions( INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), identifier, d1.getExtensions(), d2.getExtensions(), numDifferences); } } /** * Retrieves a map of the matching rule use definitions contained in the * provided schema, indexed by OID. * * @param schema The schema from which to retrieve the matching rule uses. * It must not be {@code null}. * * @return A map of the matching rule use definitions contained in the * provided schema. */ @NotNull() private Map getMatchingRuleUseMap( @NotNull final Schema schema) { final Map matchingRuleUses = new TreeMap<>(); for (final MatchingRuleUseDefinition d : schema.getMatchingRuleUses()) { if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) { matchingRuleUses.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); } } return matchingRuleUses; } /** * Indicates whether to include a schema element with the given name and set * of extensions. * * @param names The set of names for the schema element. It must not * be {@code null}, but may be empty. * @param extensions The set of extensions for the schema element. It must * not be {@code null}, but may be empty. * * @return {@code true} if an element with the given names and set of * extensions should be included, or {@code false} if not. */ private boolean includeBasedOnNameAndExtensions( @NotNull final String[] names, @NotNull final Map extensions) { if (includeOrExcludeBasedOnName && (names.length > 0)) { boolean includeFound = false; for (final String name : names) { final String lowerName = StaticUtils.toLowerCase(name); for (final String excludePrefix : excludeNamePrefixes) { if (lowerName.startsWith(excludePrefix)) { return false; } } if (! includeNamePrefixes.isEmpty()) { for (final String includePrefix : includeNamePrefixes) { if (lowerName.startsWith(includePrefix)) { includeFound = true; break; } } } } if ((! includeNamePrefixes.isEmpty()) && (! includeFound)) { return false; } } if (includeOrExcludeBasedOnExtensions && (! extensions.isEmpty())) { boolean includeFound = false; for (final Map.Entry e : extensions.entrySet()) { final String lowerName = StaticUtils.toLowerCase(e.getKey()); final String[] values = e.getValue(); final String[] lowerValues = new String[values.length]; for (int i=0; i < values.length; i++) { lowerValues[i] = StaticUtils.toLowerCase(values[i]); } final List excludeValues = excludeExtensionValues.get(lowerName); if (excludeValues != null) { for (final String lowerValue : lowerValues) { if (excludeValues.contains(lowerValue)) { return false; } } } final List includeValues = includeExtensionValues.get(lowerName); if (includeValues != null) { for (final String lowerValue : lowerValues) { if (includeValues.contains(lowerValue)) { includeFound = true; break; } } } } if ((! includeExtensionValues.isEmpty()) && (! includeFound)) { return false; } } return true; } /** * Reports a difference between schema elements. * * @param message The message to display with information about * the difference. It must not be {@code null}. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. * @param additionalStrings A set of additional strings that should also be * displayed, in addition to the provided message. * Each additional string will be presented on its * own line without any wrapping. It must not be * {@code null}, but may be empty. */ private void reportDifference(@NotNull final String message, @NotNull final AtomicInteger numDifferences, @NotNull final String... additionalStrings) { wrapErr(0, WRAP_COLUMN, message); for (final String additionalString : additionalStrings) { err(additionalString); } err(); numDifferences.incrementAndGet(); } /** * Identifies differences between string values for two schema elements. * * @param elementTypeName A name for the type of schema element being * compared. It must not be {@code null}. * @param elementIdentifier An identifier (e.g., the name or OID) for the * schema element for which to make the * determination. It must not be {@code null}. * @param stringDescriptor A descriptor for the string values being * compared. * @param string1 The string value from the first schema element. * It may be {@code null} if the element does not * contain a value for the associated field. * @param string2 The string value from the first second element. * It may be {@code null} if the element does not * contain a value for the associated field. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareStringValues(@NotNull final String elementTypeName, @NotNull final String elementIdentifier, @NotNull final String stringDescriptor, @Nullable final String string1, @Nullable final String string2, @NotNull final AtomicInteger numDifferences) { if (string1 == null) { if (string2 != null) { reportDifference( WARN_COMPARE_SCHEMA_STRING_MISSING_FROM_SERVER.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), elementTypeName, elementIdentifier, stringDescriptor, string2, INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences); } } else if (string2 == null) { reportDifference( WARN_COMPARE_SCHEMA_STRING_MISSING_FROM_SERVER.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), elementTypeName, elementIdentifier, stringDescriptor, string1, INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences); } else if (! string1.equalsIgnoreCase(string2)) { reportDifference( WARN_COMPARE_SCHEMA_STRING_DIFFERENT_BETWEEN_SERVERS.get( elementTypeName, elementIdentifier, stringDescriptor, string1, string2), numDifferences); } } /** * Identifies differences between the sets of string arrays for two schema * elements. * * @param elementTypeName A name for the type of schema element being * compared. It must not be {@code null}. * @param elementIdentifier An identifier (e.g., the name or OID) for the * schema element for which to make the * determination. It must not be {@code null}. * @param stringDescriptor A descriptor for the string values being * compared. * @param array1 The array of values for the target field from * the element in the first schema. It must not be * {@code null}, but may be empty. * @param array2 The array of values for the target field from * the element in the second schema. It must not * be {@code null}, but may be empty. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareStringArrayValues( @NotNull final String elementTypeName, @NotNull final String elementIdentifier, @NotNull final String stringDescriptor, @NotNull final String[] array1, @NotNull final String[] array2, @NotNull final AtomicInteger numDifferences) { if (array1.length == 0) { switch (array2.length) { case 0: // The element doesn't have any names in either of the servers, so // there is no difference. break; case 1: // The element has names in the second server, but not in the first. reportDifference( WARN_COMPARE_SCHEMA_STRING_ARRAY_SINGLE_IN_ONLY_ONE_SERVER.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), elementTypeName, elementIdentifier, stringDescriptor, array2[0], INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences); break; default: reportDifference( WARN_COMPARE_SCHEMA_STRING_ARRAY_MULTIPLE_IN_ONLY_ONE_SERVER.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), elementTypeName, elementIdentifier, stringDescriptor, Arrays.toString(array2), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences); break; } } else if (array2.length == 0) { // The element has names in the first server, but not in the second. if (array1.length == 1) { reportDifference( WARN_COMPARE_SCHEMA_STRING_ARRAY_SINGLE_IN_ONLY_ONE_SERVER.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), elementTypeName, elementIdentifier, stringDescriptor, array1[0], INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences); } else { reportDifference( WARN_COMPARE_SCHEMA_STRING_ARRAY_MULTIPLE_IN_ONLY_ONE_SERVER.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), elementTypeName, elementIdentifier, stringDescriptor, Arrays.toString(array1), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences); } } else { // The element has names in both servers. See if there are any // differences between them. final Map n1 = getNameMap(array1); final Map n2 = getNameMap(array2); for (final Map.Entry e : n1.entrySet()) { final String lowerName = e.getKey(); if (n2.remove(lowerName) == null) { reportDifference( WARN_COMPARE_SCHEMA_STRING_ARRAY_VALUE_MISSING_FROM_SERVER.get( elementTypeName, elementIdentifier, stringDescriptor, INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), e.getValue(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences); } } for (final String nameOnlyInServer2 : n2.values()) { reportDifference( WARN_COMPARE_SCHEMA_STRING_ARRAY_VALUE_MISSING_FROM_SERVER.get( elementTypeName, elementIdentifier, stringDescriptor, INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), nameOnlyInServer2, INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences); } } } /** * Identifies differences between the sets of names for two schema elements. * * @param elementTypeName A name for the type of schema element being * compared. It must not be {@code null}. * @param elementIdentifier An identifier (e.g., the name or OID) for the * schema element for which to make the * determination. It must not be {@code null}. * @param names1 The set of names for the element from the first * schema. It must not be {@code null}, but may be * empty. * @param names2 The set of names for the element from the second * schema. It must not be {@code null}, but may be * empty. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. * * @return The identifier string that should be used to identify the * associated schema element. If both sets of names are non-empty * and have the same first element, then that name will be used as * the identifier. Otherwise, the provided identifier will be used. */ @NotNull() private String compareNames(@NotNull final String elementTypeName, @NotNull final String elementIdentifier, @NotNull final String[] names1, @NotNull final String[] names2, @NotNull final AtomicInteger numDifferences) { compareStringArrayValues(elementTypeName, elementIdentifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_NAME.get(), names1, names2, numDifferences); // Identify the best identifier to use for the schema element going forward. if ((names1.length > 0) && (names2.length > 0) && (names1[0].equalsIgnoreCase(names2[0]))) { return names1[0]; } else { return elementIdentifier; } } /** * Identifies difference between boolean values for two schema elements. * * @param elementTypeName A name for the type of schema element being * compared. It must not be {@code null}. * @param elementIdentifier An identifier (e.g., the name or OID) for the * schema element for which to make the * determination. It must not be {@code null}. * @param booleanFieldName The name of the Boolean field being compared. * @param value1 The Boolean value from the first schema element. * @param value2 The Boolean value from the second schema * element. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareBooleanValues(@NotNull final String elementTypeName, @NotNull final String elementIdentifier, @NotNull final String booleanFieldName, final boolean value1, final boolean value2, @NotNull final AtomicInteger numDifferences) { if (value1 != value2) { if (value1) { reportDifference( WARN_COMPARE_SCHEMA_BOOLEAN_DIFFERENCE.get( elementTypeName, elementIdentifier, booleanFieldName, INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences); } else { reportDifference( WARN_COMPARE_SCHEMA_BOOLEAN_DIFFERENCE.get( elementTypeName, elementIdentifier, booleanFieldName, INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences); } } } /** * Identifies differences between the sets of extensions for two schema * elements. * * @param elementTypeName A name for the type of schema element being * compared. It must not be {@code null}. * @param elementIdentifier An identifier (e.g., the name or OID) for the * schema element for which to make the * determination. It must not be {@code null}. * @param extensions1 The set of extensions for the element from the * first schema. It must not be {@code null}, but * may be empty. * @param extensions2 The set of extensions for the element from the * second schema. It must not be {@code null}, but * may be empty. * @param numDifferences A counter used to keep track of the number of * differences found between the schemas. It must * not be {@code null}. */ private void compareExtensions( @NotNull final String elementTypeName, @NotNull final String elementIdentifier, @NotNull final Map extensions1, @NotNull final Map extensions2, @NotNull final AtomicInteger numDifferences) { if (ignoreExtensions) { return; } // Convert the extensions into a map of sets so that we can alter the // contents of both the map and its sets. final Map> e1 = convertToUpdatableExtensionsMap(extensions1); final Map> e2 = convertToUpdatableExtensionsMap(extensions2); // Iterate through the extensions and identify differences between them. for (final Map.Entry> e : e1.entrySet()) { final String extensionName = e.getKey(); final Set extension1Values = e.getValue(); final Set extension2Values = e2.remove(extensionName); if (extension2Values == null) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_EXTENSION.get( INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), elementTypeName, elementIdentifier, extensionName, INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), numDifferences); } else if (! extension1Values.equals(extension2Values)) { reportDifference( WARN_COMPARE_SCHEMA_EXTENSION_DIFFERENCE.get( elementTypeName, elementIdentifier, extensionName), numDifferences); } } for (final String extensionName : e2.keySet()) { reportDifference( WARN_COMPARE_SCHEMA_MISSING_EXTENSION.get( INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), elementTypeName, elementIdentifier, extensionName, INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), numDifferences); } } /** * Converts the provided extensions map into an updatable map that associates * each extension name key with a modifiable set of values rather than an * array. In addition, all strings will be converted to lowercase for more * efficient case-insensitive comparison. * * @param extensionsMap The map to be converted. It must not be * {@code null}, but may be empty. * * @return A modifiable map that contains the information in the provided map * in a form that is better suited for comparing extensions between * two definitions. */ @NotNull() private static Map> convertToUpdatableExtensionsMap( @NotNull final Map extensionsMap) { final Map> convertedExtensionsMap = new TreeMap<>(); for (final Map.Entry e : extensionsMap.entrySet()) { final String lowerExtensionName = StaticUtils.toLowerCase(e.getKey()); final Set lowerExtensionValues = new TreeSet<>(); for (final String extensionValue : e.getValue()) { lowerExtensionValues.add(StaticUtils.toLowerCase(extensionValue)); } convertedExtensionsMap.put(lowerExtensionName, lowerExtensionValues); } return convertedExtensionsMap; } /** * Retrieves a modifiable map containing the provided names. The key for each * entry in the map will be the name in all lowercase, and the value will be * the name in the case in which it is provided. * * @param names The names to include in the resulting map. It must not be * {@code null}. * * @return A modifiable map containing the provided names. */ @NotNull() private static Map getNameMap(@NotNull final String[] names) { final Map m = new TreeMap<>(); for (final String name : names) { m.put(StaticUtils.toLowerCase(name), name); } return m; } /** * Logs the provided message to standard error and sets it as the tool * completion message. * * @param message The completion message. It must not be {@code null}. */ private void logCompletionError(@NotNull final String message) { completionMessageRef.compareAndSet(null, message); wrapErr(0, WRAP_COLUMN, message); } /** * {@inheritDoc} */ @Override() @NotNull() public LinkedHashMap getExampleUsages() { final LinkedHashMap examples = new LinkedHashMap<>(); examples.put( new String[] { "--firstHostname", "ds1.example.com", "--firstPort", "636", "--firstUseSSL", "--firstBindDN", "cn=Directory Manager", "--firstBindPasswordFile", "/path/to/password.txt", "--secondHostname", "ds2.example.com", "--secondPort", "636", "--secondUseSSL", "--secondBindDN", "cn=Directory Manager", "--secondBindPasswordFile", "/path/to/password.txt" }, INFO_COMPARE_LDAP_SCHEMAS_EXAMPLE.get()); return examples; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy