001/* 002 * Copyright 2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.tools; 037 038 039 040import java.io.OutputStream; 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.HashMap; 044import java.util.HashSet; 045import java.util.Iterator; 046import java.util.LinkedHashMap; 047import java.util.List; 048import java.util.Map; 049import java.util.Set; 050import java.util.TreeMap; 051import java.util.TreeSet; 052import java.util.concurrent.atomic.AtomicInteger; 053import java.util.concurrent.atomic.AtomicReference; 054 055import com.unboundid.ldap.sdk.Entry; 056import com.unboundid.ldap.sdk.LDAPConnection; 057import com.unboundid.ldap.sdk.LDAPException; 058import com.unboundid.ldap.sdk.ResultCode; 059import com.unboundid.ldap.sdk.RootDSE; 060import com.unboundid.ldap.sdk.SearchRequest; 061import com.unboundid.ldap.sdk.SearchScope; 062import com.unboundid.ldap.sdk.Version; 063import com.unboundid.ldap.sdk.schema.AttributeSyntaxDefinition; 064import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 065import com.unboundid.ldap.sdk.schema.DITContentRuleDefinition; 066import com.unboundid.ldap.sdk.schema.DITStructureRuleDefinition; 067import com.unboundid.ldap.sdk.schema.MatchingRuleDefinition; 068import com.unboundid.ldap.sdk.schema.MatchingRuleUseDefinition; 069import com.unboundid.ldap.sdk.schema.NameFormDefinition; 070import com.unboundid.ldap.sdk.schema.ObjectClassDefinition; 071import com.unboundid.ldap.sdk.schema.Schema; 072import com.unboundid.ldap.sdk.unboundidds.controls. 073 ExtendedSchemaInfoRequestControl; 074import com.unboundid.util.Debug; 075import com.unboundid.util.MultiServerLDAPCommandLineTool; 076import com.unboundid.util.NotNull; 077import com.unboundid.util.Nullable; 078import com.unboundid.util.OID; 079import com.unboundid.util.StaticUtils; 080import com.unboundid.util.ThreadSafety; 081import com.unboundid.util.ThreadSafetyLevel; 082import com.unboundid.util.args.ArgumentException; 083import com.unboundid.util.args.ArgumentParser; 084import com.unboundid.util.args.BooleanArgument; 085import com.unboundid.util.args.DNArgument; 086import com.unboundid.util.args.StringArgument; 087 088import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*; 089 090 091 092/** 093 * This class implements a command-line tool that can be used to retrieve the 094 * schemas from two LDAP servers and identify any differences between them. 095 * <BR> 096 * <BLOCKQUOTE> 097 * <B>NOTE:</B> This class, and other classes within the 098 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 099 * supported for use against Ping Identity, UnboundID, and 100 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 101 * for proprietary functionality or for external specifications that are not 102 * considered stable or mature enough to be guaranteed to work in an 103 * interoperable way with other types of LDAP servers. 104 * </BLOCKQUOTE> 105 * <BR> 106 * Comparisons that this tool may perform include: 107 * <UL> 108 * <LI>Definitions that are present in one server but not another.</LI> 109 * <LI>Corresponding definitions with the same OID but different names or sets 110 * of names.</LI> 111 * <LI>Corresponding definitions with different descriptions, obsolete state, 112 * or sets of extensions.</LI> 113 * <LI>Corresponding attribute types with differences in syntaxes, matching 114 * rules, superior type, single-valued/multivalued behavior, usage, 115 * collective state, or NO-USER-MODIFICATION state.</LI> 116 * <LI>Corresponding object classes with differences in required or optional 117 * attributes, superior class, or object class type.</LI> 118 * <LI>Corresponding DIT content rules with differences in required, optional, 119 * or prohibited attributes, or allowed auxiliary classes.</LI> 120 * <LI>Corresponding name forms with differences in structural class, 121 * required attributes, or optional attributes.</LI> 122 * <LI>Corresponding DIT structure rules with different name form IDs or 123 * superior rule IDs.</LI> 124 * <LI>Corresponding matching rule uses with different sets of applicable 125 * attribute types.</LI> 126 * </UL> 127 */ 128@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 129public final class CompareLDAPSchemas 130 extends MultiServerLDAPCommandLineTool 131{ 132 /** 133 * The column at which long lines should be wrapped. 134 */ 135 private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; 136 137 138 139 /** 140 * The index number used to reference the first server. 141 */ 142 private static final int FIRST_SERVER_INDEX = 0; 143 144 145 146 /** 147 * The index number used to reference the second server. 148 */ 149 private static final int SECOND_SERVER_INDEX = 1; 150 151 152 153 /** 154 * The name of the command-line argument used to indicate that the tool should 155 * not examine schema elements that have an extension with a given name and 156 * value. 157 */ 158 @NotNull private static final String 159 ARG_NAME_EXCLUDE_ELEMENTS_WITH_EXTENSION_VALUE = 160 "excludeElementsWithExtensionValue"; 161 162 163 164 /** 165 * The name of the command-line argument used to indicate that the tool should 166 * not examine schema elements with names matching a specified prefix. 167 */ 168 @NotNull private static final String 169 ARG_NAME_EXCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX = 170 "excludeElementsWithNameMatchingPrefix"; 171 172 173 174 /** 175 * The name of the command-line argument used to specify the DN of the first 176 * server's subschema subentry. 177 */ 178 @NotNull private static final String ARG_NAME_FIRST_SCHEMA_ENTRY_DN = 179 "firstSchemaEntryDN"; 180 181 182 183 /** 184 * The name of the command-line argument used to indicate that the tool should 185 * use the get extended schema info request control if the server reports that 186 * it is supported. 187 */ 188 @NotNull private static final String ARG_NAME_GET_EXTENDED_SCHEMA_INFO = 189 "getExtendedSchemaInfo"; 190 191 192 193 /** 194 * The name of the command-line argument used to indicate that the tool should 195 * ignore differences in element descriptions. 196 */ 197 @NotNull private static final String ARG_NAME_IGNORE_DESCRIPTIONS = 198 "ignoreDescriptions"; 199 200 201 202 /** 203 * The name of the command-line argument used to indicate that the tool should 204 * ignore differences in element extensions. 205 */ 206 @NotNull private static final String ARG_NAME_IGNORE_EXTENSIONS = 207 "ignoreExtensions"; 208 209 210 211 /** 212 * The name of the command-line argument used to indicate that the tool should 213 * only examine schema elements that have an extension with a given name and 214 * value. 215 */ 216 @NotNull private static final String 217 ARG_NAME_INCLUDE_ELEMENTS_WITH_EXTENSION_VALUE = 218 "includeElementsWithExtensionValue"; 219 220 221 222 /** 223 * The name of the command-line argument used to indicate that the tool should 224 * only examine schema elements with names matching a specified prefix. 225 */ 226 @NotNull private static final String 227 ARG_NAME_INCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX = 228 "includeElementsWithNameMatchingPrefix"; 229 230 231 232 /** 233 * The name of the command-line argument used to specify the types of schema 234 * elements that the server should examine. 235 */ 236 @NotNull private static final String ARG_NAME_SCHEMA_ELEMENT_TYPE = 237 "schemaElementType"; 238 239 240 241 /** 242 * The name of the command-line argument used to specify the DN of the second 243 * server's subschema subentry. 244 */ 245 @NotNull private static final String ARG_NAME_SECOND_SCHEMA_ENTRY_DN = 246 "secondSchemaEntryDN"; 247 248 249 250 /** 251 * The name of the schema element type value that indicates that the tool 252 * should examine attribute syntaxes. 253 */ 254 @NotNull private static final String SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES = 255 "attribute-syntaxes"; 256 257 258 259 /** 260 * The name of the schema element type value that indicates that the tool 261 * should examine attribute types. 262 */ 263 @NotNull private static final String SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES = 264 "attribute-types"; 265 266 267 268 /** 269 * The name of the schema element type value that indicates that the tool 270 * should examine DIT content rules. 271 */ 272 @NotNull private static final String SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES = 273 "dit-content-rules"; 274 275 276 277 /** 278 * The name of the schema element type value that indicates that the tool 279 * should examine DIT structure rules. 280 */ 281 @NotNull private static final String SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES = 282 "dit-structure-rules"; 283 284 285 286 /** 287 * The name of the schema element type value that indicates that the tool 288 * should examine matching rule uses. 289 */ 290 @NotNull private static final String SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES = 291 "matching-rule-uses"; 292 293 294 295 /** 296 * The name of the schema element type value that indicates that the tool 297 * should examine matching rules. 298 */ 299 @NotNull private static final String SCHEMA_ELEMENT_TYPE_MATCHING_RULES = 300 "matching-rules"; 301 302 303 304 /** 305 * The name of the schema element type value that indicates that the tool 306 * should examine object classes. 307 */ 308 @NotNull private static final String SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES = 309 "object-classes"; 310 311 312 313 /** 314 * The name of the schema element type value that indicates that the tool 315 * should examine name forms. 316 */ 317 @NotNull private static final String SCHEMA_ELEMENT_TYPE_NAME_FORMS = 318 "name-forms"; 319 320 321 322 // A reference to the argument parser for this tool. 323 @NotNull private final AtomicReference<ArgumentParser> parserRef; 324 325 // A reference to the completion message for this tool. 326 @NotNull private final AtomicReference<String> completionMessageRef; 327 328 // Indicates whether to ignore differences in schema element descriptions. 329 private boolean ignoreDescriptions; 330 331 // Indicates whether to ignore differences in schema element extensions. 332 private boolean ignoreExtensions; 333 334 // Indicates whether we may include or exclude schema elements based on their 335 // extensions. 336 private boolean includeOrExcludeBasedOnExtensions; 337 338 // Indicates whether we may include or exclude schema elements based on their 339 // name. 340 private boolean includeOrExcludeBasedOnName; 341 342 // A list of name prefixes for schema elements to exclude from the comparison. 343 @NotNull private final List<String> excludeNamePrefixes; 344 345 // A list of name prefixes for schema elements to include in the comparison. 346 @NotNull private final List<String> includeNamePrefixes; 347 348 // A map of schema extension values for schema elements to exclude from the 349 // comparison. 350 @NotNull private final Map<String,List<String>> excludeExtensionValues; 351 352 // A map of schema extension values for schema elements to include in the 353 // comparison. 354 @NotNull private final Map<String,List<String>> includeExtensionValues; 355 356 // The set of schema element types to examine. 357 @NotNull private final Set<String> schemaElementTypes; 358 359 360 361 /** 362 * Runs this tool with the provided set of arguments, using the default 363 * streams for standard output and standard error. 364 * 365 * @param args The command-line arguments to use to run this program. It 366 * must not be {@code null}, but may be empty. 367 */ 368 public static void main(@NotNull final String... args) 369 { 370 final ResultCode resultCode = main(System.out, System.err, args); 371 if (resultCode != ResultCode.SUCCESS) 372 { 373 System.exit(resultCode.intValue()); 374 } 375 } 376 377 378 379 /** 380 * Runs this tool with the provided set of arguments, using the provided 381 * streams for standard output and standard error. 382 * 383 * @param out The output stream to use for standard output. It may be 384 * {@code null} if standard output should be suppressed. 385 * @param err The output stream to use for standard error. It may be 386 * {@code null} if standard error should be suppressed. 387 * @param args The command-line arguments to use to run this program. It 388 * must not be {@code null}, but may be empty. 389 * 390 * @return The result code with information about the result of processing. 391 * A result code of {@code SUCCESS} indicates that all processing 392 * completed successfully and no differences were identified. A 393 * result code of {@code COMPARE_FALSE} indicates that all processing 394 * completed successfully, but one or more differences were 395 * identified between the server schemas. Any other result code 396 * indicates that some problem occurred during processing. 397 */ 398 @NotNull() 399 public static ResultCode main(@Nullable final OutputStream out, 400 @Nullable final OutputStream err, 401 @NotNull final String... args) 402 { 403 final CompareLDAPSchemas tool = new CompareLDAPSchemas(out, err); 404 return tool.runTool(args); 405 } 406 407 408 409 /** 410 * Creates a new instance of this tool with the provided streams for standard 411 * output and standard error. 412 * 413 * @param out The output stream to use for standard output. It may be 414 * {@code null} if standard output should be suppressed. 415 * @param err The output stream to use for standard error. It may be 416 * {@code null} if standard error should be suppressed. 417 */ 418 public CompareLDAPSchemas(@Nullable final OutputStream out, 419 @Nullable final OutputStream err) 420 { 421 super(out, err, new String[] { "first", "second" }, null); 422 423 parserRef = new AtomicReference<>(); 424 completionMessageRef = new AtomicReference<>(); 425 426 schemaElementTypes = new HashSet<>(StaticUtils.setOf( 427 SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES, 428 SCHEMA_ELEMENT_TYPE_MATCHING_RULES, 429 SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES, 430 SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES, 431 SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES, 432 SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES, 433 SCHEMA_ELEMENT_TYPE_NAME_FORMS, 434 SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)); 435 436 ignoreDescriptions = false; 437 ignoreExtensions = false; 438 includeNamePrefixes = new ArrayList<>(); 439 excludeNamePrefixes = new ArrayList<>(); 440 includeExtensionValues = new HashMap<>(); 441 excludeExtensionValues = new HashMap<>(); 442 } 443 444 445 446 /** 447 * {@inheritDoc} 448 */ 449 @Override() 450 @NotNull() 451 public String getToolName() 452 { 453 return "compare-ldap-schemas"; 454 } 455 456 457 458 /** 459 * {@inheritDoc} 460 */ 461 @Override() 462 @NotNull() 463 public String getToolDescription() 464 { 465 return INFO_COMPARE_SCHEMA_TOOL_DESC.get(); 466 } 467 468 469 470 /** 471 * {@inheritDoc} 472 */ 473 @Override() 474 @NotNull() 475 public String getToolVersion() 476 { 477 return Version.getNumericVersionString(); 478 } 479 480 481 482 /** 483 * {@inheritDoc} 484 */ 485 @Override() 486 protected boolean includeAlternateLongIdentifiers() 487 { 488 return true; 489 } 490 491 492 493 /** 494 * {@inheritDoc} 495 */ 496 @Override() 497 public void addNonLDAPArguments(@NotNull final ArgumentParser parser) 498 throws ArgumentException 499 { 500 parserRef.set(parser); 501 502 final DNArgument firstSchemaEntryDNArg = new DNArgument(null, 503 ARG_NAME_FIRST_SCHEMA_ENTRY_DN, false, 1, null, 504 INFO_COMPARE_SCHEMA_ARG_DESC_FIRST_SCHEMA_ENTRY_DN.get()); 505 firstSchemaEntryDNArg.addLongIdentifier("first-schema-entry-dn", true); 506 firstSchemaEntryDNArg.addLongIdentifier("firstSchemaEntry", true); 507 firstSchemaEntryDNArg.addLongIdentifier("first-schema-entry", true); 508 firstSchemaEntryDNArg.addLongIdentifier("firstSchemaDN", true); 509 firstSchemaEntryDNArg.addLongIdentifier("first-schema-dn", true); 510 firstSchemaEntryDNArg.addLongIdentifier("firstSchema", true); 511 firstSchemaEntryDNArg.addLongIdentifier("first-schema", true); 512 parser.addArgument(firstSchemaEntryDNArg); 513 514 final DNArgument secondSchemaEntryDNArg = new DNArgument(null, 515 ARG_NAME_SECOND_SCHEMA_ENTRY_DN, false, 1, null, 516 INFO_COMPARE_SCHEMA_ARG_DESC_SECOND_SCHEMA_ENTRY_DN.get()); 517 secondSchemaEntryDNArg.addLongIdentifier("second-schema-entry-dn", true); 518 secondSchemaEntryDNArg.addLongIdentifier("secondSchemaEntry", true); 519 secondSchemaEntryDNArg.addLongIdentifier("second-schema-entry", true); 520 secondSchemaEntryDNArg.addLongIdentifier("secondSchemaDN", true); 521 secondSchemaEntryDNArg.addLongIdentifier("second-schema-dn", true); 522 secondSchemaEntryDNArg.addLongIdentifier("secondSchema", true); 523 secondSchemaEntryDNArg.addLongIdentifier("second-schema", true); 524 parser.addArgument(secondSchemaEntryDNArg); 525 526 final StringArgument schemaElementTypesArg = new StringArgument(null, 527 ARG_NAME_SCHEMA_ELEMENT_TYPE, false, 0, 528 INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_SCHEMA_ELEMENT_TYPE.get(), 529 INFO_COMPARE_SCHEMA_ARG_DESC_SCHEMA_ELEMENT_TYPE.get( 530 SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES, 531 SCHEMA_ELEMENT_TYPE_MATCHING_RULES, 532 SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES, 533 SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES, 534 SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES, 535 SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES, 536 SCHEMA_ELEMENT_TYPE_NAME_FORMS, 537 SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)); 538 schemaElementTypesArg.addLongIdentifier("schema-element-types", true); 539 parser.addArgument(schemaElementTypesArg); 540 541 final BooleanArgument getExtendedSchemaInfoArg = new BooleanArgument(null, 542 ARG_NAME_GET_EXTENDED_SCHEMA_INFO, 1, 543 INFO_COMPARE_SCHEMA_ARG_DESC_GET_EXTENDED_SCHEMA_INFO.get()); 544 getExtendedSchemaInfoArg.addLongIdentifier("get-extended-schema-info", 545 true); 546 parser.addArgument(getExtendedSchemaInfoArg); 547 548 final BooleanArgument ignoreDescriptionsArg = new BooleanArgument(null, 549 ARG_NAME_IGNORE_DESCRIPTIONS, 1, 550 INFO_COMPARE_SCHEMA_ARG_DESC_IGNORE_DESCRIPTIONS.get()); 551 ignoreDescriptionsArg.addLongIdentifier("ignore-descriptions", true); 552 ignoreDescriptionsArg.addLongIdentifier("ignoreDescription", true); 553 ignoreDescriptionsArg.addLongIdentifier("ignore-description", true); 554 parser.addArgument(ignoreDescriptionsArg); 555 556 final BooleanArgument ignoreExtensionsArg = new BooleanArgument(null, 557 ARG_NAME_IGNORE_EXTENSIONS, 1, 558 INFO_COMPARE_SCHEMA_ARG_DESC_IGNORE_EXTENSIONS.get()); 559 ignoreExtensionsArg.addLongIdentifier("ignore-extensions", true); 560 ignoreExtensionsArg.addLongIdentifier("ignoreExtension", true); 561 ignoreExtensionsArg.addLongIdentifier("ignore-extension", true); 562 parser.addArgument(ignoreExtensionsArg); 563 564 final StringArgument includeElementsWithNameMatchingPrefixArg = 565 new StringArgument(null, 566 ARG_NAME_INCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, false, 0, 567 INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_PREFIX.get(), 568 INFO_COMPARE_SCHEMA_ARG_DESC_INCLUDE_NAME_MATCHING_PREFIX.get()); 569 includeElementsWithNameMatchingPrefixArg.addLongIdentifier( 570 "include-elements-with-name-matching-prefix", true); 571 parser.addArgument(includeElementsWithNameMatchingPrefixArg); 572 573 final StringArgument excludeElementsWithNameMatchingPrefixArg = 574 new StringArgument(null, 575 ARG_NAME_EXCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, false, 0, 576 INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_PREFIX.get(), 577 INFO_COMPARE_SCHEMA_ARG_DESC_EXCLUDE_NAME_MATCHING_PREFIX.get()); 578 excludeElementsWithNameMatchingPrefixArg.addLongIdentifier( 579 "exclude-elements-with-name-matching-prefix", true); 580 parser.addArgument(excludeElementsWithNameMatchingPrefixArg); 581 582 final StringArgument includeElementsWithExtensionValueArg = 583 new StringArgument(null, 584 ARG_NAME_INCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, false, 0, 585 INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_EXTENSION_VALUE.get(), 586 INFO_COMPARE_SCHEMA_ARG_DESC_INCLUDE_EXTENSION_VALUE.get()); 587 includeElementsWithExtensionValueArg.addLongIdentifier( 588 "include-elements-with-extension-value", true); 589 parser.addArgument(includeElementsWithExtensionValueArg); 590 591 final StringArgument excludeElementsWithExtensionValueArg = 592 new StringArgument(null, 593 ARG_NAME_EXCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, false, 0, 594 INFO_COMPARE_SCHEMA_ARG_PLACEHOLDER_EXTENSION_VALUE.get(), 595 INFO_COMPARE_SCHEMA_ARG_DESC_EXCLUDE_EXTENSION_VALUE.get()); 596 excludeElementsWithExtensionValueArg.addLongIdentifier( 597 "exclude-elements-with-extension-value", true); 598 parser.addArgument(excludeElementsWithExtensionValueArg); 599 } 600 601 602 603 /** 604 * {@inheritDoc} 605 */ 606 @Override() 607 public void doExtendedNonLDAPArgumentValidation() 608 throws ArgumentException 609 { 610 // Identify the types of schema elements to examine. 611 final ArgumentParser parser = parserRef.get(); 612 final StringArgument schemaElementTypesArg = 613 parser.getStringArgument(ARG_NAME_SCHEMA_ELEMENT_TYPE); 614 if ((schemaElementTypesArg != null) && schemaElementTypesArg.isPresent()) 615 { 616 schemaElementTypes.clear(); 617 for (final String value : schemaElementTypesArg.getValues()) 618 { 619 if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES)) 620 { 621 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES); 622 } 623 else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_MATCHING_RULES)) 624 { 625 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_MATCHING_RULES); 626 } 627 else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES)) 628 { 629 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES); 630 } 631 else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES)) 632 { 633 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES); 634 } 635 else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES)) 636 { 637 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES); 638 } 639 else if (value.equalsIgnoreCase( 640 SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES)) 641 { 642 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES); 643 } 644 else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_NAME_FORMS)) 645 { 646 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_NAME_FORMS); 647 } 648 else if (value.equalsIgnoreCase(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)) 649 { 650 schemaElementTypes.add(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES); 651 } 652 else 653 { 654 throw new ArgumentException( 655 ERR_COMPARE_SCHEMA_INVALID_SCHEMA_ELEMENT_TYPE.get(value, 656 ARG_NAME_SCHEMA_ELEMENT_TYPE, 657 SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES, 658 SCHEMA_ELEMENT_TYPE_MATCHING_RULES, 659 SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES, 660 SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES, 661 SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES, 662 SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES, 663 SCHEMA_ELEMENT_TYPE_NAME_FORMS, 664 SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)); 665 } 666 } 667 } 668 669 670 // Determine whether to ignore schema element descriptions or extensions. 671 final BooleanArgument ignoreDescriptionsArg = 672 parser.getBooleanArgument(ARG_NAME_IGNORE_DESCRIPTIONS); 673 ignoreDescriptions = 674 ((ignoreDescriptionsArg != null) && ignoreDescriptionsArg.isPresent()); 675 676 final BooleanArgument ignoreExtensionsArg = 677 parser.getBooleanArgument(ARG_NAME_IGNORE_EXTENSIONS); 678 ignoreExtensions = 679 ((ignoreExtensionsArg != null) && ignoreExtensionsArg.isPresent()); 680 681 682 // Identify the schema element name prefixes to include and exclude. 683 getNamePrefixes(ARG_NAME_INCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, 684 includeNamePrefixes); 685 getNamePrefixes(ARG_NAME_EXCLUDE_ELEMENTS_WITH_NAME_MATCHING_PREFIX, 686 excludeNamePrefixes); 687 includeOrExcludeBasedOnName = (! includeNamePrefixes.isEmpty()) || 688 (! excludeNamePrefixes.isEmpty()); 689 690 691 // Identify the schema element extension values to include and exclude. 692 getExtensionValues(ARG_NAME_INCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, 693 includeExtensionValues); 694 getExtensionValues(ARG_NAME_EXCLUDE_ELEMENTS_WITH_EXTENSION_VALUE, 695 excludeExtensionValues); 696 includeOrExcludeBasedOnExtensions = (! includeExtensionValues.isEmpty()) || 697 (! excludeExtensionValues.isEmpty()); 698 } 699 700 701 702 /** 703 * Populates the provided list with the set of schema element prefixes 704 * contained in the specified argument. 705 * 706 * @param argumentName The name of the argument whose values will be used to 707 * populate the given list. 708 * @param prefixList The list to be updated to include the values of the 709 * specified argument. 710 */ 711 private void getNamePrefixes(@NotNull final String argumentName, 712 @NotNull final List<String> prefixList) 713 { 714 prefixList.clear(); 715 final StringArgument arg = parserRef.get().getStringArgument(argumentName); 716 if ((arg == null) || (! arg.isPresent())) 717 { 718 return; 719 } 720 721 for (final String value : arg.getValues()) 722 { 723 prefixList.add(StaticUtils.toLowerCase(value)); 724 } 725 } 726 727 728 729 /** 730 * Populates the provided map with the set of schema element extension 731 * name-value pairs contained in the specified argument. 732 * 733 * @param argumentName The name of the argument whose values will be used to 734 * populate the given map. 735 * @param extensionMap The map to be updated to include the values of the 736 * specified argument. 737 * 738 * @throws ArgumentException If there is a problem with any of the values of 739 * the specified argument. 740 */ 741 private void getExtensionValues( 742 @NotNull final String argumentName, 743 @NotNull final Map<String,List<String>> extensionMap) 744 throws ArgumentException 745 { 746 extensionMap.clear(); 747 final StringArgument arg = parserRef.get().getStringArgument(argumentName); 748 if ((arg == null) || (! arg.isPresent())) 749 { 750 return; 751 } 752 753 for (final String value : arg.getValues()) 754 { 755 final int equalPos = value.indexOf('='); 756 if (equalPos < 0) 757 { 758 throw new ArgumentException( 759 ERR_COMPARE_SCHEMA_EXTENSION_VALUE_NO_EQUALS.get(argumentName, 760 value)); 761 } 762 763 final String extensionName = 764 StaticUtils.toLowerCase(value.substring(0, equalPos)); 765 if (extensionName.isEmpty()) 766 { 767 throw new ArgumentException( 768 ERR_COMPARE_SCHEMA_EXTENSION_VALUE_EMPTY_NAME.get(argumentName, 769 value)); 770 } 771 772 final String extensionValue = 773 StaticUtils.toLowerCase(value.substring(equalPos + 1)); 774 if (extensionValue.isEmpty()) 775 { 776 throw new ArgumentException( 777 ERR_COMPARE_SCHEMA_EXTENSION_VALUE_EMPTY_VALUE.get(argumentName, 778 value)); 779 } 780 781 List<String> valueList = extensionMap.get(extensionName); 782 if (valueList == null) 783 { 784 valueList = new ArrayList<>(); 785 extensionMap.put(extensionName, valueList); 786 } 787 788 valueList.add(extensionValue); 789 } 790 } 791 792 793 794 /** 795 * {@inheritDoc} 796 */ 797 @Override() 798 public boolean supportsInteractiveMode() 799 { 800 return true; 801 } 802 803 804 805 /** 806 * {@inheritDoc} 807 */ 808 @Override() 809 public boolean defaultsToInteractiveMode() 810 { 811 return true; 812 } 813 814 815 816 /** 817 * {@inheritDoc} 818 */ 819 @Override() 820 public boolean supportsPropertiesFile() 821 { 822 return true; 823 } 824 825 826 827 /** 828 * {@inheritDoc} 829 */ 830 @Override() 831 protected boolean supportsOutputFile() 832 { 833 return true; 834 } 835 836 837 838 /** 839 * {@inheritDoc} 840 */ 841 @Override() 842 protected boolean logToolInvocationByDefault() 843 { 844 return false; 845 } 846 847 848 849 /** 850 * {@inheritDoc} 851 */ 852 @Override() 853 @Nullable() 854 protected String getToolCompletionMessage() 855 { 856 return completionMessageRef.get(); 857 } 858 859 860 861 /** 862 * {@inheritDoc} 863 */ 864 @Override() 865 @NotNull() 866 public ResultCode doToolProcessing() 867 { 868 // Get the schemas from each of the servers. 869 final Schema firstServerSchema; 870 final Map<String,LDAPException> firstUnparsableAttributeSyntaxes = 871 new LinkedHashMap<>(); 872 final Map<String,LDAPException> firstUnparsableMatchingRules = 873 new LinkedHashMap<>(); 874 final Map<String,LDAPException> firstUnparsableAttributeTypes = 875 new LinkedHashMap<>(); 876 final Map<String,LDAPException> firstUnparsableObjectClasses = 877 new LinkedHashMap<>(); 878 final Map<String,LDAPException> firstUnparsableDITContentRules = 879 new LinkedHashMap<>(); 880 final Map<String,LDAPException> firstUnparsableDITStructureRules = 881 new LinkedHashMap<>(); 882 final Map<String,LDAPException> firstUnparsableNameForms = 883 new LinkedHashMap<>(); 884 final Map<String,LDAPException> firstUnparsableMatchingRuleUses = 885 new LinkedHashMap<>(); 886 try 887 { 888 firstServerSchema = getSchema(FIRST_SERVER_INDEX, 889 ARG_NAME_FIRST_SCHEMA_ENTRY_DN, 890 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 891 firstUnparsableAttributeSyntaxes, firstUnparsableMatchingRules, 892 firstUnparsableAttributeTypes, firstUnparsableObjectClasses, 893 firstUnparsableDITContentRules, firstUnparsableDITStructureRules, 894 firstUnparsableNameForms, firstUnparsableMatchingRuleUses); 895 } 896 catch (final LDAPException e) 897 { 898 logCompletionError(e.getMessage()); 899 return e.getResultCode(); 900 } 901 902 final Schema secondServerSchema; 903 final Map<String,LDAPException> secondUnparsableAttributeSyntaxes = 904 new LinkedHashMap<>(); 905 final Map<String,LDAPException> secondUnparsableMatchingRules = 906 new LinkedHashMap<>(); 907 final Map<String,LDAPException> secondUnparsableAttributeTypes = 908 new LinkedHashMap<>(); 909 final Map<String,LDAPException> secondUnparsableObjectClasses = 910 new LinkedHashMap<>(); 911 final Map<String,LDAPException> secondUnparsableDITContentRules = 912 new LinkedHashMap<>(); 913 final Map<String,LDAPException> secondUnparsableDITStructureRules = 914 new LinkedHashMap<>(); 915 final Map<String,LDAPException> secondUnparsableNameForms = 916 new LinkedHashMap<>(); 917 final Map<String,LDAPException> secondUnparsableMatchingRuleUses = 918 new LinkedHashMap<>(); 919 try 920 { 921 secondServerSchema = getSchema(SECOND_SERVER_INDEX, 922 ARG_NAME_SECOND_SCHEMA_ENTRY_DN, 923 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 924 secondUnparsableAttributeSyntaxes, secondUnparsableMatchingRules, 925 secondUnparsableAttributeTypes, secondUnparsableObjectClasses, 926 secondUnparsableDITContentRules, secondUnparsableDITStructureRules, 927 secondUnparsableNameForms, secondUnparsableMatchingRuleUses); 928 } 929 catch (final LDAPException e) 930 { 931 logCompletionError(e.getMessage()); 932 return e.getResultCode(); 933 } 934 935 936 // Report on any unparsable schema elements. 937 final AtomicReference<ResultCode> resultCodeRef = new AtomicReference<>(); 938 boolean unparsableElementsEncountered = reportUnparsableSchemaElements( 939 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 940 firstUnparsableAttributeSyntaxes, firstUnparsableMatchingRules, 941 firstUnparsableAttributeTypes, firstUnparsableObjectClasses, 942 firstUnparsableDITContentRules, firstUnparsableDITStructureRules, 943 firstUnparsableNameForms, firstUnparsableMatchingRuleUses); 944 945 unparsableElementsEncountered |= reportUnparsableSchemaElements( 946 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 947 secondUnparsableAttributeSyntaxes, secondUnparsableMatchingRules, 948 secondUnparsableAttributeTypes, secondUnparsableObjectClasses, 949 secondUnparsableDITContentRules, secondUnparsableDITStructureRules, 950 secondUnparsableNameForms, secondUnparsableMatchingRuleUses); 951 952 if (unparsableElementsEncountered) 953 { 954 resultCodeRef.set(ResultCode.INVALID_ATTRIBUTE_SYNTAX); 955 } 956 957 958 // Validate the different types of schema elements. 959 final AtomicInteger numDifferences = new AtomicInteger(); 960 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES)) 961 { 962 compareAttributeSyntaxes(firstServerSchema, secondServerSchema, 963 numDifferences); 964 } 965 966 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULES)) 967 { 968 compareMatchingRules(firstServerSchema, secondServerSchema, 969 numDifferences); 970 } 971 972 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES)) 973 { 974 compareAttributeTypes(firstServerSchema, secondServerSchema, 975 numDifferences); 976 } 977 978 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES)) 979 { 980 compareObjectClasses(firstServerSchema, secondServerSchema, 981 numDifferences); 982 } 983 984 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES)) 985 { 986 compareDITContentRules(firstServerSchema, secondServerSchema, 987 numDifferences); 988 } 989 990 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES)) 991 { 992 compareDITStructureRules(firstServerSchema, secondServerSchema, 993 numDifferences); 994 } 995 996 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_NAME_FORMS)) 997 { 998 compareNameForms(firstServerSchema, secondServerSchema, numDifferences); 999 } 1000 1001 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)) 1002 { 1003 compareMatchingRuleUses(firstServerSchema, secondServerSchema, 1004 numDifferences); 1005 } 1006 1007 1008 // If any errors were encountered, then return an error result code. 1009 // Otherwise, if any differences were encountered, then return a 1010 // COMPARE_FALSE result code. Otherwise, return a SUCCESS result code. 1011 final int differenceCount = numDifferences.get(); 1012 if (unparsableElementsEncountered) 1013 { 1014 switch (differenceCount) 1015 { 1016 case 0: 1017 logCompletionError( 1018 ERR_COMPARE_SCHEMA_SUMMARY_UNPARSABLE_NO_DIFFERENCES.get()); 1019 break; 1020 case 1: 1021 logCompletionError( 1022 ERR_COMPARE_SCHEMA_SUMMARY_UNPARSABLE_WITH_DIFFERENCE.get()); 1023 break; 1024 default: 1025 logCompletionError( 1026 ERR_COMPARE_SCHEMA_SUMMARY_UNPARSABLE_WITH_DIFFERENCES.get( 1027 differenceCount)); 1028 break; 1029 } 1030 } 1031 else if (differenceCount > 0) 1032 { 1033 resultCodeRef.compareAndSet(null, ResultCode.COMPARE_FALSE); 1034 if (differenceCount == 1) 1035 { 1036 logCompletionError( 1037 ERR_COMPARE_SCHEMA_SUMMARY_DIFFERENCE.get()); 1038 } 1039 else 1040 { 1041 logCompletionError( 1042 ERR_COMPARE_SCHEMA_SUMMARY_DIFFERENCES.get(differenceCount)); 1043 } 1044 } 1045 else 1046 { 1047 resultCodeRef.compareAndSet(null, ResultCode.SUCCESS); 1048 final String message = INFO_COMPARE_SCHEMA_SUMMARY_NO_DIFFERENCES.get(); 1049 completionMessageRef.compareAndSet(null, message); 1050 wrapOut(0, WRAP_COLUMN, message); 1051 } 1052 1053 return resultCodeRef.get(); 1054 } 1055 1056 1057 1058 /** 1059 * Retrieves the schema from the specified server. 1060 * 1061 * @param serverIndex 1062 * The index for the server from which to retrieve the schema. 1063 * @param schemaDNArgName 1064 * The name of the argument to use to retrieve the DN of the 1065 * subschema subentry, if specified. It must not be 1066 * {@code null}. 1067 * @param serverLabel 1068 * The label to use to refer to the server. It must not be 1069 * {@code null}. 1070 * @param unparsableAttributeSyntaxes 1071 * A map that will be updated with information about any 1072 * unparsable attribute syntax definitions found in the schema 1073 * from the specified server. Each key will be the unparsable 1074 * definition, and the corresponding value will be the exception 1075 * caught while trying to parse it. It must not be {@code null}. 1076 * @param unparsableMatchingRules 1077 * A map that will be updated with information about any 1078 * unparsable matching rule definitions found in the schema 1079 * from the specified server. Each key will be the unparsable 1080 * definition, and the corresponding value will be the exception 1081 * caught while trying to parse it. It must not be {@code null}. 1082 * @param unparsableAttributeTypes 1083 * A map that will be updated with information about any 1084 * unparsable attribute type definitions found in the schema 1085 * from the specified server. Each key will be the unparsable 1086 * definition, and the corresponding value will be the exception 1087 * caught while trying to parse it. It must not be {@code null}. 1088 * @param unparsableObjectClasses 1089 * A map that will be updated with information about any 1090 * unparsable object class definitions found in the schema 1091 * from the specified server. Each key will be the unparsable 1092 * definition, and the corresponding value will be the exception 1093 * caught while trying to parse it. It must not be {@code null}. 1094 * @param unparsableDITContentRules 1095 * A map that will be updated with information about any 1096 * unparsable DIT content rule definitions found in the schema 1097 * from the specified server. Each key will be the unparsable 1098 * definition, and the corresponding value will be the exception 1099 * caught while trying to parse it. It must not be {@code null}. 1100 * @param unparsableDITStructureRules 1101 * A map that will be updated with information about any 1102 * unparsable DIT structure rule definitions found in the schema 1103 * from the specified server. Each key will be the unparsable 1104 * definition, and the corresponding value will be the exception 1105 * caught while trying to parse it. It must not be {@code null}. 1106 * @param unparsableNameForms 1107 * A map that will be updated with information about any 1108 * unparsable name form definitions found in the schema 1109 * from the specified server. Each key will be the unparsable 1110 * definition, and the corresponding value will be the exception 1111 * caught while trying to parse it. It must not be {@code null}. 1112 * @param unparsableMatchingRuleUses 1113 * A map that will be updated with information about any 1114 * unparsable matching rule use definitions found in the schema 1115 * from the specified server. Each key will be the unparsable 1116 * definition, and the corresponding value will be the exception 1117 * caught while trying to parse it. It must not be {@code null}. 1118 * 1119 * @return The schema retrieved from the server. 1120 * 1121 * @throws LDAPException If a problem occurs while attempting to obtain the 1122 * schema. 1123 */ 1124 @NotNull() 1125 private Schema getSchema(final int serverIndex, 1126 @NotNull final String schemaDNArgName, 1127 @NotNull final String serverLabel, 1128 @NotNull final Map<String,LDAPException> unparsableAttributeSyntaxes, 1129 @NotNull final Map<String,LDAPException> unparsableMatchingRules, 1130 @NotNull final Map<String,LDAPException> unparsableAttributeTypes, 1131 @NotNull final Map<String,LDAPException> unparsableObjectClasses, 1132 @NotNull final Map<String,LDAPException> unparsableDITContentRules, 1133 @NotNull final Map<String,LDAPException> unparsableDITStructureRules, 1134 @NotNull final Map<String,LDAPException> unparsableNameForms, 1135 @NotNull final Map<String,LDAPException> unparsableMatchingRuleUses) 1136 throws LDAPException 1137 { 1138 // Establish a connection to the server. 1139 final LDAPConnection conn; 1140 try 1141 { 1142 conn = getConnection(serverIndex); 1143 } 1144 catch (final LDAPException e) 1145 { 1146 Debug.debugException(e); 1147 throw new LDAPException(e.getResultCode(), 1148 ERR_COMPARE_SCHEMA_CANNOT_CONNECT.get(serverLabel, e.getMessage()), 1149 e); 1150 } 1151 1152 final ArgumentParser parser = parserRef.get(); 1153 final BooleanArgument getExtendedSchemaInfoArg = 1154 parser.getBooleanArgument(ARG_NAME_GET_EXTENDED_SCHEMA_INFO); 1155 final boolean getExtendedSchemaInfo = 1156 ((getExtendedSchemaInfoArg != null) && 1157 getExtendedSchemaInfoArg.isPresent()); 1158 1159 1160 try 1161 { 1162 // See if the schema entry DN was specified as an argument. If so, then 1163 // retrieve that entry and parse it as a schema entry. Otherwise, use the 1164 // default method for obtaining the schema. 1165 final String schemaEntryDN; 1166 final DNArgument schemaEntryDNArg = parser.getDNArgument(schemaDNArgName); 1167 if (schemaEntryDNArg.isPresent()) 1168 { 1169 schemaEntryDN = schemaEntryDNArg.getStringValue(); 1170 } 1171 else 1172 { 1173 final RootDSE rootDSE = conn.getRootDSE(); 1174 if (rootDSE == null) 1175 { 1176 throw new LDAPException(ResultCode.LOCAL_ERROR, 1177 ERR_COMPARE_SCHEMA_CANNOT_GET_ROOT_DSE.get(serverLabel)); 1178 } 1179 1180 schemaEntryDN = rootDSE.getSubschemaSubentryDN(); 1181 if (schemaEntryDN == null) 1182 { 1183 throw new LDAPException(ResultCode.LOCAL_ERROR, 1184 ERR_COMPARE_SCHEMA_CANNOT_GET_ROOT_DSE_SCHEMA_DN.get(serverLabel, 1185 RootDSE.ATTR_SUBSCHEMA_SUBENTRY)); 1186 } 1187 } 1188 1189 final SearchRequest searchRequest = new SearchRequest(schemaEntryDN, 1190 SearchScope.BASE, Schema.SUBSCHEMA_SUBENTRY_FILTER, 1191 Schema.SCHEMA_REQUEST_ATTRS); 1192 if (getExtendedSchemaInfo) 1193 { 1194 searchRequest.addControl(new ExtendedSchemaInfoRequestControl(false)); 1195 } 1196 1197 final Entry schemaEntry = conn.searchForEntry(searchRequest); 1198 if (schemaEntry == null) 1199 { 1200 throw new LDAPException(ResultCode.NO_SUCH_OBJECT, 1201 ERR_COMPARE_SCHEMA_CANNOT_GET_SCHEMA_ENTRY.get( 1202 String.valueOf(schemaEntryDN), serverLabel)); 1203 } 1204 1205 return new Schema(schemaEntry, unparsableAttributeSyntaxes, 1206 unparsableMatchingRules, unparsableAttributeTypes, 1207 unparsableObjectClasses, unparsableDITContentRules, 1208 unparsableDITStructureRules, unparsableNameForms, 1209 unparsableMatchingRuleUses); 1210 } 1211 catch (final LDAPException e) 1212 { 1213 Debug.debugException(e); 1214 throw new LDAPException(e.getResultCode(), 1215 ERR_COMPARE_SCHEMA_CANNOT_GET_SCHEMA.get(serverLabel, 1216 e.getMessage()), 1217 e); 1218 } 1219 finally 1220 { 1221 conn.close(); 1222 } 1223 } 1224 1225 1226 1227 /** 1228 * Reports error messages about any unparsable elements found in a server's 1229 * schema. 1230 * 1231 * @param serverLabel 1232 * The label for the associated directory server instance. 1233 * @param unparsableAttributeSyntaxes 1234 * A map with information about any unparsable attribute syntax 1235 * definitions found in the schema. 1236 * @param unparsableMatchingRules 1237 * A map with information about any unparsable matching rule 1238 * definitions found in the schema. 1239 * @param unparsableAttributeTypes 1240 * A map with information about any unparsable attribute type 1241 * definitions found in the schema. 1242 * @param unparsableObjectClasses 1243 * A map with information about any unparsable object class 1244 * definitions found in the schema. 1245 * @param unparsableDITContentRules 1246 * A map with information about any unparsable DIT content rule 1247 * definitions found in the schema. 1248 * @param unparsableDITStructureRules 1249 * A map with information about any unparsable DIT structure rule 1250 * definitions found in the schema. 1251 * @param unparsableNameForms 1252 * A map with information about any unparsable name form 1253 * definitions found in the schema. 1254 * @param unparsableMatchingRuleUses 1255 * A map with information about any unparsable matching rule use 1256 * definitions found in the schema. 1257 * 1258 * @return {@code true} if the schema contained any unparsable elements, or 1259 * {@code false} if not. 1260 */ 1261 private boolean reportUnparsableSchemaElements( 1262 @NotNull final String serverLabel, 1263 @NotNull final Map<String,LDAPException> unparsableAttributeSyntaxes, 1264 @NotNull final Map<String,LDAPException> unparsableMatchingRules, 1265 @NotNull final Map<String,LDAPException> unparsableAttributeTypes, 1266 @NotNull final Map<String,LDAPException> unparsableObjectClasses, 1267 @NotNull final Map<String,LDAPException> unparsableDITContentRules, 1268 @NotNull final Map<String,LDAPException> unparsableDITStructureRules, 1269 @NotNull final Map<String,LDAPException> unparsableNameForms, 1270 @NotNull final Map<String,LDAPException> unparsableMatchingRuleUses) 1271 { 1272 boolean unparsableFound = false; 1273 1274 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAXES)) 1275 { 1276 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1277 unparsableAttributeSyntaxes, 1278 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get()); 1279 } 1280 1281 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULES)) 1282 { 1283 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1284 unparsableMatchingRules, 1285 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get()); 1286 } 1287 1288 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPES)) 1289 { 1290 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1291 unparsableAttributeTypes, 1292 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get()); 1293 } 1294 1295 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_OBJECT_CLASSES)) 1296 { 1297 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1298 unparsableObjectClasses, 1299 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get()); 1300 } 1301 1302 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULES)) 1303 { 1304 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1305 unparsableDITContentRules, 1306 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get()); 1307 } 1308 1309 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULES)) 1310 { 1311 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1312 unparsableDITStructureRules, 1313 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get()); 1314 } 1315 1316 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_NAME_FORMS)) 1317 { 1318 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1319 unparsableNameForms, 1320 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get()); 1321 } 1322 1323 if (schemaElementTypes.contains(SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USES)) 1324 { 1325 unparsableFound |= reportUnparsableSchemaElements(serverLabel, 1326 unparsableMatchingRuleUses, 1327 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get()); 1328 } 1329 1330 return unparsableFound; 1331 } 1332 1333 1334 1335 /** 1336 * Reports error messages about any unparsable elements of the specified type 1337 * found in a server's schema. 1338 * 1339 * @param serverLabel The label for the associated directory server 1340 * instance. It must not be {@code null}. 1341 * @param unparsableElements The set of unparsable elements of a given type. 1342 * It must not be {@code null}, but may be empty. 1343 * @param elementTypeName The name of the schema element type. It must 1344 * not be {@code null}. 1345 * 1346 * @return {@code true} if the provided map contained information about one 1347 * or more unparsable elements, or {@code false} if not. 1348 */ 1349 private boolean reportUnparsableSchemaElements( 1350 @NotNull final String serverLabel, 1351 @NotNull final Map<String,LDAPException> unparsableElements, 1352 @NotNull final String elementTypeName) 1353 { 1354 for (final Map.Entry<String,LDAPException> e : 1355 unparsableElements.entrySet()) 1356 { 1357 wrapErr(0, WRAP_COLUMN, 1358 ERR_COMPARE_SCHEMA_UNPARSABLE_ELEMENT.get(elementTypeName, 1359 serverLabel, e.getValue().getMessage())); 1360 err(e.getKey()); 1361 err(); 1362 } 1363 1364 return (! unparsableElements.isEmpty()); 1365 } 1366 1367 1368 1369 /** 1370 * Compares the attribute syntax definitions contained in the provided 1371 * schemas. 1372 * 1373 * @param firstServerSchema The schema retrieved from the first server. It 1374 * must not be {@code null}. 1375 * @param secondServerSchema The schema retrieved from the second server. 1376 * It must not be {@code null}. 1377 * @param numDifferences A counter used to keep track of the number of 1378 * differences found between the schemas. It must 1379 * not be {@code null}. 1380 */ 1381 private void compareAttributeSyntaxes( 1382 @NotNull final Schema firstServerSchema, 1383 @NotNull final Schema secondServerSchema, 1384 @NotNull final AtomicInteger numDifferences) 1385 { 1386 // Get the attribute syntax definitions from each of the schemas. 1387 final Map<OID,AttributeSyntaxDefinition> syntaxes1 = 1388 getAttributeSyntaxMap(firstServerSchema); 1389 final Map<OID,AttributeSyntaxDefinition> syntaxes2 = 1390 getAttributeSyntaxMap(secondServerSchema); 1391 1392 1393 // Identify syntaxes that exist in one server but not another. If any are 1394 // found, then report them and remove them from the set. 1395 Iterator<Map.Entry<OID,AttributeSyntaxDefinition>> iterator = 1396 syntaxes1.entrySet().iterator(); 1397 while (iterator.hasNext()) 1398 { 1399 final Map.Entry<OID,AttributeSyntaxDefinition> e = iterator.next(); 1400 final OID oid = e.getKey(); 1401 if (! syntaxes2.containsKey(oid)) 1402 { 1403 reportDifference( 1404 WARN_COMPARE_SCHEMA_MISSING_SYNTAX.get( 1405 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 1406 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 1407 numDifferences, 1408 e.getValue().toString()); 1409 iterator.remove(); 1410 } 1411 } 1412 1413 iterator = syntaxes2.entrySet().iterator(); 1414 while (iterator.hasNext()) 1415 { 1416 final Map.Entry<OID,AttributeSyntaxDefinition> e = iterator.next(); 1417 final OID oid = e.getKey(); 1418 if (! syntaxes1.containsKey(oid)) 1419 { 1420 reportDifference( 1421 WARN_COMPARE_SCHEMA_MISSING_SYNTAX.get( 1422 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 1423 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 1424 numDifferences, 1425 e.getValue().toString()); 1426 iterator.remove(); 1427 } 1428 } 1429 1430 1431 // Any remaining syntaxes should exist in both servers. Compare them and 1432 // see if there are any differences between them. 1433 for (final OID oid : syntaxes1.keySet()) 1434 { 1435 final AttributeSyntaxDefinition d1 = syntaxes1.get(oid); 1436 final AttributeSyntaxDefinition d2 = syntaxes2.get(oid); 1437 1438 if (! ignoreDescriptions) 1439 { 1440 compareStringValues( 1441 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), 1442 oid.toString(), 1443 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 1444 d1.getDescription(), d2.getDescription(), numDifferences); 1445 } 1446 1447 compareExtensions( 1448 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), 1449 oid.toString(), d1.getExtensions(), d2.getExtensions(), 1450 numDifferences); 1451 } 1452 } 1453 1454 1455 1456 /** 1457 * Retrieves a map of the attribute syntax definitions contained in the 1458 * provided schema, indexed by OID. 1459 * 1460 * @param schema The schema from which to retrieve the attribute syntaxes. 1461 * It must not be {@code null}. 1462 * 1463 * @return A map of the attribute syntax definitions contained in the 1464 * provided schema. 1465 */ 1466 @NotNull() 1467 private Map<OID,AttributeSyntaxDefinition> getAttributeSyntaxMap( 1468 @NotNull final Schema schema) 1469 { 1470 final Map<OID,AttributeSyntaxDefinition> syntaxes = new TreeMap<>(); 1471 for (final AttributeSyntaxDefinition d : schema.getAttributeSyntaxes()) 1472 { 1473 if (includeBasedOnNameAndExtensions(StaticUtils.NO_STRINGS, 1474 d.getExtensions())) 1475 { 1476 syntaxes.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); 1477 } 1478 } 1479 1480 return syntaxes; 1481 } 1482 1483 1484 1485 /** 1486 * Compares the matching rule definitions contained in the provided schemas. 1487 * 1488 * @param firstServerSchema The schema retrieved from the first server. It 1489 * must not be {@code null}. 1490 * @param secondServerSchema The schema retrieved from the second server. 1491 * It must not be {@code null}. 1492 * @param numDifferences A counter used to keep track of the number of 1493 * differences found between the schemas. It must 1494 * not be {@code null}. 1495 */ 1496 private void compareMatchingRules( 1497 @NotNull final Schema firstServerSchema, 1498 @NotNull final Schema secondServerSchema, 1499 @NotNull final AtomicInteger numDifferences) 1500 { 1501 // Get the matching rule definitions from each of the schemas. 1502 final Map<OID,MatchingRuleDefinition> matchingRules1 = 1503 getMatchingRuleMap(firstServerSchema); 1504 final Map<OID,MatchingRuleDefinition> matchingRules2 = 1505 getMatchingRuleMap(secondServerSchema); 1506 1507 1508 // Identify matching rules that exist in one server but not another. If any 1509 // are found, then report them and remove them from the set. 1510 Iterator<Map.Entry<OID,MatchingRuleDefinition>> iterator = 1511 matchingRules1.entrySet().iterator(); 1512 while (iterator.hasNext()) 1513 { 1514 final Map.Entry<OID,MatchingRuleDefinition> e = iterator.next(); 1515 final OID oid = e.getKey(); 1516 if (! matchingRules2.containsKey(oid)) 1517 { 1518 reportDifference( 1519 WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE.get( 1520 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 1521 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 1522 numDifferences, 1523 e.getValue().toString()); 1524 iterator.remove(); 1525 } 1526 } 1527 1528 iterator = matchingRules2.entrySet().iterator(); 1529 while (iterator.hasNext()) 1530 { 1531 final Map.Entry<OID,MatchingRuleDefinition> e = iterator.next(); 1532 final OID oid = e.getKey(); 1533 if (! matchingRules1.containsKey(oid)) 1534 { 1535 reportDifference( 1536 WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE.get( 1537 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 1538 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 1539 numDifferences, 1540 e.getValue().toString()); 1541 iterator.remove(); 1542 } 1543 } 1544 1545 1546 // Any remaining matching rules should exist in both servers. Compare them 1547 // and see if there are any differences between them. 1548 for (final OID oid : matchingRules1.keySet()) 1549 { 1550 final MatchingRuleDefinition d1 = matchingRules1.get(oid); 1551 final MatchingRuleDefinition d2 = matchingRules2.get(oid); 1552 1553 final String identifier = compareNames( 1554 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), 1555 oid.toString(), d1.getNames(), d2.getNames(), numDifferences); 1556 1557 compareStringValues( 1558 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), identifier, 1559 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SYNTAX_OID.get(), 1560 d1.getSyntaxOID(), d2.getSyntaxOID(), numDifferences); 1561 1562 compareBooleanValues( 1563 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), identifier, 1564 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), 1565 d1.isObsolete(), d2.isObsolete(), numDifferences); 1566 1567 if (! ignoreDescriptions) 1568 { 1569 compareStringValues( 1570 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE.get(), identifier, 1571 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 1572 d1.getDescription(), d2.getDescription(), numDifferences); 1573 } 1574 1575 compareExtensions( 1576 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), 1577 identifier, d1.getExtensions(), d2.getExtensions(), 1578 numDifferences); 1579 } 1580 } 1581 1582 1583 1584 /** 1585 * Retrieves a map of the matching rule definitions contained in the provided 1586 * schema, indexed by OID. 1587 * 1588 * @param schema The schema from which to retrieve the matching rules. It 1589 * must not be {@code null}. 1590 * 1591 * @return A map of the matching rule definitions contained in the provided 1592 * schema. 1593 */ 1594 @NotNull() 1595 private Map<OID,MatchingRuleDefinition> getMatchingRuleMap( 1596 @NotNull final Schema schema) 1597 { 1598 final Map<OID,MatchingRuleDefinition> matchingRules = new TreeMap<>(); 1599 for (final MatchingRuleDefinition d : schema.getMatchingRules()) 1600 { 1601 if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) 1602 { 1603 matchingRules.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); 1604 } 1605 } 1606 1607 return matchingRules; 1608 } 1609 1610 1611 1612 /** 1613 * Compares the attribute type definitions contained in the provided schemas. 1614 * 1615 * @param firstServerSchema The schema retrieved from the first server. It 1616 * must not be {@code null}. 1617 * @param secondServerSchema The schema retrieved from the second server. 1618 * It must not be {@code null}. 1619 * @param numDifferences A counter used to keep track of the number of 1620 * differences found between the schemas. It must 1621 * not be {@code null}. 1622 */ 1623 private void compareAttributeTypes( 1624 @NotNull final Schema firstServerSchema, 1625 @NotNull final Schema secondServerSchema, 1626 @NotNull final AtomicInteger numDifferences) 1627 { 1628 // Get the attribute type definitions from each of the schemas. 1629 final Map<OID,AttributeTypeDefinition> attributeTypes1 = 1630 getAttributeTypeMap(firstServerSchema); 1631 final Map<OID,AttributeTypeDefinition> attributeTypes2 = 1632 getAttributeTypeMap(secondServerSchema); 1633 1634 1635 // Identify attribute types that exist in one server but not another. If 1636 // any are found, then report them and remove them from the set. 1637 Iterator<Map.Entry<OID,AttributeTypeDefinition>> iterator = 1638 attributeTypes1.entrySet().iterator(); 1639 while (iterator.hasNext()) 1640 { 1641 final Map.Entry<OID,AttributeTypeDefinition> e = iterator.next(); 1642 final OID oid = e.getKey(); 1643 if (! attributeTypes2.containsKey(oid)) 1644 { 1645 reportDifference( 1646 WARN_COMPARE_SCHEMA_MISSING_ATTRIBUTE_TYPE.get( 1647 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 1648 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 1649 numDifferences, 1650 e.getValue().toString()); 1651 iterator.remove(); 1652 } 1653 } 1654 1655 iterator = attributeTypes2.entrySet().iterator(); 1656 while (iterator.hasNext()) 1657 { 1658 final Map.Entry<OID,AttributeTypeDefinition> e = iterator.next(); 1659 final OID oid = e.getKey(); 1660 if (! attributeTypes1.containsKey(oid)) 1661 { 1662 reportDifference( 1663 WARN_COMPARE_SCHEMA_MISSING_ATTRIBUTE_TYPE.get( 1664 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 1665 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 1666 numDifferences, 1667 e.getValue().toString()); 1668 iterator.remove(); 1669 } 1670 } 1671 1672 1673 // Any remaining attribute types should exist in both servers. Compare them 1674 // and see if there are any differences between them. 1675 for (final OID oid : attributeTypes1.keySet()) 1676 { 1677 final AttributeTypeDefinition d1 = attributeTypes1.get(oid); 1678 final AttributeTypeDefinition d2 = attributeTypes2.get(oid); 1679 1680 final String identifier = compareNames( 1681 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), 1682 oid.toString(), d1.getNames(), d2.getNames(), numDifferences); 1683 1684 compareStringValues( 1685 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1686 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUPERIOR_TYPE.get(), 1687 d1.getSuperiorType(), d2.getSuperiorType(), numDifferences); 1688 1689 compareStringValues( 1690 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1691 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SYNTAX_OID.get(), 1692 d1.getSyntaxOID(), d2.getSyntaxOID(), numDifferences); 1693 1694 compareStringValues( 1695 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1696 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_EQUALITY_MR.get(), 1697 d1.getEqualityMatchingRule(), d2.getEqualityMatchingRule(), 1698 numDifferences); 1699 1700 compareStringValues( 1701 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1702 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_ORDERING_MR.get(), 1703 d1.getOrderingMatchingRule(), d2.getOrderingMatchingRule(), 1704 numDifferences); 1705 1706 compareStringValues( 1707 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1708 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUBSTRING_MR.get(), 1709 d1.getSubstringMatchingRule(), d2.getSubstringMatchingRule(), 1710 numDifferences); 1711 1712 compareBooleanValues( 1713 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1714 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_SINGLE_VALUE.get(), 1715 d1.isSingleValued(), d2.isSingleValued(), numDifferences); 1716 1717 compareStringValues( 1718 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1719 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_USAGE.get(), 1720 d1.getUsage().getName(), d2.getUsage().getName(), 1721 numDifferences); 1722 1723 compareBooleanValues( 1724 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1725 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_NO_USER_MOD.get(), 1726 d1.isNoUserModification(), d2.isNoUserModification(), 1727 numDifferences); 1728 1729 compareBooleanValues( 1730 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1731 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_COLLECTIVE.get(), 1732 d1.isCollective(), d2.isCollective(), numDifferences); 1733 1734 compareBooleanValues( 1735 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1736 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), 1737 d1.isObsolete(), d2.isObsolete(), numDifferences); 1738 1739 if (! ignoreDescriptions) 1740 { 1741 compareStringValues( 1742 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_TYPE.get(), identifier, 1743 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 1744 d1.getDescription(), d2.getDescription(), numDifferences); 1745 } 1746 1747 compareExtensions( 1748 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_ATTRIBUTE_SYNTAX.get(), 1749 identifier, d1.getExtensions(), d2.getExtensions(), 1750 numDifferences); 1751 } 1752 } 1753 1754 1755 1756 /** 1757 * Retrieves a map of the attribute type definitions contained in the provided 1758 * schema, indexed by OID. 1759 * 1760 * @param schema The schema from which to retrieve the attribute types. It 1761 * must not be {@code null}. 1762 * 1763 * @return A map of the attribute type definitions contained in the provided 1764 * schema. 1765 */ 1766 @NotNull() 1767 private Map<OID,AttributeTypeDefinition> getAttributeTypeMap( 1768 @NotNull final Schema schema) 1769 { 1770 final Map<OID,AttributeTypeDefinition> attributeTypes = new TreeMap<>(); 1771 for (final AttributeTypeDefinition d : schema.getAttributeTypes()) 1772 { 1773 if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) 1774 { 1775 attributeTypes.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); 1776 } 1777 } 1778 1779 return attributeTypes; 1780 } 1781 1782 1783 1784 /** 1785 * Compares the object class definitions contained in the provided schemas. 1786 * 1787 * @param firstServerSchema The schema retrieved from the first server. It 1788 * must not be {@code null}. 1789 * @param secondServerSchema The schema retrieved from the second server. 1790 * It must not be {@code null}. 1791 * @param numDifferences A counter used to keep track of the number of 1792 * differences found between the schemas. It must 1793 * not be {@code null}. 1794 */ 1795 private void compareObjectClasses( 1796 @NotNull final Schema firstServerSchema, 1797 @NotNull final Schema secondServerSchema, 1798 @NotNull final AtomicInteger numDifferences) 1799 { 1800 // Get the object class definitions from each of the schemas. 1801 final Map<OID,ObjectClassDefinition> objectClasses1 = 1802 getObjectClassMap(firstServerSchema); 1803 final Map<OID,ObjectClassDefinition> objectClasses2 = 1804 getObjectClassMap(secondServerSchema); 1805 1806 1807 // Identify object classes that exist in one server but not another. If 1808 // any are found, then report them and remove them from the set. 1809 Iterator<Map.Entry<OID,ObjectClassDefinition>> iterator = 1810 objectClasses1.entrySet().iterator(); 1811 while (iterator.hasNext()) 1812 { 1813 final Map.Entry<OID,ObjectClassDefinition> e = iterator.next(); 1814 final OID oid = e.getKey(); 1815 if (! objectClasses2.containsKey(oid)) 1816 { 1817 reportDifference( 1818 WARN_COMPARE_SCHEMA_MISSING_OBJECT_CLASS.get( 1819 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 1820 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 1821 numDifferences, 1822 e.getValue().toString()); 1823 iterator.remove(); 1824 } 1825 } 1826 1827 iterator = objectClasses2.entrySet().iterator(); 1828 while (iterator.hasNext()) 1829 { 1830 final Map.Entry<OID,ObjectClassDefinition> e = iterator.next(); 1831 final OID oid = e.getKey(); 1832 if (! objectClasses1.containsKey(oid)) 1833 { 1834 reportDifference( 1835 WARN_COMPARE_SCHEMA_MISSING_OBJECT_CLASS.get( 1836 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 1837 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 1838 numDifferences, 1839 e.getValue().toString()); 1840 iterator.remove(); 1841 } 1842 } 1843 1844 1845 // Any remaining object classes should exist in both servers. Compare them 1846 // and see if there are any differences between them. 1847 for (final OID oid : objectClasses1.keySet()) 1848 { 1849 final ObjectClassDefinition d1 = objectClasses1.get(oid); 1850 final ObjectClassDefinition d2 = objectClasses2.get(oid); 1851 1852 final String identifier = compareNames( 1853 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), 1854 oid.toString(), d1.getNames(), d2.getNames(), numDifferences); 1855 1856 compareStringArrayValues( 1857 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, 1858 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUPERIOR_TYPE.get(), 1859 d1.getSuperiorClasses(), d2.getSuperiorClasses(), numDifferences); 1860 1861 compareStringArrayValues( 1862 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, 1863 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_REQUIRED_ATTRIBUTE.get(), 1864 d1.getRequiredAttributes(), d2.getRequiredAttributes(), 1865 numDifferences); 1866 1867 compareStringArrayValues( 1868 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, 1869 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OPTIONAL_ATTRIBUTE.get(), 1870 d1.getOptionalAttributes(), d2.getOptionalAttributes(), 1871 numDifferences); 1872 1873 final String oc1Type = (d1.getObjectClassType() == null) 1874 ? null 1875 : d1.getObjectClassType().getName(); 1876 final String oc2Type = (d2.getObjectClassType() == null) 1877 ? null 1878 : d2.getObjectClassType().getName(); 1879 compareStringValues( 1880 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, 1881 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OBJECT_CLASS_TYPE.get(), 1882 oc1Type, oc2Type, numDifferences); 1883 1884 compareBooleanValues( 1885 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, 1886 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), 1887 d1.isObsolete(), d2.isObsolete(), numDifferences); 1888 1889 if (! ignoreDescriptions) 1890 { 1891 compareStringValues( 1892 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), identifier, 1893 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 1894 d1.getDescription(), d2.getDescription(), numDifferences); 1895 } 1896 1897 compareExtensions( 1898 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_OBJECT_CLASS.get(), 1899 identifier, d1.getExtensions(), d2.getExtensions(), 1900 numDifferences); 1901 } 1902 } 1903 1904 1905 1906 /** 1907 * Retrieves a map of the object class definitions contained in the provided 1908 * schema, indexed by OID. 1909 * 1910 * @param schema The schema from which to retrieve the object classes. It 1911 * must not be {@code null}. 1912 * 1913 * @return A map of the object class definitions contained in the provided 1914 * schema. 1915 */ 1916 @NotNull() 1917 private Map<OID,ObjectClassDefinition> getObjectClassMap( 1918 @NotNull final Schema schema) 1919 { 1920 final Map<OID,ObjectClassDefinition> objectClasses = new TreeMap<>(); 1921 for (final ObjectClassDefinition d : schema.getObjectClasses()) 1922 { 1923 if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) 1924 { 1925 objectClasses.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); 1926 } 1927 } 1928 1929 return objectClasses; 1930 } 1931 1932 1933 1934 /** 1935 * Compares the DIT content rule definitions contained in the provided 1936 * schemas. 1937 * 1938 * @param firstServerSchema The schema retrieved from the first server. It 1939 * must not be {@code null}. 1940 * @param secondServerSchema The schema retrieved from the second server. 1941 * It must not be {@code null}. 1942 * @param numDifferences A counter used to keep track of the number of 1943 * differences found between the schemas. It must 1944 * not be {@code null}. 1945 */ 1946 private void compareDITContentRules( 1947 @NotNull final Schema firstServerSchema, 1948 @NotNull final Schema secondServerSchema, 1949 @NotNull final AtomicInteger numDifferences) 1950 { 1951 // Get the DIT content rule definitions from each of the schemas. 1952 final Map<OID,DITContentRuleDefinition> ditContentRules1 = 1953 getDITContentRuleMap(firstServerSchema); 1954 final Map<OID,DITContentRuleDefinition> ditContentRules2 = 1955 getDITContentRuleMap(secondServerSchema); 1956 1957 1958 // Identify DIT content rules that exist in one server but not another. If 1959 // any are found, then report them and remove them from the set. 1960 Iterator<Map.Entry<OID,DITContentRuleDefinition>> iterator = 1961 ditContentRules1.entrySet().iterator(); 1962 while (iterator.hasNext()) 1963 { 1964 final Map.Entry<OID,DITContentRuleDefinition> e = iterator.next(); 1965 final OID oid = e.getKey(); 1966 if (! ditContentRules2.containsKey(oid)) 1967 { 1968 reportDifference( 1969 WARN_COMPARE_SCHEMA_MISSING_DIT_CONTENT_RULE.get( 1970 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 1971 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 1972 numDifferences, 1973 e.getValue().toString()); 1974 iterator.remove(); 1975 } 1976 } 1977 1978 iterator = ditContentRules2.entrySet().iterator(); 1979 while (iterator.hasNext()) 1980 { 1981 final Map.Entry<OID,DITContentRuleDefinition> e = iterator.next(); 1982 final OID oid = e.getKey(); 1983 if (! ditContentRules1.containsKey(oid)) 1984 { 1985 reportDifference( 1986 WARN_COMPARE_SCHEMA_MISSING_DIT_CONTENT_RULE.get( 1987 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 1988 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 1989 numDifferences, 1990 e.getValue().toString()); 1991 iterator.remove(); 1992 } 1993 } 1994 1995 1996 // Any remaining DIT content rules should exist in both servers. Compare 1997 // them and see if there are any differences between them. 1998 for (final OID oid : ditContentRules1.keySet()) 1999 { 2000 final DITContentRuleDefinition d1 = ditContentRules1.get(oid); 2001 final DITContentRuleDefinition d2 = ditContentRules2.get(oid); 2002 2003 final String identifier = compareNames( 2004 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), 2005 oid.toString(), d1.getNames(), d2.getNames(), numDifferences); 2006 2007 compareStringArrayValues( 2008 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, 2009 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_REQUIRED_ATTRIBUTE.get(), 2010 d1.getRequiredAttributes(), d2.getRequiredAttributes(), 2011 numDifferences); 2012 2013 compareStringArrayValues( 2014 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, 2015 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OPTIONAL_ATTRIBUTE.get(), 2016 d1.getOptionalAttributes(), d2.getOptionalAttributes(), 2017 numDifferences); 2018 2019 compareStringArrayValues( 2020 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, 2021 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_PROHIBITED_ATTRIBUTE.get(), 2022 d1.getProhibitedAttributes(), d2.getProhibitedAttributes(), 2023 numDifferences); 2024 2025 compareStringArrayValues( 2026 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, 2027 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_AUXILIARY_CLASS.get(), 2028 d1.getAuxiliaryClasses(), d2.getAuxiliaryClasses(), numDifferences); 2029 2030 compareBooleanValues( 2031 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), identifier, 2032 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), 2033 d1.isObsolete(), d2.isObsolete(), numDifferences); 2034 2035 if (! ignoreDescriptions) 2036 { 2037 compareStringValues( 2038 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), 2039 identifier, 2040 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 2041 d1.getDescription(), d2.getDescription(), numDifferences); 2042 } 2043 2044 compareExtensions( 2045 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_CONTENT_RULE.get(), 2046 identifier, d1.getExtensions(), d2.getExtensions(), 2047 numDifferences); 2048 } 2049 } 2050 2051 2052 2053 /** 2054 * Retrieves a map of the DIT content rule definitions contained in the 2055 * provided schema, indexed by OID. 2056 * 2057 * @param schema The schema from which to retrieve the DIT content rules. 2058 * It must not be {@code null}. 2059 * 2060 * @return A map of the DIT content rule definitions contained in the 2061 * provided schema. 2062 */ 2063 @NotNull() 2064 private Map<OID,DITContentRuleDefinition> getDITContentRuleMap( 2065 @NotNull final Schema schema) 2066 { 2067 final Map<OID,DITContentRuleDefinition> ditContentRules = new TreeMap<>(); 2068 for (final DITContentRuleDefinition d : schema.getDITContentRules()) 2069 { 2070 if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) 2071 { 2072 ditContentRules.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); 2073 } 2074 } 2075 2076 return ditContentRules; 2077 } 2078 2079 2080 2081 /** 2082 * Compares the DIT structure rule definitions contained in the provided 2083 * schemas. 2084 * 2085 * @param firstServerSchema The schema retrieved from the first server. It 2086 * must not be {@code null}. 2087 * @param secondServerSchema The schema retrieved from the second server. 2088 * It must not be {@code null}. 2089 * @param numDifferences A counter used to keep track of the number of 2090 * differences found between the schemas. It must 2091 * not be {@code null}. 2092 */ 2093 private void compareDITStructureRules( 2094 @NotNull final Schema firstServerSchema, 2095 @NotNull final Schema secondServerSchema, 2096 @NotNull final AtomicInteger numDifferences) 2097 { 2098 // Get the DIT structure rule definitions from each of the schemas. 2099 final Map<Integer,DITStructureRuleDefinition> ditStructureRules1 = 2100 getDITStructureRuleMap(firstServerSchema); 2101 final Map<Integer,DITStructureRuleDefinition> ditStructureRules2 = 2102 getDITStructureRuleMap(secondServerSchema); 2103 2104 2105 // Identify DIT structure rules that exist in one server but not another. 2106 // If any are found, then report them and remove them from the set. 2107 Iterator<Map.Entry<Integer,DITStructureRuleDefinition>> iterator = 2108 ditStructureRules1.entrySet().iterator(); 2109 while (iterator.hasNext()) 2110 { 2111 final Map.Entry<Integer,DITStructureRuleDefinition> e = iterator.next(); 2112 final Integer id = e.getKey(); 2113 if (! ditStructureRules2.containsKey(id)) 2114 { 2115 reportDifference( 2116 WARN_COMPARE_SCHEMA_MISSING_DIT_STRUCTURE_RULE.get( 2117 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2118 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2119 numDifferences, 2120 e.getValue().toString()); 2121 iterator.remove(); 2122 } 2123 } 2124 2125 iterator = ditStructureRules2.entrySet().iterator(); 2126 while (iterator.hasNext()) 2127 { 2128 final Map.Entry<Integer,DITStructureRuleDefinition> e = iterator.next(); 2129 final Integer oid = e.getKey(); 2130 if (! ditStructureRules1.containsKey(oid)) 2131 { 2132 reportDifference( 2133 WARN_COMPARE_SCHEMA_MISSING_DIT_STRUCTURE_RULE.get( 2134 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2135 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2136 numDifferences, 2137 e.getValue().toString()); 2138 iterator.remove(); 2139 } 2140 } 2141 2142 2143 // Any remaining DIT structure rules should exist in both servers. Compare 2144 // them and see if there are any differences between them. 2145 for (final Integer id : ditStructureRules1.keySet()) 2146 { 2147 final DITStructureRuleDefinition d1 = ditStructureRules1.get(id); 2148 final DITStructureRuleDefinition d2 = ditStructureRules2.get(id); 2149 2150 final String identifier = compareNames( 2151 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), 2152 id.toString(), d1.getNames(), d2.getNames(), numDifferences); 2153 2154 compareStringValues( 2155 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), 2156 identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_NAME_FORM.get(), 2157 d1.getNameFormID(), d2.getNameFormID(), numDifferences); 2158 2159 final String[] superiorRuleIDs1 = 2160 intArrayToStringArray(d1.getSuperiorRuleIDs()); 2161 final String[] superiorRuleIDs2 = 2162 intArrayToStringArray(d2.getSuperiorRuleIDs()); 2163 compareStringArrayValues( 2164 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), 2165 identifier, 2166 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_SUPERIOR_RULE_ID.get(), 2167 superiorRuleIDs1, superiorRuleIDs2, numDifferences); 2168 2169 compareBooleanValues( 2170 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), 2171 identifier, INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), 2172 d1.isObsolete(), d2.isObsolete(), numDifferences); 2173 2174 if (! ignoreDescriptions) 2175 { 2176 compareStringValues( 2177 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), 2178 identifier, INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 2179 d1.getDescription(), d2.getDescription(), numDifferences); 2180 } 2181 2182 compareExtensions( 2183 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_DIT_STRUCTURE_RULE.get(), 2184 identifier, d1.getExtensions(), d2.getExtensions(), 2185 numDifferences); 2186 } 2187 } 2188 2189 2190 2191 /** 2192 * Retrieves a map of the DIT structure rule definitions contained in the 2193 * provided schema, indexed by rule ID. 2194 * 2195 * @param schema The schema from which to retrieve the DIT structure rules. 2196 * It must not be {@code null}. 2197 * 2198 * @return A map of the DIT structure rule definitions contained in the 2199 * provided schema. 2200 */ 2201 @NotNull() 2202 private Map<Integer,DITStructureRuleDefinition> getDITStructureRuleMap( 2203 @NotNull final Schema schema) 2204 { 2205 final Map<Integer,DITStructureRuleDefinition> ditStructureRules = 2206 new TreeMap<>(); 2207 for (final DITStructureRuleDefinition d : schema.getDITStructureRules()) 2208 { 2209 if (includeBasedOnNameAndExtensions(StaticUtils.NO_STRINGS, 2210 d.getExtensions())) 2211 { 2212 ditStructureRules.put(d.getRuleID(), d); 2213 } 2214 } 2215 2216 return ditStructureRules; 2217 } 2218 2219 2220 2221 /** 2222 * Converts the provided integer array to a string array in which each element 2223 * is the string representation of the corresponding element in the provided 2224 * integer array. 2225 * 2226 * @param intArray The integer array to convert to a string array. It must 2227 * not be {@code null}, but may be empty. 2228 * 2229 * @return A string array in which each element is the string representation 2230 * of the corresponding element in the provided integer array. 2231 */ 2232 @NotNull() 2233 private static String[] intArrayToStringArray(@NotNull final int[] intArray) 2234 { 2235 final String[] stringArray = new String[intArray.length]; 2236 for (int i=0; i < intArray.length; i++) 2237 { 2238 stringArray[i] = String.valueOf(intArray[i]); 2239 } 2240 2241 return stringArray; 2242 } 2243 2244 2245 2246 /** 2247 * Compares the name form definitions contained in the provided schemas. 2248 * 2249 * @param firstServerSchema The schema retrieved from the first server. It 2250 * must not be {@code null}. 2251 * @param secondServerSchema The schema retrieved from the second server. 2252 * It must not be {@code null}. 2253 * @param numDifferences A counter used to keep track of the number of 2254 * differences found between the schemas. It must 2255 * not be {@code null}. 2256 */ 2257 private void compareNameForms( 2258 @NotNull final Schema firstServerSchema, 2259 @NotNull final Schema secondServerSchema, 2260 @NotNull final AtomicInteger numDifferences) 2261 { 2262 // Get the name form definitions from each of the schemas. 2263 final Map<OID,NameFormDefinition> nameForms1 = 2264 getNameFormMap(firstServerSchema); 2265 final Map<OID,NameFormDefinition> nameForms2 = 2266 getNameFormMap(secondServerSchema); 2267 2268 2269 // Identify name forms that exist in one server but not another. If 2270 // any are found, then report them and remove them from the set. 2271 Iterator<Map.Entry<OID,NameFormDefinition>> iterator = 2272 nameForms1.entrySet().iterator(); 2273 while (iterator.hasNext()) 2274 { 2275 final Map.Entry<OID,NameFormDefinition> e = iterator.next(); 2276 final OID oid = e.getKey(); 2277 if (! nameForms2.containsKey(oid)) 2278 { 2279 reportDifference( 2280 WARN_COMPARE_SCHEMA_MISSING_NAME_FORM.get( 2281 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2282 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2283 numDifferences, 2284 e.getValue().toString()); 2285 iterator.remove(); 2286 } 2287 } 2288 2289 iterator = nameForms2.entrySet().iterator(); 2290 while (iterator.hasNext()) 2291 { 2292 final Map.Entry<OID,NameFormDefinition> e = iterator.next(); 2293 final OID oid = e.getKey(); 2294 if (! nameForms1.containsKey(oid)) 2295 { 2296 reportDifference( 2297 WARN_COMPARE_SCHEMA_MISSING_NAME_FORM.get( 2298 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2299 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2300 numDifferences, 2301 e.getValue().toString()); 2302 iterator.remove(); 2303 } 2304 } 2305 2306 2307 // Any remaining name forms should exist in both servers. Compare them and 2308 // see if there are any differences between them. 2309 for (final OID oid : nameForms1.keySet()) 2310 { 2311 final NameFormDefinition d1 = nameForms1.get(oid); 2312 final NameFormDefinition d2 = nameForms2.get(oid); 2313 2314 final String identifier = compareNames( 2315 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), 2316 oid.toString(), d1.getNames(), d2.getNames(), numDifferences); 2317 2318 compareStringValues( 2319 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, 2320 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_STRUCTURAL_CLASS.get(), 2321 d1.getStructuralClass(), d2.getStructuralClass(), numDifferences); 2322 2323 compareStringArrayValues( 2324 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, 2325 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_REQUIRED_ATTRIBUTE.get(), 2326 d1.getRequiredAttributes(), d2.getRequiredAttributes(), 2327 numDifferences); 2328 2329 compareStringArrayValues( 2330 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, 2331 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_OPTIONAL_ATTRIBUTE.get(), 2332 d1.getOptionalAttributes(), d2.getOptionalAttributes(), 2333 numDifferences); 2334 2335 compareBooleanValues( 2336 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, 2337 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), 2338 d1.isObsolete(), d2.isObsolete(), numDifferences); 2339 2340 if (! ignoreDescriptions) 2341 { 2342 compareStringValues( 2343 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), identifier, 2344 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 2345 d1.getDescription(), d2.getDescription(), numDifferences); 2346 } 2347 2348 compareExtensions( 2349 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_NAME_FORM.get(), 2350 identifier, d1.getExtensions(), d2.getExtensions(), 2351 numDifferences); 2352 } 2353 } 2354 2355 2356 2357 /** 2358 * Retrieves a map of the name form definitions contained in the provided 2359 * schema, indexed by OID. 2360 * 2361 * @param schema The schema from which to retrieve the name forms. It must 2362 * not be {@code null}. 2363 * 2364 * @return A map of the name form definitions contained in the provided 2365 * schema. 2366 */ 2367 @NotNull() 2368 private Map<OID,NameFormDefinition> getNameFormMap( 2369 @NotNull final Schema schema) 2370 { 2371 final Map<OID,NameFormDefinition> nameForms = new TreeMap<>(); 2372 for (final NameFormDefinition d : schema.getNameForms()) 2373 { 2374 if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) 2375 { 2376 nameForms.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); 2377 } 2378 } 2379 2380 return nameForms; 2381 } 2382 2383 2384 2385 /** 2386 * Compares the matching rule use definitions contained in the provided 2387 * schemas. 2388 * 2389 * @param firstServerSchema The schema retrieved from the first server. It 2390 * must not be {@code null}. 2391 * @param secondServerSchema The schema retrieved from the second server. 2392 * It must not be {@code null}. 2393 * @param numDifferences A counter used to keep track of the number of 2394 * differences found between the schemas. It must 2395 * not be {@code null}. 2396 */ 2397 private void compareMatchingRuleUses( 2398 @NotNull final Schema firstServerSchema, 2399 @NotNull final Schema secondServerSchema, 2400 @NotNull final AtomicInteger numDifferences) 2401 { 2402 // Get the matching rule use definitions from each of the schemas. 2403 final Map<OID,MatchingRuleUseDefinition> matchingRuleUses1 = 2404 getMatchingRuleUseMap(firstServerSchema); 2405 final Map<OID,MatchingRuleUseDefinition> matchingRuleUses2 = 2406 getMatchingRuleUseMap(secondServerSchema); 2407 2408 2409 // Identify matching rule uses that exist in one server but not another. If 2410 // any are found, then report them and remove them from the set. 2411 Iterator<Map.Entry<OID,MatchingRuleUseDefinition>> iterator = 2412 matchingRuleUses1.entrySet().iterator(); 2413 while (iterator.hasNext()) 2414 { 2415 final Map.Entry<OID,MatchingRuleUseDefinition> e = iterator.next(); 2416 final OID oid = e.getKey(); 2417 if (! matchingRuleUses2.containsKey(oid)) 2418 { 2419 reportDifference( 2420 WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE_USE.get( 2421 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2422 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2423 numDifferences, 2424 e.getValue().toString()); 2425 iterator.remove(); 2426 } 2427 } 2428 2429 iterator = matchingRuleUses2.entrySet().iterator(); 2430 while (iterator.hasNext()) 2431 { 2432 final Map.Entry<OID,MatchingRuleUseDefinition> e = iterator.next(); 2433 final OID oid = e.getKey(); 2434 if (! matchingRuleUses1.containsKey(oid)) 2435 { 2436 reportDifference( 2437 WARN_COMPARE_SCHEMA_MISSING_MATCHING_RULE_USE.get( 2438 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2439 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2440 numDifferences, 2441 e.getValue().toString()); 2442 iterator.remove(); 2443 } 2444 } 2445 2446 2447 // Any remaining matching rule uses should exist in both servers. Compare 2448 // them and see if there are any differences between them. 2449 for (final OID oid : matchingRuleUses1.keySet()) 2450 { 2451 final MatchingRuleUseDefinition d1 = matchingRuleUses1.get(oid); 2452 final MatchingRuleUseDefinition d2 = matchingRuleUses2.get(oid); 2453 2454 final String identifier = compareNames( 2455 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), 2456 oid.toString(), d1.getNames(), d2.getNames(), numDifferences); 2457 2458 compareStringArrayValues( 2459 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), identifier, 2460 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_APPLICABLE_ATTRIBUTE.get(), 2461 d1.getApplicableAttributeTypes(), d2.getApplicableAttributeTypes(), 2462 numDifferences); 2463 2464 compareBooleanValues( 2465 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), identifier, 2466 INFO_COMPARE_SCHEMA_BOOLEAN_FIELD_NAME_OBSOLETE.get(), 2467 d1.isObsolete(), d2.isObsolete(), numDifferences); 2468 2469 if (! ignoreDescriptions) 2470 { 2471 compareStringValues( 2472 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), identifier, 2473 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_DESCRIPTION.get(), 2474 d1.getDescription(), d2.getDescription(), numDifferences); 2475 } 2476 2477 compareExtensions( 2478 INFO_COMPARE_SCHEMA_ELEMENT_TYPE_MATCHING_RULE_USE.get(), 2479 identifier, d1.getExtensions(), d2.getExtensions(), 2480 numDifferences); 2481 } 2482 } 2483 2484 2485 2486 /** 2487 * Retrieves a map of the matching rule use definitions contained in the 2488 * provided schema, indexed by OID. 2489 * 2490 * @param schema The schema from which to retrieve the matching rule uses. 2491 * It must not be {@code null}. 2492 * 2493 * @return A map of the matching rule use definitions contained in the 2494 * provided schema. 2495 */ 2496 @NotNull() 2497 private Map<OID,MatchingRuleUseDefinition> getMatchingRuleUseMap( 2498 @NotNull final Schema schema) 2499 { 2500 final Map<OID,MatchingRuleUseDefinition> matchingRuleUses = new TreeMap<>(); 2501 for (final MatchingRuleUseDefinition d : schema.getMatchingRuleUses()) 2502 { 2503 if (includeBasedOnNameAndExtensions(d.getNames(), d.getExtensions())) 2504 { 2505 matchingRuleUses.put(new OID(StaticUtils.toLowerCase(d.getOID())), d); 2506 } 2507 } 2508 2509 return matchingRuleUses; 2510 } 2511 2512 2513 2514 /** 2515 * Indicates whether to include a schema element with the given name and set 2516 * of extensions. 2517 * 2518 * @param names The set of names for the schema element. It must not 2519 * be {@code null}, but may be empty. 2520 * @param extensions The set of extensions for the schema element. It must 2521 * not be {@code null}, but may be empty. 2522 * 2523 * @return {@code true} if an element with the given names and set of 2524 * extensions should be included, or {@code false} if not. 2525 */ 2526 private boolean includeBasedOnNameAndExtensions( 2527 @NotNull final String[] names, 2528 @NotNull final Map<String,String[]> extensions) 2529 { 2530 if (includeOrExcludeBasedOnName && (names.length > 0)) 2531 { 2532 boolean includeFound = false; 2533 for (final String name : names) 2534 { 2535 final String lowerName = StaticUtils.toLowerCase(name); 2536 for (final String excludePrefix : excludeNamePrefixes) 2537 { 2538 if (lowerName.startsWith(excludePrefix)) 2539 { 2540 return false; 2541 } 2542 } 2543 2544 if (! includeNamePrefixes.isEmpty()) 2545 { 2546 for (final String includePrefix : includeNamePrefixes) 2547 { 2548 if (lowerName.startsWith(includePrefix)) 2549 { 2550 includeFound = true; 2551 break; 2552 } 2553 } 2554 } 2555 } 2556 2557 if ((! includeNamePrefixes.isEmpty()) && (! includeFound)) 2558 { 2559 return false; 2560 } 2561 } 2562 2563 2564 if (includeOrExcludeBasedOnExtensions && (! extensions.isEmpty())) 2565 { 2566 boolean includeFound = false; 2567 for (final Map.Entry<String,String[]> e : extensions.entrySet()) 2568 { 2569 final String lowerName = StaticUtils.toLowerCase(e.getKey()); 2570 final String[] values = e.getValue(); 2571 final String[] lowerValues = new String[values.length]; 2572 for (int i=0; i < values.length; i++) 2573 { 2574 lowerValues[i] = StaticUtils.toLowerCase(values[i]); 2575 } 2576 2577 final List<String> excludeValues = 2578 excludeExtensionValues.get(lowerName); 2579 if (excludeValues != null) 2580 { 2581 for (final String lowerValue : lowerValues) 2582 { 2583 if (excludeValues.contains(lowerValue)) 2584 { 2585 return false; 2586 } 2587 } 2588 } 2589 2590 final List<String> includeValues = 2591 includeExtensionValues.get(lowerName); 2592 if (includeValues != null) 2593 { 2594 for (final String lowerValue : lowerValues) 2595 { 2596 if (includeValues.contains(lowerValue)) 2597 { 2598 includeFound = true; 2599 break; 2600 } 2601 } 2602 } 2603 } 2604 2605 if ((! includeExtensionValues.isEmpty()) && (! includeFound)) 2606 { 2607 return false; 2608 } 2609 } 2610 2611 2612 return true; 2613 } 2614 2615 2616 2617 /** 2618 * Reports a difference between schema elements. 2619 * 2620 * @param message The message to display with information about 2621 * the difference. It must not be {@code null}. 2622 * @param numDifferences A counter used to keep track of the number of 2623 * differences found between the schemas. It must 2624 * not be {@code null}. 2625 * @param additionalStrings A set of additional strings that should also be 2626 * displayed, in addition to the provided message. 2627 * Each additional string will be presented on its 2628 * own line without any wrapping. It must not be 2629 * {@code null}, but may be empty. 2630 */ 2631 private void reportDifference(@NotNull final String message, 2632 @NotNull final AtomicInteger numDifferences, 2633 @NotNull final String... additionalStrings) 2634 { 2635 wrapErr(0, WRAP_COLUMN, message); 2636 for (final String additionalString : additionalStrings) 2637 { 2638 err(additionalString); 2639 } 2640 err(); 2641 numDifferences.incrementAndGet(); 2642 } 2643 2644 2645 2646 /** 2647 * Identifies differences between string values for two schema elements. 2648 * 2649 * @param elementTypeName A name for the type of schema element being 2650 * compared. It must not be {@code null}. 2651 * @param elementIdentifier An identifier (e.g., the name or OID) for the 2652 * schema element for which to make the 2653 * determination. It must not be {@code null}. 2654 * @param stringDescriptor A descriptor for the string values being 2655 * compared. 2656 * @param string1 The string value from the first schema element. 2657 * It may be {@code null} if the element does not 2658 * contain a value for the associated field. 2659 * @param string2 The string value from the first second element. 2660 * It may be {@code null} if the element does not 2661 * contain a value for the associated field. 2662 * @param numDifferences A counter used to keep track of the number of 2663 * differences found between the schemas. It must 2664 * not be {@code null}. 2665 */ 2666 private void compareStringValues(@NotNull final String elementTypeName, 2667 @NotNull final String elementIdentifier, 2668 @NotNull final String stringDescriptor, 2669 @Nullable final String string1, 2670 @Nullable final String string2, 2671 @NotNull final AtomicInteger numDifferences) 2672 { 2673 if (string1 == null) 2674 { 2675 if (string2 != null) 2676 { 2677 reportDifference( 2678 WARN_COMPARE_SCHEMA_STRING_MISSING_FROM_SERVER.get( 2679 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2680 elementTypeName, elementIdentifier, stringDescriptor, 2681 string2, INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2682 numDifferences); 2683 } 2684 } 2685 else if (string2 == null) 2686 { 2687 reportDifference( 2688 WARN_COMPARE_SCHEMA_STRING_MISSING_FROM_SERVER.get( 2689 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2690 elementTypeName, elementIdentifier, stringDescriptor, 2691 string1, INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2692 numDifferences); 2693 } 2694 else if (! string1.equalsIgnoreCase(string2)) 2695 { 2696 reportDifference( 2697 WARN_COMPARE_SCHEMA_STRING_DIFFERENT_BETWEEN_SERVERS.get( 2698 elementTypeName, elementIdentifier, stringDescriptor, string1, 2699 string2), 2700 numDifferences); 2701 } 2702 } 2703 2704 2705 2706 /** 2707 * Identifies differences between the sets of string arrays for two schema 2708 * elements. 2709 * 2710 * @param elementTypeName A name for the type of schema element being 2711 * compared. It must not be {@code null}. 2712 * @param elementIdentifier An identifier (e.g., the name or OID) for the 2713 * schema element for which to make the 2714 * determination. It must not be {@code null}. 2715 * @param stringDescriptor A descriptor for the string values being 2716 * compared. 2717 * @param array1 The array of values for the target field from 2718 * the element in the first schema. It must not be 2719 * {@code null}, but may be empty. 2720 * @param array2 The array of values for the target field from 2721 * the element in the second schema. It must not 2722 * be {@code null}, but may be empty. 2723 * @param numDifferences A counter used to keep track of the number of 2724 * differences found between the schemas. It must 2725 * not be {@code null}. 2726 */ 2727 private void compareStringArrayValues( 2728 @NotNull final String elementTypeName, 2729 @NotNull final String elementIdentifier, 2730 @NotNull final String stringDescriptor, 2731 @NotNull final String[] array1, 2732 @NotNull final String[] array2, 2733 @NotNull final AtomicInteger numDifferences) 2734 { 2735 if (array1.length == 0) 2736 { 2737 switch (array2.length) 2738 { 2739 case 0: 2740 // The element doesn't have any names in either of the servers, so 2741 // there is no difference. 2742 break; 2743 case 1: 2744 // The element has names in the second server, but not in the first. 2745 reportDifference( 2746 WARN_COMPARE_SCHEMA_STRING_ARRAY_SINGLE_IN_ONLY_ONE_SERVER.get( 2747 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2748 elementTypeName, elementIdentifier, stringDescriptor, 2749 array2[0], INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2750 numDifferences); 2751 break; 2752 default: 2753 reportDifference( 2754 WARN_COMPARE_SCHEMA_STRING_ARRAY_MULTIPLE_IN_ONLY_ONE_SERVER.get( 2755 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2756 elementTypeName, elementIdentifier, stringDescriptor, 2757 Arrays.toString(array2), 2758 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2759 numDifferences); 2760 break; 2761 } 2762 } 2763 else if (array2.length == 0) 2764 { 2765 // The element has names in the first server, but not in the second. 2766 if (array1.length == 1) 2767 { 2768 reportDifference( 2769 WARN_COMPARE_SCHEMA_STRING_ARRAY_SINGLE_IN_ONLY_ONE_SERVER.get( 2770 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2771 elementTypeName, elementIdentifier, stringDescriptor, 2772 array1[0], INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2773 numDifferences); 2774 } 2775 else 2776 { 2777 reportDifference( 2778 WARN_COMPARE_SCHEMA_STRING_ARRAY_MULTIPLE_IN_ONLY_ONE_SERVER.get( 2779 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2780 elementTypeName, elementIdentifier, stringDescriptor, 2781 Arrays.toString(array1), 2782 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2783 numDifferences); 2784 } 2785 } 2786 else 2787 { 2788 // The element has names in both servers. See if there are any 2789 // differences between them. 2790 final Map<String,String> n1 = getNameMap(array1); 2791 final Map<String,String> n2 = getNameMap(array2); 2792 for (final Map.Entry<String,String> e : n1.entrySet()) 2793 { 2794 final String lowerName = e.getKey(); 2795 if (n2.remove(lowerName) == null) 2796 { 2797 reportDifference( 2798 WARN_COMPARE_SCHEMA_STRING_ARRAY_VALUE_MISSING_FROM_SERVER.get( 2799 elementTypeName, elementIdentifier, stringDescriptor, 2800 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2801 e.getValue(), 2802 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2803 numDifferences); 2804 } 2805 } 2806 2807 for (final String nameOnlyInServer2 : n2.values()) 2808 { 2809 reportDifference( 2810 WARN_COMPARE_SCHEMA_STRING_ARRAY_VALUE_MISSING_FROM_SERVER.get( 2811 elementTypeName, elementIdentifier, stringDescriptor, 2812 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2813 nameOnlyInServer2, 2814 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2815 numDifferences); 2816 } 2817 } 2818 } 2819 2820 2821 2822 /** 2823 * Identifies differences between the sets of names for two schema elements. 2824 * 2825 * @param elementTypeName A name for the type of schema element being 2826 * compared. It must not be {@code null}. 2827 * @param elementIdentifier An identifier (e.g., the name or OID) for the 2828 * schema element for which to make the 2829 * determination. It must not be {@code null}. 2830 * @param names1 The set of names for the element from the first 2831 * schema. It must not be {@code null}, but may be 2832 * empty. 2833 * @param names2 The set of names for the element from the second 2834 * schema. It must not be {@code null}, but may be 2835 * empty. 2836 * @param numDifferences A counter used to keep track of the number of 2837 * differences found between the schemas. It must 2838 * not be {@code null}. 2839 * 2840 * @return The identifier string that should be used to identify the 2841 * associated schema element. If both sets of names are non-empty 2842 * and have the same first element, then that name will be used as 2843 * the identifier. Otherwise, the provided identifier will be used. 2844 */ 2845 @NotNull() 2846 private String compareNames(@NotNull final String elementTypeName, 2847 @NotNull final String elementIdentifier, 2848 @NotNull final String[] names1, 2849 @NotNull final String[] names2, 2850 @NotNull final AtomicInteger numDifferences) 2851 { 2852 compareStringArrayValues(elementTypeName, elementIdentifier, 2853 INFO_COMPARE_SCHEMA_STRING_DESCRIPTOR_NAME.get(), names1, names2, 2854 numDifferences); 2855 2856 2857 // Identify the best identifier to use for the schema element going forward. 2858 if ((names1.length > 0) && (names2.length > 0) && 2859 (names1[0].equalsIgnoreCase(names2[0]))) 2860 { 2861 return names1[0]; 2862 } 2863 else 2864 { 2865 return elementIdentifier; 2866 } 2867 } 2868 2869 2870 2871 /** 2872 * Identifies difference between boolean values for two schema elements. 2873 * 2874 * @param elementTypeName A name for the type of schema element being 2875 * compared. It must not be {@code null}. 2876 * @param elementIdentifier An identifier (e.g., the name or OID) for the 2877 * schema element for which to make the 2878 * determination. It must not be {@code null}. 2879 * @param booleanFieldName The name of the Boolean field being compared. 2880 * @param value1 The Boolean value from the first schema element. 2881 * @param value2 The Boolean value from the second schema 2882 * element. 2883 * @param numDifferences A counter used to keep track of the number of 2884 * differences found between the schemas. It must 2885 * not be {@code null}. 2886 */ 2887 private void compareBooleanValues(@NotNull final String elementTypeName, 2888 @NotNull final String elementIdentifier, 2889 @NotNull final String booleanFieldName, 2890 final boolean value1, 2891 final boolean value2, 2892 @NotNull final AtomicInteger numDifferences) 2893 { 2894 if (value1 != value2) 2895 { 2896 if (value1) 2897 { 2898 reportDifference( 2899 WARN_COMPARE_SCHEMA_BOOLEAN_DIFFERENCE.get( 2900 elementTypeName, elementIdentifier, booleanFieldName, 2901 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2902 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2903 numDifferences); 2904 } 2905 else 2906 { 2907 reportDifference( 2908 WARN_COMPARE_SCHEMA_BOOLEAN_DIFFERENCE.get( 2909 elementTypeName, elementIdentifier, booleanFieldName, 2910 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2911 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2912 numDifferences); 2913 } 2914 } 2915 } 2916 2917 2918 2919 /** 2920 * Identifies differences between the sets of extensions for two schema 2921 * elements. 2922 * 2923 * @param elementTypeName A name for the type of schema element being 2924 * compared. It must not be {@code null}. 2925 * @param elementIdentifier An identifier (e.g., the name or OID) for the 2926 * schema element for which to make the 2927 * determination. It must not be {@code null}. 2928 * @param extensions1 The set of extensions for the element from the 2929 * first schema. It must not be {@code null}, but 2930 * may be empty. 2931 * @param extensions2 The set of extensions for the element from the 2932 * second schema. It must not be {@code null}, but 2933 * may be empty. 2934 * @param numDifferences A counter used to keep track of the number of 2935 * differences found between the schemas. It must 2936 * not be {@code null}. 2937 */ 2938 private void compareExtensions( 2939 @NotNull final String elementTypeName, 2940 @NotNull final String elementIdentifier, 2941 @NotNull final Map<String,String[]> extensions1, 2942 @NotNull final Map<String,String[]> extensions2, 2943 @NotNull final AtomicInteger numDifferences) 2944 { 2945 if (ignoreExtensions) 2946 { 2947 return; 2948 } 2949 2950 2951 // Convert the extensions into a map of sets so that we can alter the 2952 // contents of both the map and its sets. 2953 final Map<String,Set<String>> e1 = 2954 convertToUpdatableExtensionsMap(extensions1); 2955 final Map<String,Set<String>> e2 = 2956 convertToUpdatableExtensionsMap(extensions2); 2957 2958 2959 // Iterate through the extensions and identify differences between them. 2960 for (final Map.Entry<String,Set<String>> e : e1.entrySet()) 2961 { 2962 final String extensionName = e.getKey(); 2963 final Set<String> extension1Values = e.getValue(); 2964 final Set<String> extension2Values = e2.remove(extensionName); 2965 if (extension2Values == null) 2966 { 2967 reportDifference( 2968 WARN_COMPARE_SCHEMA_MISSING_EXTENSION.get( 2969 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get(), 2970 elementTypeName, elementIdentifier, extensionName, 2971 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get()), 2972 numDifferences); 2973 } 2974 else if (! extension1Values.equals(extension2Values)) 2975 { 2976 reportDifference( 2977 WARN_COMPARE_SCHEMA_EXTENSION_DIFFERENCE.get( 2978 elementTypeName, elementIdentifier, extensionName), 2979 numDifferences); 2980 } 2981 } 2982 2983 for (final String extensionName : e2.keySet()) 2984 { 2985 reportDifference( 2986 WARN_COMPARE_SCHEMA_MISSING_EXTENSION.get( 2987 INFO_COMPARE_SCHEMA_SECOND_SERVER_LABEL.get(), 2988 elementTypeName, elementIdentifier, extensionName, 2989 INFO_COMPARE_SCHEMA_FIRST_SERVER_LABEL.get()), 2990 numDifferences); 2991 } 2992 } 2993 2994 2995 2996 /** 2997 * Converts the provided extensions map into an updatable map that associates 2998 * each extension name key with a modifiable set of values rather than an 2999 * array. In addition, all strings will be converted to lowercase for more 3000 * efficient case-insensitive comparison. 3001 * 3002 * @param extensionsMap The map to be converted. It must not be 3003 * {@code null}, but may be empty. 3004 * 3005 * @return A modifiable map that contains the information in the provided map 3006 * in a form that is better suited for comparing extensions between 3007 * two definitions. 3008 */ 3009 @NotNull() 3010 private static Map<String,Set<String>> convertToUpdatableExtensionsMap( 3011 @NotNull final Map<String,String[]> extensionsMap) 3012 { 3013 final Map<String,Set<String>> convertedExtensionsMap = new TreeMap<>(); 3014 for (final Map.Entry<String,String[]> e : extensionsMap.entrySet()) 3015 { 3016 final String lowerExtensionName = StaticUtils.toLowerCase(e.getKey()); 3017 final Set<String> lowerExtensionValues = new TreeSet<>(); 3018 for (final String extensionValue : e.getValue()) 3019 { 3020 lowerExtensionValues.add(StaticUtils.toLowerCase(extensionValue)); 3021 } 3022 3023 convertedExtensionsMap.put(lowerExtensionName, lowerExtensionValues); 3024 } 3025 3026 return convertedExtensionsMap; 3027 } 3028 3029 3030 3031 /** 3032 * Retrieves a modifiable map containing the provided names. The key for each 3033 * entry in the map will be the name in all lowercase, and the value will be 3034 * the name in the case in which it is provided. 3035 * 3036 * @param names The names to include in the resulting map. It must not be 3037 * {@code null}. 3038 * 3039 * @return A modifiable map containing the provided names. 3040 */ 3041 @NotNull() 3042 private static Map<String,String> getNameMap(@NotNull final String[] names) 3043 { 3044 final Map<String,String> m = new TreeMap<>(); 3045 for (final String name : names) 3046 { 3047 m.put(StaticUtils.toLowerCase(name), name); 3048 } 3049 3050 return m; 3051 } 3052 3053 3054 3055 /** 3056 * Logs the provided message to standard error and sets it as the tool 3057 * completion message. 3058 * 3059 * @param message The completion message. It must not be {@code null}. 3060 */ 3061 private void logCompletionError(@NotNull final String message) 3062 { 3063 completionMessageRef.compareAndSet(null, message); 3064 wrapErr(0, WRAP_COLUMN, message); 3065 } 3066 3067 3068 3069 /** 3070 * {@inheritDoc} 3071 */ 3072 @Override() 3073 @NotNull() 3074 public LinkedHashMap<String[],String> getExampleUsages() 3075 { 3076 final LinkedHashMap<String[],String> examples = new LinkedHashMap<>(); 3077 3078 examples.put( 3079 new String[] 3080 { 3081 "--firstHostname", "ds1.example.com", 3082 "--firstPort", "636", 3083 "--firstUseSSL", 3084 "--firstBindDN", "cn=Directory Manager", 3085 "--firstBindPasswordFile", "/path/to/password.txt", 3086 "--secondHostname", "ds2.example.com", 3087 "--secondPort", "636", 3088 "--secondUseSSL", 3089 "--secondBindDN", "cn=Directory Manager", 3090 "--secondBindPasswordFile", "/path/to/password.txt" 3091 }, 3092 INFO_COMPARE_LDAP_SCHEMAS_EXAMPLE.get()); 3093 3094 return examples; 3095 } 3096}