001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 import org.opends.messages.Message; 029 030 031 032 import java.lang.reflect.Method; 033 import java.util.ArrayList; 034 import java.util.Iterator; 035 import java.util.List; 036 import java.util.concurrent.ConcurrentHashMap; 037 038 import org.opends.server.admin.ClassPropertyDefinition; 039 import org.opends.server.admin.server.ConfigurationAddListener; 040 import org.opends.server.admin.server.ConfigurationChangeListener; 041 import org.opends.server.admin.server.ConfigurationDeleteListener; 042 import org.opends.server.admin.std.meta.ApproximateMatchingRuleCfgDefn; 043 import org.opends.server.admin.std.meta.EqualityMatchingRuleCfgDefn; 044 import org.opends.server.admin.std.meta.OrderingMatchingRuleCfgDefn; 045 import org.opends.server.admin.std.meta.SubstringMatchingRuleCfgDefn; 046 import org.opends.server.admin.std.server.ApproximateMatchingRuleCfg; 047 import org.opends.server.admin.std.server.EqualityMatchingRuleCfg; 048 import org.opends.server.admin.std.server.MatchingRuleCfg; 049 import org.opends.server.admin.std.server.OrderingMatchingRuleCfg; 050 import org.opends.server.admin.std.server.SubstringMatchingRuleCfg; 051 import org.opends.server.admin.std.server.RootCfg; 052 import org.opends.server.admin.server.ServerManagementContext; 053 import org.opends.server.api.ApproximateMatchingRule; 054 import org.opends.server.api.EqualityMatchingRule; 055 import org.opends.server.api.MatchingRule; 056 import org.opends.server.api.OrderingMatchingRule; 057 import org.opends.server.api.SubstringMatchingRule; 058 import org.opends.server.config.ConfigException; 059 import org.opends.server.types.AttributeType; 060 import org.opends.server.types.ConfigChangeResult; 061 import org.opends.server.types.DirectoryException; 062 import org.opends.server.types.DN; 063 064 065 import org.opends.server.types.InitializationException; 066 import org.opends.server.types.MatchingRuleUse; 067 import org.opends.server.types.ResultCode; 068 069 import static org.opends.messages.ConfigMessages.*; 070 071 import static org.opends.server.util.StaticUtils.*; 072 import org.opends.server.loggers.ErrorLogger; 073 074 075 /** 076 * This class defines a utility that will be used to manage the set of matching 077 * rules defined in the Directory Server. It wil initialize the rules when the 078 * server starts, and then will manage any additions, removals, or modifications 079 * to any matching rules while the server is running. 080 */ 081 public class MatchingRuleConfigManager 082 implements ConfigurationChangeListener<MatchingRuleCfg>, 083 ConfigurationAddListener<MatchingRuleCfg>, 084 ConfigurationDeleteListener<MatchingRuleCfg> 085 086 { 087 // A mapping between the DNs of the config entries and the associated matching 088 // rules. 089 private ConcurrentHashMap<DN,MatchingRule> matchingRules; 090 091 092 093 /** 094 * Creates a new instance of this matching rule config manager. 095 */ 096 public MatchingRuleConfigManager() 097 { 098 matchingRules = new ConcurrentHashMap<DN,MatchingRule>(); 099 } 100 101 102 103 /** 104 * Initializes all matching rules currently defined in the Directory Server 105 * configuration. This should only be called at Directory Server startup. 106 * 107 * @throws ConfigException If a configuration problem causes the matching 108 * rule initialization process to fail. 109 * 110 * @throws InitializationException If a problem occurs while initializing 111 * the matching rules that is not related to 112 * the server configuration. 113 */ 114 public void initializeMatchingRules() 115 throws ConfigException, InitializationException 116 { 117 // Get the root configuration object. 118 ServerManagementContext managementContext = 119 ServerManagementContext.getInstance(); 120 RootCfg rootConfiguration = 121 managementContext.getRootConfiguration(); 122 123 124 // Register as an add and delete listener with the root configuration so we 125 // can be notified if any matching rule entries are added or removed. 126 rootConfiguration.addMatchingRuleAddListener(this); 127 rootConfiguration.addMatchingRuleDeleteListener(this); 128 129 130 //Initialize the existing matching rules. 131 for (String name : rootConfiguration.listMatchingRules()) 132 { 133 MatchingRuleCfg mrConfiguration = rootConfiguration.getMatchingRule(name); 134 mrConfiguration.addChangeListener(this); 135 136 if (mrConfiguration.isEnabled()) 137 { 138 String className = mrConfiguration.getJavaClass(); 139 try 140 { 141 MatchingRule matchingRule = 142 loadMatchingRule(className, mrConfiguration, true); 143 144 try 145 { 146 DirectoryServer.registerMatchingRule(matchingRule, false); 147 matchingRules.put(mrConfiguration.dn(), matchingRule); 148 } 149 catch (DirectoryException de) 150 { 151 Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get( 152 String.valueOf(mrConfiguration.dn()), de.getMessageObject()); 153 ErrorLogger.logError(message); 154 continue; 155 } 156 } 157 catch (InitializationException ie) 158 { 159 ErrorLogger.logError(ie.getMessageObject()); 160 continue; 161 } 162 } 163 } 164 } 165 166 167 168 /** 169 * {@inheritDoc} 170 */ 171 public boolean isConfigurationAddAcceptable(MatchingRuleCfg configuration, 172 List<Message> unacceptableReasons) 173 { 174 if (configuration.isEnabled()) 175 { 176 // Get the name of the class and make sure we can instantiate it as a 177 // matching rule. 178 String className = configuration.getJavaClass(); 179 try 180 { 181 loadMatchingRule(className, configuration, false); 182 } 183 catch (InitializationException ie) 184 { 185 unacceptableReasons.add(ie.getMessageObject()); 186 return false; 187 } 188 } 189 190 // If we've gotten here, then it's fine. 191 return true; 192 } 193 194 195 196 /** 197 * {@inheritDoc} 198 */ 199 public ConfigChangeResult applyConfigurationAdd(MatchingRuleCfg configuration) 200 { 201 ResultCode resultCode = ResultCode.SUCCESS; 202 boolean adminActionRequired = false; 203 ArrayList<Message> messages = new ArrayList<Message>(); 204 205 configuration.addChangeListener(this); 206 207 if (! configuration.isEnabled()) 208 { 209 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 210 } 211 212 MatchingRule matchingRule = null; 213 214 // Get the name of the class and make sure we can instantiate it as a 215 // matching rule. 216 String className = configuration.getJavaClass(); 217 try 218 { 219 matchingRule = loadMatchingRule(className, configuration, true); 220 221 try 222 { 223 DirectoryServer.registerMatchingRule(matchingRule, false); 224 matchingRules.put(configuration.dn(), matchingRule); 225 } 226 catch (DirectoryException de) 227 { 228 Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get( 229 String.valueOf(configuration.dn()), 230 de.getMessageObject()); 231 messages.add(message); 232 233 if (resultCode == ResultCode.SUCCESS) 234 { 235 resultCode = DirectoryServer.getServerErrorResultCode(); 236 } 237 } 238 } 239 catch (InitializationException ie) 240 { 241 if (resultCode == ResultCode.SUCCESS) 242 { 243 resultCode = DirectoryServer.getServerErrorResultCode(); 244 } 245 246 messages.add(ie.getMessageObject()); 247 } 248 249 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 250 } 251 252 253 254 /** 255 * {@inheritDoc} 256 */ 257 public boolean isConfigurationDeleteAcceptable(MatchingRuleCfg configuration, 258 List<Message> unacceptableReasons) 259 { 260 // If the matching rule is enabled, then check to see if there are any 261 // defined attribute types or matching rule uses that use the matching rule. 262 // If so, then don't allow it to be deleted. 263 boolean configAcceptable = true; 264 MatchingRule matchingRule = matchingRules.get(configuration.dn()); 265 if (matchingRule != null) 266 { 267 String oid = matchingRule.getOID(); 268 for (AttributeType at : DirectoryServer.getAttributeTypes().values()) 269 { 270 ApproximateMatchingRule amr = at.getApproximateMatchingRule(); 271 if ((amr != null) && oid.equals(amr.getOID())) 272 { 273 Message message = 274 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get( 275 matchingRule.getName(), 276 at.getNameOrOID()); 277 unacceptableReasons.add(message); 278 279 configAcceptable = false; 280 continue; 281 } 282 283 EqualityMatchingRule emr = at.getEqualityMatchingRule(); 284 if ((emr != null) && oid.equals(emr.getOID())) 285 { 286 Message message = 287 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get( 288 matchingRule.getName(), 289 at.getNameOrOID()); 290 unacceptableReasons.add(message); 291 292 configAcceptable = false; 293 continue; 294 } 295 296 OrderingMatchingRule omr = at.getOrderingMatchingRule(); 297 if ((omr != null) && oid.equals(omr.getOID())) 298 { 299 Message message = 300 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get( 301 matchingRule.getName(), 302 at.getNameOrOID()); 303 unacceptableReasons.add(message); 304 305 configAcceptable = false; 306 continue; 307 } 308 309 SubstringMatchingRule smr = at.getSubstringMatchingRule(); 310 if ((smr != null) && oid.equals(smr.getOID())) 311 { 312 Message message = 313 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get( 314 matchingRule.getName(), 315 at.getNameOrOID()); 316 unacceptableReasons.add(message); 317 318 configAcceptable = false; 319 continue; 320 } 321 } 322 323 for (MatchingRuleUse mru : DirectoryServer.getMatchingRuleUses().values()) 324 { 325 if (oid.equals(mru.getMatchingRule().getOID())) 326 { 327 Message message = 328 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_MRU.get( 329 matchingRule.getName(), 330 mru.getName()); 331 unacceptableReasons.add(message); 332 333 configAcceptable = false; 334 continue; 335 } 336 } 337 } 338 339 return configAcceptable; 340 } 341 342 343 344 /** 345 * {@inheritDoc} 346 */ 347 public ConfigChangeResult applyConfigurationDelete( 348 MatchingRuleCfg configuration) 349 { 350 ResultCode resultCode = ResultCode.SUCCESS; 351 boolean adminActionRequired = false; 352 ArrayList<Message> messages = new ArrayList<Message>(); 353 354 MatchingRule matchingRule = matchingRules.remove(configuration.dn()); 355 if (matchingRule != null) 356 { 357 DirectoryServer.deregisterMatchingRule(matchingRule); 358 matchingRule.finalizeMatchingRule(); 359 } 360 361 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 362 } 363 364 365 366 /** 367 * {@inheritDoc} 368 */ 369 public boolean isConfigurationChangeAcceptable(MatchingRuleCfg configuration, 370 List<Message> unacceptableReasons) 371 { 372 boolean configAcceptable = true; 373 if (configuration.isEnabled()) 374 { 375 // Get the name of the class and make sure we can instantiate it as a 376 // matching rule. 377 String className = configuration.getJavaClass(); 378 try 379 { 380 loadMatchingRule(className, configuration, false); 381 } 382 catch (InitializationException ie) 383 { 384 unacceptableReasons.add(ie.getMessageObject()); 385 configAcceptable = false; 386 } 387 } 388 else 389 { 390 // If the matching rule is currently enabled and the change would make it 391 // disabled, then only allow it if the matching rule isn't already in use. 392 MatchingRule matchingRule = matchingRules.get(configuration.dn()); 393 if (matchingRule != null) 394 { 395 String oid = matchingRule.getOID(); 396 for (AttributeType at : DirectoryServer.getAttributeTypes().values()) 397 { 398 ApproximateMatchingRule amr = at.getApproximateMatchingRule(); 399 if ((amr != null) && oid.equals(amr.getOID())) 400 { 401 Message message = 402 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT.get( 403 matchingRule.getName(), 404 at.getNameOrOID()); 405 unacceptableReasons.add(message); 406 407 configAcceptable = false; 408 continue; 409 } 410 411 EqualityMatchingRule emr = at.getEqualityMatchingRule(); 412 if ((emr != null) && oid.equals(emr.getOID())) 413 { 414 Message message = 415 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT.get( 416 matchingRule.getName(), 417 at.getNameOrOID()); 418 unacceptableReasons.add(message); 419 420 configAcceptable = false; 421 continue; 422 } 423 424 OrderingMatchingRule omr = at.getOrderingMatchingRule(); 425 if ((omr != null) && oid.equals(omr.getOID())) 426 { 427 Message message = 428 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT.get( 429 matchingRule.getName(), 430 at.getNameOrOID()); 431 unacceptableReasons.add(message); 432 433 configAcceptable = false; 434 continue; 435 } 436 437 SubstringMatchingRule smr = at.getSubstringMatchingRule(); 438 if ((smr != null) && oid.equals(smr.getOID())) 439 { 440 Message message = WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT 441 .get(matchingRule.getName(), at.getNameOrOID()); 442 unacceptableReasons.add(message); 443 444 configAcceptable = false; 445 continue; 446 } 447 } 448 449 for (MatchingRuleUse mru : 450 DirectoryServer.getMatchingRuleUses().values()) 451 { 452 if (oid.equals(mru.getMatchingRule().getOID())) 453 { 454 Message message = 455 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_MRU.get( 456 matchingRule.getName(), mru.getName()); 457 unacceptableReasons.add(message); 458 459 configAcceptable = false; 460 continue; 461 } 462 } 463 } 464 } 465 466 return configAcceptable; 467 } 468 469 470 471 /** 472 * {@inheritDoc} 473 */ 474 public ConfigChangeResult applyConfigurationChange( 475 MatchingRuleCfg configuration) 476 { 477 ResultCode resultCode = ResultCode.SUCCESS; 478 boolean adminActionRequired = false; 479 ArrayList<Message> messages = new ArrayList<Message>(); 480 481 482 // Get the existing matching rule if it's already enabled. 483 MatchingRule existingRule = matchingRules.get(configuration.dn()); 484 485 486 // If the new configuration has the matching rule disabled, then disable it 487 // if it is enabled, or do nothing if it's already disabled. 488 if (! configuration.isEnabled()) 489 { 490 if (existingRule != null) 491 { 492 DirectoryServer.deregisterMatchingRule(existingRule); 493 494 MatchingRule rule = matchingRules.remove(configuration.dn()); 495 if (rule != null) 496 { 497 rule.finalizeMatchingRule(); 498 } 499 } 500 501 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 502 } 503 504 505 // Get the class for the matching rule. If the matching rule is already 506 // enabled, then we shouldn't do anything with it although if the class has 507 // changed then we'll at least need to indicate that administrative action 508 // is required. If the matching rule is disabled, then instantiate the 509 // class and initialize and register it as a matching rule. 510 String className = configuration.getJavaClass(); 511 if (existingRule != null) 512 { 513 if (! className.equals(existingRule.getClass().getName())) 514 { 515 adminActionRequired = true; 516 } 517 518 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 519 } 520 521 MatchingRule matchingRule = null; 522 try 523 { 524 matchingRule = loadMatchingRule(className, configuration, true); 525 526 try 527 { 528 DirectoryServer.registerMatchingRule(matchingRule, false); 529 matchingRules.put(configuration.dn(), matchingRule); 530 } 531 catch (DirectoryException de) 532 { 533 Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get( 534 String.valueOf(configuration.dn()), de.getMessageObject()); 535 messages.add(message); 536 537 if (resultCode == ResultCode.SUCCESS) 538 { 539 resultCode = DirectoryServer.getServerErrorResultCode(); 540 } 541 } 542 } 543 catch (InitializationException ie) 544 { 545 if (resultCode == ResultCode.SUCCESS) 546 { 547 resultCode = DirectoryServer.getServerErrorResultCode(); 548 } 549 550 messages.add(ie.getMessageObject()); 551 } 552 553 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 554 } 555 556 557 558 /** 559 * Loads the specified class, instantiates it as an attribute syntax, and 560 * optionally initializes that instance. 561 * 562 * @param className The fully-qualified name of the attribute syntax 563 * class to load, instantiate, and initialize. 564 * @param configuration The configuration to use to initialize the attribute 565 * syntax. It must not be {@code null}. 566 * @param initialize Indicates whether the matching rule instance should 567 * be initialized. 568 * 569 * @return The possibly initialized attribute syntax. 570 * 571 * @throws InitializationException If a problem occurred while attempting to 572 * initialize the attribute syntax. 573 */ 574 private MatchingRule loadMatchingRule(String className, 575 MatchingRuleCfg configuration, 576 boolean initialize) 577 throws InitializationException 578 { 579 try 580 { 581 MatchingRule matchingRule = null; 582 if (configuration instanceof ApproximateMatchingRuleCfg) 583 { 584 ApproximateMatchingRuleCfgDefn definition = 585 ApproximateMatchingRuleCfgDefn.getInstance(); 586 ClassPropertyDefinition propertyDefinition = 587 definition.getJavaClassPropertyDefinition(); 588 Class<? extends ApproximateMatchingRule> approximateMatchingRuleClass = 589 propertyDefinition.loadClass(className, 590 ApproximateMatchingRule.class); 591 matchingRule = approximateMatchingRuleClass.newInstance(); 592 } 593 else if (configuration instanceof EqualityMatchingRuleCfg) 594 { 595 EqualityMatchingRuleCfgDefn definition = 596 EqualityMatchingRuleCfgDefn.getInstance(); 597 ClassPropertyDefinition propertyDefinition = 598 definition.getJavaClassPropertyDefinition(); 599 Class<? extends EqualityMatchingRule> equalityMatchingRuleClass = 600 propertyDefinition.loadClass(className, 601 EqualityMatchingRule.class); 602 matchingRule = equalityMatchingRuleClass.newInstance(); 603 } 604 else if (configuration instanceof OrderingMatchingRuleCfg) 605 { 606 OrderingMatchingRuleCfgDefn definition = 607 OrderingMatchingRuleCfgDefn.getInstance(); 608 ClassPropertyDefinition propertyDefinition = 609 definition.getJavaClassPropertyDefinition(); 610 Class<? extends OrderingMatchingRule> orderingMatchingRuleClass = 611 propertyDefinition.loadClass(className, 612 OrderingMatchingRule.class); 613 matchingRule = orderingMatchingRuleClass.newInstance(); 614 } 615 else if (configuration instanceof SubstringMatchingRuleCfg) 616 { 617 SubstringMatchingRuleCfgDefn definition = 618 SubstringMatchingRuleCfgDefn.getInstance(); 619 ClassPropertyDefinition propertyDefinition = 620 definition.getJavaClassPropertyDefinition(); 621 Class<? extends SubstringMatchingRule> substringMatchingRuleClass = 622 propertyDefinition.loadClass(className, 623 SubstringMatchingRule.class); 624 matchingRule = substringMatchingRuleClass.newInstance(); 625 } 626 else 627 { 628 throw new AssertionError("Unsupported matching rule type: " + 629 className + " with config type " + 630 configuration.getClass().getName()); 631 } 632 633 if (initialize) 634 { 635 Method method = matchingRule.getClass().getMethod( 636 "initializeMatchingRule", configuration.configurationClass()); 637 method.invoke(matchingRule, configuration); 638 } 639 else 640 { 641 Method method = 642 matchingRule.getClass().getMethod("isConfigurationAcceptable", 643 MatchingRuleCfg.class, 644 List.class); 645 646 List<Message> unacceptableReasons = new ArrayList<Message>(); 647 Boolean acceptable = (Boolean) method.invoke(matchingRule, 648 configuration, 649 unacceptableReasons); 650 if (! acceptable) 651 { 652 StringBuilder buffer = new StringBuilder(); 653 if (! unacceptableReasons.isEmpty()) 654 { 655 Iterator<Message> iterator = unacceptableReasons.iterator(); 656 buffer.append(iterator.next()); 657 while (iterator.hasNext()) 658 { 659 buffer.append(". "); 660 buffer.append(iterator.next()); 661 } 662 } 663 664 Message message = ERR_CONFIG_SCHEMA_MR_CONFIG_NOT_ACCEPTABLE.get( 665 String.valueOf(configuration.dn()), buffer.toString()); 666 throw new InitializationException(message); 667 } 668 } 669 670 return matchingRule; 671 } 672 catch (Exception e) 673 { 674 Message message = ERR_CONFIG_SCHEMA_MR_CANNOT_INITIALIZE. 675 get(className, String.valueOf(configuration.dn()), 676 stackTraceToSingleLineString(e)); 677 throw new InitializationException(message, e); 678 } 679 } 680 } 681