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 2007-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.admin; 028 029 030 031 import static org.opends.messages.AdminMessages.*; 032 import static org.opends.server.loggers.debug.DebugLogger.*; 033 import static org.opends.server.util.Validator.*; 034 035 import java.util.Collection; 036 import java.util.Collections; 037 import java.util.EnumSet; 038 import java.util.HashMap; 039 import java.util.Iterator; 040 import java.util.LinkedList; 041 import java.util.List; 042 import java.util.Locale; 043 import java.util.Map; 044 import java.util.MissingResourceException; 045 import java.util.SortedSet; 046 047 import org.opends.messages.Message; 048 import org.opends.server.admin.client.AuthorizationException; 049 import org.opends.server.admin.client.ClientConstraintHandler; 050 import org.opends.server.admin.client.CommunicationException; 051 import org.opends.server.admin.client.ManagedObject; 052 import org.opends.server.admin.client.ManagedObjectDecodingException; 053 import org.opends.server.admin.client.ManagementContext; 054 import org.opends.server.admin.condition.Condition; 055 import org.opends.server.admin.condition.Conditions; 056 import org.opends.server.admin.server.ConfigurationDeleteListener; 057 import org.opends.server.admin.server.ServerConstraintHandler; 058 import org.opends.server.admin.server.ServerManagedObject; 059 import org.opends.server.admin.server.ServerManagedObjectChangeListener; 060 import org.opends.server.admin.server.ServerManagementContext; 061 import org.opends.server.admin.std.meta.RootCfgDefn; 062 import org.opends.server.config.ConfigException; 063 import org.opends.server.loggers.ErrorLogger; 064 import org.opends.server.loggers.debug.DebugTracer; 065 import org.opends.server.types.ConfigChangeResult; 066 import org.opends.server.types.DN; 067 import org.opends.server.types.DebugLogLevel; 068 import org.opends.server.types.ResultCode; 069 import org.opends.server.util.StaticUtils; 070 071 072 073 /** 074 * Aggregation property definition. 075 * <p> 076 * An aggregation property names one or more managed objects which are 077 * required by the managed object associated with this property. An 078 * aggregation property definition takes care to perform referential 079 * integrity checks: referenced managed objects cannot be deleted. Nor 080 * can an aggregation reference non-existent managed objects. 081 * Referential integrity checks are <b>not</b> performed during value 082 * validation. Instead they are performed when changes to the managed 083 * object are committed. 084 * <p> 085 * An aggregation property definition can optionally identify two 086 * properties: 087 * <ul> 088 * <li>an <code>enabled</code> property in the aggregated managed 089 * object - the property must be a {@link BooleanPropertyDefinition} 090 * and indicate whether the aggregated managed object is enabled or 091 * not. If specified, the administration framework will prevent the 092 * aggregated managed object from being disabled while it is 093 * referenced 094 * <li>an <code>enabled</code> property in this property's managed 095 * object - the property must be a {@link BooleanPropertyDefinition} 096 * and indicate whether this property's managed object is enabled or 097 * not. If specified, and as long as there is an equivalent 098 * <code>enabled</code> property defined for the aggregated managed 099 * object, the <code>enabled</code> property in the aggregated 100 * managed object will only be checked when this property is true. 101 * </ul> 102 * In other words, these properties can be used to make sure that 103 * referenced managed objects are not disabled while they are 104 * referenced. 105 * 106 * @param <C> 107 * The type of client managed object configuration that this 108 * aggregation property definition refers to. 109 * @param <S> 110 * The type of server managed object configuration that this 111 * aggregation property definition refers to. 112 */ 113 public final class AggregationPropertyDefinition 114 <C extends ConfigurationClient, S extends Configuration> 115 extends PropertyDefinition<String> { 116 117 /** 118 * An interface for incrementally constructing aggregation property 119 * definitions. 120 * 121 * @param <C> 122 * The type of client managed object configuration that 123 * this aggregation property definition refers to. 124 * @param <S> 125 * The type of server managed object configuration that 126 * this aggregation property definition refers to. 127 */ 128 public static class Builder 129 <C extends ConfigurationClient, S extends Configuration> 130 extends AbstractBuilder<String, AggregationPropertyDefinition<C, S>> { 131 132 // The string representation of the managed object path specifying 133 // the parent of the aggregated managed objects. 134 private String parentPathString = null; 135 136 // The name of a relation in the parent managed object which 137 // contains the aggregated managed objects. 138 private String rdName = null; 139 140 // The condition which is used to determine if a referenced 141 // managed object is enabled. 142 private Condition targetIsEnabledCondition = Conditions.TRUE; 143 144 // The condition which is used to determine whether or not 145 // referenced managed objects need to be enabled. 146 private Condition targetNeedsEnablingCondition = Conditions.TRUE; 147 148 149 150 // Private constructor 151 private Builder(AbstractManagedObjectDefinition<?, ?> d, 152 String propertyName) { 153 super(d, propertyName); 154 } 155 156 157 158 /** 159 * Sets the name of the managed object which is the parent of the 160 * aggregated managed objects. 161 * <p> 162 * This must be defined before the property definition can be 163 * built. 164 * 165 * @param pathString 166 * The string representation of the managed object path 167 * specifying the parent of the aggregated managed 168 * objects. 169 */ 170 public final void setParentPath(String pathString) { 171 this.parentPathString = pathString; 172 } 173 174 175 176 /** 177 * Sets the relation in the parent managed object which contains 178 * the aggregated managed objects. 179 * <p> 180 * This must be defined before the property definition can be 181 * built. 182 * 183 * @param rdName 184 * The name of a relation in the parent managed object 185 * which contains the aggregated managed objects. 186 */ 187 public final void setRelationDefinition(String rdName) { 188 this.rdName = rdName; 189 } 190 191 192 193 /** 194 * Sets the condition which is used to determine if a referenced 195 * managed object is enabled. By default referenced managed 196 * objects are assumed to always be enabled. 197 * 198 * @param condition 199 * The condition which is used to determine if a 200 * referenced managed object is enabled. 201 */ 202 public final void setTargetIsEnabledCondition(Condition condition) { 203 this.targetIsEnabledCondition = condition; 204 } 205 206 207 208 /** 209 * Sets the condition which is used to determine whether or not 210 * referenced managed objects need to be enabled. By default 211 * referenced managed objects must always be enabled. 212 * 213 * @param condition 214 * The condition which is used to determine whether or 215 * not referenced managed objects need to be enabled. 216 */ 217 public final void setTargetNeedsEnablingCondition(Condition condition) { 218 this.targetNeedsEnablingCondition = condition; 219 } 220 221 222 223 /** 224 * {@inheritDoc} 225 */ 226 @Override 227 protected AggregationPropertyDefinition<C, S> buildInstance( 228 AbstractManagedObjectDefinition<?, ?> d, String propertyName, 229 EnumSet<PropertyOption> options, AdministratorAction adminAction, 230 DefaultBehaviorProvider<String> defaultBehavior) { 231 // Make sure that the parent path has been defined. 232 if (parentPathString == null) { 233 throw new IllegalStateException("Parent path undefined"); 234 } 235 236 // Make sure that the relation definition has been defined. 237 if (rdName == null) { 238 throw new IllegalStateException("Relation definition undefined"); 239 } 240 241 return new AggregationPropertyDefinition<C, S>(d, propertyName, options, 242 adminAction, defaultBehavior, parentPathString, rdName, 243 targetNeedsEnablingCondition, targetIsEnabledCondition); 244 } 245 246 } 247 248 249 250 /** 251 * A change listener which prevents the named component from being 252 * disabled. 253 */ 254 private class ReferentialIntegrityChangeListener implements 255 ServerManagedObjectChangeListener<S> { 256 257 // The error message which should be returned if an attempt is 258 // made to disable the referenced component. 259 private final Message message; 260 261 // The path of the referenced component. 262 private final ManagedObjectPath<C, S> path; 263 264 265 266 // Creates a new referential integrity delete listener. 267 private ReferentialIntegrityChangeListener(ManagedObjectPath<C, S> path, 268 Message message) { 269 this.path = path; 270 this.message = message; 271 } 272 273 274 275 /** 276 * {@inheritDoc} 277 */ 278 public ConfigChangeResult applyConfigurationChange( 279 ServerManagedObject<? extends S> mo) { 280 try { 281 if (targetIsEnabledCondition.evaluate(mo)) { 282 return new ConfigChangeResult(ResultCode.SUCCESS, false); 283 } 284 } catch (ConfigException e) { 285 // This should not happen - ignore it and throw an exception 286 // anyway below. 287 } 288 289 // This should not happen - the previous call-back should have 290 // trapped this. 291 throw new IllegalStateException("Attempting to disable a referenced " 292 + relationDefinition.getChildDefinition().getUserFriendlyName()); 293 } 294 295 296 297 /** 298 * {@inheritDoc} 299 */ 300 public boolean isConfigurationChangeAcceptable( 301 ServerManagedObject<? extends S> mo, 302 List<Message> unacceptableReasons) { 303 // Always prevent the referenced component from being 304 // disabled. 305 try { 306 if (!targetIsEnabledCondition.evaluate(mo)) { 307 unacceptableReasons.add(message); 308 return false; 309 } else { 310 return true; 311 } 312 } catch (ConfigException e) { 313 // The condition could not be evaluated. 314 if (debugEnabled()) { 315 TRACER.debugCaught(DebugLogLevel.ERROR, e); 316 } 317 318 Message message = ERR_REFINT_UNABLE_TO_EVALUATE_TARGET_CONDITION.get(mo 319 .getManagedObjectDefinition().getUserFriendlyName(), String 320 .valueOf(mo.getDN()), StaticUtils.getExceptionMessage(e)); 321 ErrorLogger.logError(message); 322 unacceptableReasons.add(message); 323 return false; 324 } 325 } 326 327 328 329 // Gets the path associated with this listener. 330 private ManagedObjectPath<C, S> getManagedObjectPath() { 331 return path; 332 } 333 334 } 335 336 337 338 /** 339 * A delete listener which prevents the named component from being 340 * deleted. 341 */ 342 private class ReferentialIntegrityDeleteListener implements 343 ConfigurationDeleteListener<S> { 344 345 // The DN of the referenced configuration entry. 346 private final DN dn; 347 348 // The error message which should be returned if an attempt is 349 // made to delete the referenced component. 350 private final Message message; 351 352 353 354 // Creates a new referential integrity delete listener. 355 private ReferentialIntegrityDeleteListener(DN dn, Message message) { 356 this.dn = dn; 357 this.message = message; 358 } 359 360 361 362 /** 363 * {@inheritDoc} 364 */ 365 public ConfigChangeResult applyConfigurationDelete(S configuration) { 366 // This should not happen - the 367 // isConfigurationDeleteAcceptable() call-back should have 368 // trapped this. 369 if (configuration.dn().equals(dn)) { 370 // This should not happen - the 371 // isConfigurationDeleteAcceptable() call-back should have 372 // trapped this. 373 throw new IllegalStateException("Attempting to delete a referenced " 374 + relationDefinition.getChildDefinition().getUserFriendlyName()); 375 } else { 376 return new ConfigChangeResult(ResultCode.SUCCESS, false); 377 } 378 } 379 380 381 382 /** 383 * {@inheritDoc} 384 */ 385 public boolean isConfigurationDeleteAcceptable(S configuration, 386 List<Message> unacceptableReasons) { 387 if (configuration.dn().equals(dn)) { 388 // Always prevent deletion of the referenced component. 389 unacceptableReasons.add(message); 390 return false; 391 } 392 393 return true; 394 } 395 396 } 397 398 399 400 /** 401 * The server-side constraint handler implementation. 402 */ 403 private class ServerHandler extends ServerConstraintHandler { 404 405 /** 406 * {@inheritDoc} 407 */ 408 @Override 409 public boolean isUsable(ServerManagedObject<?> managedObject, 410 Collection<Message> unacceptableReasons) throws ConfigException { 411 SortedSet<String> names = managedObject 412 .getPropertyValues(AggregationPropertyDefinition.this); 413 ServerManagementContext context = ServerManagementContext.getInstance(); 414 Message thisUFN = managedObject.getManagedObjectDefinition() 415 .getUserFriendlyName(); 416 String thisDN = managedObject.getDN().toString(); 417 Message thatUFN = getRelationDefinition().getUserFriendlyName(); 418 419 boolean isUsable = true; 420 boolean needsEnabling = targetNeedsEnablingCondition 421 .evaluate(managedObject); 422 for (String name : names) { 423 ManagedObjectPath<C, S> path = getChildPath(name); 424 String thatDN = path.toDN().toString(); 425 426 if (!context.managedObjectExists(path)) { 427 Message msg = ERR_SERVER_REFINT_DANGLING_REFERENCE.get(name, 428 getName(), thisUFN, thisDN, thatUFN, thatDN); 429 unacceptableReasons.add(msg); 430 isUsable = false; 431 } else if (needsEnabling) { 432 // Check that the referenced component is enabled if 433 // required. 434 ServerManagedObject<? extends S> ref = context.getManagedObject(path); 435 if (!targetIsEnabledCondition.evaluate(ref)) { 436 Message msg = ERR_SERVER_REFINT_TARGET_DISABLED.get(name, 437 getName(), thisUFN, thisDN, thatUFN, thatDN); 438 unacceptableReasons.add(msg); 439 isUsable = false; 440 } 441 } 442 } 443 444 return isUsable; 445 } 446 447 448 449 /** 450 * {@inheritDoc} 451 */ 452 @Override 453 public void performPostAdd(ServerManagedObject<?> managedObject) 454 throws ConfigException { 455 // First make sure existing listeners associated with this 456 // managed object are removed. This is required in order to 457 // prevent multiple change listener registrations from 458 // occurring, for example if this call-back is invoked multiple 459 // times after the same add event. 460 performPostDelete(managedObject); 461 462 // Add change and delete listeners against all referenced 463 // components. 464 Message thisUFN = managedObject.getManagedObjectDefinition() 465 .getUserFriendlyName(); 466 String thisDN = managedObject.getDN().toString(); 467 Message thatUFN = getRelationDefinition().getUserFriendlyName(); 468 469 // Referenced managed objects will only need a change listener 470 // if they have can be disabled. 471 boolean needsChangeListeners = targetNeedsEnablingCondition 472 .evaluate(managedObject); 473 474 // Delete listeners need to be registered against the parent 475 // entry of the referenced components. 476 ServerManagementContext context = ServerManagementContext.getInstance(); 477 ManagedObjectPath<?, ?> parentPath = getParentPath(); 478 ServerManagedObject<?> parent = context.getManagedObject(parentPath); 479 480 // Create entries in the listener tables. 481 List<ReferentialIntegrityDeleteListener> dlist = 482 new LinkedList<ReferentialIntegrityDeleteListener>(); 483 deleteListeners.put(managedObject.getDN(), dlist); 484 485 List<ReferentialIntegrityChangeListener> clist = 486 new LinkedList<ReferentialIntegrityChangeListener>(); 487 changeListeners.put(managedObject.getDN(), clist); 488 489 for (String name : managedObject 490 .getPropertyValues(AggregationPropertyDefinition.this)) { 491 ManagedObjectPath<C, S> path = getChildPath(name); 492 DN dn = path.toDN(); 493 String thatDN = dn.toString(); 494 495 // Register the delete listener. 496 Message msg = ERR_SERVER_REFINT_CANNOT_DELETE.get(thatUFN, thatDN, 497 getName(), thisUFN, thisDN); 498 ReferentialIntegrityDeleteListener dl = 499 new ReferentialIntegrityDeleteListener(dn, msg); 500 parent.registerDeleteListener(getRelationDefinition(), dl); 501 dlist.add(dl); 502 503 // Register the change listener if required. 504 if (needsChangeListeners) { 505 ServerManagedObject<? extends S> ref = context.getManagedObject(path); 506 msg = ERR_SERVER_REFINT_CANNOT_DISABLE.get(thatUFN, thatDN, 507 getName(), thisUFN, thisDN); 508 ReferentialIntegrityChangeListener cl = 509 new ReferentialIntegrityChangeListener(path, msg); 510 ref.registerChangeListener(cl); 511 clist.add(cl); 512 } 513 } 514 } 515 516 517 518 /** 519 * {@inheritDoc} 520 */ 521 @Override 522 public void performPostDelete(ServerManagedObject<?> managedObject) 523 throws ConfigException { 524 // Remove any registered delete and change listeners. 525 ServerManagementContext context = ServerManagementContext.getInstance(); 526 DN dn = managedObject.getDN(); 527 528 // Delete listeners need to be deregistered against the parent 529 // entry of the referenced components. 530 ManagedObjectPath<?, ?> parentPath = getParentPath(); 531 ServerManagedObject<?> parent = context.getManagedObject(parentPath); 532 if (deleteListeners.containsKey(dn)) { 533 for (ReferentialIntegrityDeleteListener dl : deleteListeners.get(dn)) { 534 parent.deregisterDeleteListener(getRelationDefinition(), dl); 535 } 536 deleteListeners.remove(dn); 537 } 538 539 // Change listeners need to be deregistered from their 540 // associated referenced component. 541 if (changeListeners.containsKey(dn)) { 542 for (ReferentialIntegrityChangeListener cl : changeListeners.get(dn)) { 543 ManagedObjectPath<C, S> path = cl.getManagedObjectPath(); 544 ServerManagedObject<? extends S> ref = context.getManagedObject(path); 545 ref.deregisterChangeListener(cl); 546 } 547 changeListeners.remove(dn); 548 } 549 } 550 551 552 553 /** 554 * {@inheritDoc} 555 */ 556 @Override 557 public void performPostModify(ServerManagedObject<?> managedObject) 558 throws ConfigException { 559 // Remove all the constraints associated with this managed 560 // object and then re-register them. 561 performPostDelete(managedObject); 562 performPostAdd(managedObject); 563 } 564 } 565 566 567 568 /** 569 * The client-side constraint handler implementation which enforces 570 * referential integrity when aggregating managed objects are added 571 * or modified. 572 */ 573 private class SourceClientHandler extends ClientConstraintHandler { 574 575 /** 576 * {@inheritDoc} 577 */ 578 @Override 579 public boolean isAddAcceptable(ManagementContext context, 580 ManagedObject<?> managedObject, Collection<Message> unacceptableReasons) 581 throws AuthorizationException, CommunicationException { 582 // If all of this managed object's "enabled" properties are true 583 // then any referenced managed objects must also be enabled. 584 boolean needsEnabling = targetNeedsEnablingCondition.evaluate(context, 585 managedObject); 586 587 // Check the referenced managed objects exist and, if required, 588 // are enabled. 589 boolean isAcceptable = true; 590 Message ufn = getRelationDefinition().getUserFriendlyName(); 591 for (String name : managedObject 592 .getPropertyValues(AggregationPropertyDefinition.this)) { 593 // Retrieve the referenced managed object and make sure it 594 // exists. 595 ManagedObjectPath<?, ?> path = getChildPath(name); 596 ManagedObject<?> ref; 597 try { 598 ref = context.getManagedObject(path); 599 } catch (DefinitionDecodingException e) { 600 Message msg = ERR_CLIENT_REFINT_TARGET_INVALID.get(ufn, name, 601 getName(), e.getMessageObject()); 602 unacceptableReasons.add(msg); 603 isAcceptable = false; 604 continue; 605 } catch (ManagedObjectDecodingException e) { 606 Message msg = ERR_CLIENT_REFINT_TARGET_INVALID.get(ufn, name, 607 getName(), e.getMessageObject()); 608 unacceptableReasons.add(msg); 609 isAcceptable = false; 610 continue; 611 } catch (ManagedObjectNotFoundException e) { 612 Message msg = ERR_CLIENT_REFINT_TARGET_DANGLING_REFERENCE.get(ufn, 613 name, getName()); 614 unacceptableReasons.add(msg); 615 isAcceptable = false; 616 continue; 617 } 618 619 // Make sure the reference managed object is enabled. 620 if (needsEnabling) { 621 if (!targetIsEnabledCondition.evaluate(context, ref)) { 622 Message msg = ERR_CLIENT_REFINT_TARGET_DISABLED.get(ufn, name, 623 getName()); 624 unacceptableReasons.add(msg); 625 isAcceptable = false; 626 } 627 } 628 } 629 return isAcceptable; 630 } 631 632 633 634 /** 635 * {@inheritDoc} 636 */ 637 @Override 638 public boolean isModifyAcceptable(ManagementContext context, 639 ManagedObject<?> managedObject, Collection<Message> unacceptableReasons) 640 throws AuthorizationException, CommunicationException { 641 // The same constraint applies as for adds. 642 return isAddAcceptable(context, managedObject, unacceptableReasons); 643 } 644 645 } 646 647 648 649 /** 650 * The client-side constraint handler implementation which enforces 651 * referential integrity when aggregated managed objects are deleted 652 * or modified. 653 */ 654 private class TargetClientHandler extends ClientConstraintHandler { 655 656 /** 657 * {@inheritDoc} 658 */ 659 @Override 660 public boolean isDeleteAcceptable(ManagementContext context, 661 ManagedObjectPath<?, ?> path, Collection<Message> unacceptableReasons) 662 throws AuthorizationException, CommunicationException { 663 // Any references to the deleted managed object should cause a 664 // constraint violation. 665 boolean isAcceptable = true; 666 for (ManagedObject<?> mo : findReferences(context, 667 getManagedObjectDefinition(), path.getName())) { 668 String name = mo.getManagedObjectPath().getName(); 669 if (name == null) { 670 Message msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITHOUT_NAME.get( 671 getName(), mo.getManagedObjectDefinition().getUserFriendlyName(), 672 getManagedObjectDefinition().getUserFriendlyName()); 673 unacceptableReasons.add(msg); 674 } else { 675 Message msg = ERR_CLIENT_REFINT_CANNOT_DELETE_WITH_NAME.get( 676 getName(), mo.getManagedObjectDefinition().getUserFriendlyName(), 677 name, getManagedObjectDefinition().getUserFriendlyName()); 678 unacceptableReasons.add(msg); 679 } 680 isAcceptable = false; 681 } 682 return isAcceptable; 683 } 684 685 686 687 /** 688 * {@inheritDoc} 689 */ 690 @Override 691 public boolean isModifyAcceptable(ManagementContext context, 692 ManagedObject<?> managedObject, Collection<Message> unacceptableReasons) 693 throws AuthorizationException, CommunicationException { 694 // If the modified managed object is disabled and there are some 695 // active references then refuse the change. 696 if (targetIsEnabledCondition.evaluate(context, managedObject)) { 697 return true; 698 } 699 700 // The referenced managed object is disabled. Need to check for 701 // active references. 702 boolean isAcceptable = true; 703 for (ManagedObject<?> mo : findReferences(context, 704 getManagedObjectDefinition(), managedObject.getManagedObjectPath() 705 .getName())) { 706 if (targetNeedsEnablingCondition.evaluate(context, mo)) { 707 String name = mo.getManagedObjectPath().getName(); 708 if (name == null) { 709 Message msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITHOUT_NAME.get( 710 managedObject.getManagedObjectDefinition() 711 .getUserFriendlyName(), getName(), mo 712 .getManagedObjectDefinition().getUserFriendlyName()); 713 unacceptableReasons.add(msg); 714 } else { 715 Message msg = ERR_CLIENT_REFINT_CANNOT_DISABLE_WITH_NAME.get( 716 managedObject.getManagedObjectDefinition() 717 .getUserFriendlyName(), getName(), mo 718 .getManagedObjectDefinition().getUserFriendlyName(), name); 719 unacceptableReasons.add(msg); 720 } 721 isAcceptable = false; 722 } 723 } 724 return isAcceptable; 725 } 726 727 728 729 // Find all managed objects which reference the named managed 730 // object using this property. 731 private <CC extends ConfigurationClient> 732 List<ManagedObject<? extends CC>> findReferences( 733 ManagementContext context, AbstractManagedObjectDefinition<CC, ?> mod, 734 String name) throws AuthorizationException, CommunicationException { 735 List<ManagedObject<? extends CC>> instances = findInstances(context, mod); 736 737 Iterator<ManagedObject<? extends CC>> i = instances.iterator(); 738 while (i.hasNext()) { 739 ManagedObject<? extends CC> mo = i.next(); 740 boolean hasReference = false; 741 742 for (String value : mo 743 .getPropertyValues(AggregationPropertyDefinition.this)) { 744 if (compare(value, name) == 0) { 745 hasReference = true; 746 break; 747 } 748 } 749 750 if (!hasReference) { 751 i.remove(); 752 } 753 } 754 755 return instances; 756 } 757 758 759 760 // Find all instances of a specific type of managed object. 761 @SuppressWarnings("unchecked") 762 private <CC extends ConfigurationClient> 763 List<ManagedObject<? extends CC>> findInstances( 764 ManagementContext context, AbstractManagedObjectDefinition<CC, ?> mod) 765 throws AuthorizationException, CommunicationException { 766 List<ManagedObject<? extends CC>> instances = 767 new LinkedList<ManagedObject<? extends CC>>(); 768 769 if (mod == RootCfgDefn.getInstance()) { 770 instances.add((ManagedObject<? extends CC>) context 771 .getRootConfigurationManagedObject()); 772 } else { 773 for (RelationDefinition<? super CC, ?> rd : mod 774 .getAllReverseRelationDefinitions()) { 775 for (ManagedObject<?> parent : findInstances(context, rd 776 .getParentDefinition())) { 777 try { 778 if (rd instanceof SingletonRelationDefinition) { 779 SingletonRelationDefinition<? super CC, ?> srd = 780 (SingletonRelationDefinition<? super CC, ?>) rd; 781 ManagedObject<?> mo = parent.getChild(srd); 782 if (mo.getManagedObjectDefinition().isChildOf(mod)) { 783 instances.add((ManagedObject<? extends CC>) mo); 784 } 785 } else if (rd instanceof OptionalRelationDefinition) { 786 OptionalRelationDefinition<? super CC, ?> ord = 787 (OptionalRelationDefinition<? super CC, ?>) rd; 788 ManagedObject<?> mo = parent.getChild(ord); 789 if (mo.getManagedObjectDefinition().isChildOf(mod)) { 790 instances.add((ManagedObject<? extends CC>) mo); 791 } 792 } else if (rd instanceof InstantiableRelationDefinition) { 793 InstantiableRelationDefinition<? super CC, ?> ird = 794 (InstantiableRelationDefinition<? super CC, ?>) rd; 795 796 for (String name : parent.listChildren(ird)) { 797 ManagedObject<?> mo = parent.getChild(ird, name); 798 if (mo.getManagedObjectDefinition().isChildOf(mod)) { 799 instances.add((ManagedObject<? extends CC>) mo); 800 } 801 } 802 } 803 } catch (OperationsException e) { 804 // Ignore all operations exceptions. 805 } 806 } 807 } 808 } 809 810 return instances; 811 } 812 } 813 814 815 816 /** 817 * The tracer object for the debug logger. 818 */ 819 private static final DebugTracer TRACER = getTracer(); 820 821 822 823 /** 824 * Creates an aggregation property definition builder. 825 * 826 * @param <C> 827 * The type of client managed object configuration that 828 * this aggregation property definition refers to. 829 * @param <S> 830 * The type of server managed object configuration that 831 * this aggregation property definition refers to. 832 * @param d 833 * The managed object definition associated with this 834 * property definition. 835 * @param propertyName 836 * The property name. 837 * @return Returns the new aggregation property definition builder. 838 */ 839 public static <C extends ConfigurationClient, S extends Configuration> 840 Builder<C, S> createBuilder( 841 AbstractManagedObjectDefinition<?, ?> d, String propertyName) { 842 return new Builder<C, S>(d, propertyName); 843 } 844 845 // The active server-side referential integrity change listeners 846 // associated with this property. 847 private final Map<DN, List<ReferentialIntegrityChangeListener>> 848 changeListeners = new HashMap<DN, 849 List<ReferentialIntegrityChangeListener>>(); 850 851 // The active server-side referential integrity delete listeners 852 // associated with this property. 853 private final Map<DN, List<ReferentialIntegrityDeleteListener>> 854 deleteListeners = new HashMap<DN, 855 List<ReferentialIntegrityDeleteListener>>(); 856 857 // The name of the managed object which is the parent of the 858 // aggregated managed objects. 859 private ManagedObjectPath<?, ?> parentPath; 860 861 // The string representation of the managed object path specifying 862 // the parent of the aggregated managed objects. 863 private final String parentPathString; 864 865 // The name of a relation in the parent managed object which 866 // contains the aggregated managed objects. 867 private final String rdName; 868 869 // The relation in the parent managed object which contains the 870 // aggregated managed objects. 871 private InstantiableRelationDefinition<C, S> relationDefinition; 872 873 // The source constraint. 874 private final Constraint sourceConstraint; 875 876 // The condition which is used to determine if a referenced managed 877 // object is enabled. 878 private final Condition targetIsEnabledCondition; 879 880 // The condition which is used to determine whether or not 881 // referenced managed objects need to be enabled. 882 private final Condition targetNeedsEnablingCondition; 883 884 885 886 // Private constructor. 887 private AggregationPropertyDefinition( 888 AbstractManagedObjectDefinition<?, ?> d, String propertyName, 889 EnumSet<PropertyOption> options, AdministratorAction adminAction, 890 DefaultBehaviorProvider<String> defaultBehavior, String parentPathString, 891 String rdName, Condition targetNeedsEnablingCondition, 892 Condition targetIsEnabledCondition) { 893 super(d, String.class, propertyName, options, adminAction, defaultBehavior); 894 895 this.parentPathString = parentPathString; 896 this.rdName = rdName; 897 this.targetNeedsEnablingCondition = targetNeedsEnablingCondition; 898 this.targetIsEnabledCondition = targetIsEnabledCondition; 899 this.sourceConstraint = new Constraint() { 900 901 /** 902 * {@inheritDoc} 903 */ 904 public Collection<ClientConstraintHandler> getClientConstraintHandlers() { 905 ClientConstraintHandler handler = new SourceClientHandler(); 906 return Collections.singleton(handler); 907 } 908 909 910 911 /** 912 * {@inheritDoc} 913 */ 914 public Collection<ServerConstraintHandler> getServerConstraintHandlers() { 915 ServerConstraintHandler handler = new ServerHandler(); 916 return Collections.singleton(handler); 917 } 918 }; 919 } 920 921 922 923 /** 924 * {@inheritDoc} 925 */ 926 @Override 927 public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) { 928 return v.visitAggregation(this, p); 929 } 930 931 932 933 /** 934 * {@inheritDoc} 935 */ 936 @Override 937 public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) { 938 return v.visitAggregation(this, value, p); 939 } 940 941 942 943 /** 944 * {@inheritDoc} 945 */ 946 @Override 947 public String decodeValue(String value) 948 throws IllegalPropertyValueStringException { 949 ensureNotNull(value); 950 951 try { 952 validateValue(value); 953 return value; 954 } catch (IllegalPropertyValueException e) { 955 throw new IllegalPropertyValueStringException(this, value); 956 } 957 } 958 959 960 961 /** 962 * Constructs a DN for a referenced managed object having the 963 * provided name. This method is implemented by first calling 964 * {@link #getChildPath(String)} and then invoking 965 * {@code ManagedObjectPath.toDN()} on the returned path. 966 * 967 * @param name 968 * The name of the child managed object. 969 * @return Returns a DN for a referenced managed object having the 970 * provided name. 971 */ 972 public final DN getChildDN(String name) { 973 return getChildPath(name).toDN(); 974 } 975 976 977 978 /** 979 * Constructs a managed object path for a referenced managed object 980 * having the provided name. 981 * 982 * @param name 983 * The name of the child managed object. 984 * @return Returns a managed object path for a referenced managed 985 * object having the provided name. 986 */ 987 public final ManagedObjectPath<C, S> getChildPath(String name) { 988 return parentPath.child(relationDefinition, name); 989 } 990 991 992 993 /** 994 * Gets the name of the managed object which is the parent of the 995 * aggregated managed objects. 996 * 997 * @return Returns the name of the managed object which is the 998 * parent of the aggregated managed objects. 999 */ 1000 public final ManagedObjectPath<?, ?> getParentPath() { 1001 return parentPath; 1002 } 1003 1004 1005 1006 /** 1007 * Gets the relation in the parent managed object which contains the 1008 * aggregated managed objects. 1009 * 1010 * @return Returns the relation in the parent managed object which 1011 * contains the aggregated managed objects. 1012 */ 1013 public final InstantiableRelationDefinition<C, S> getRelationDefinition() { 1014 return relationDefinition; 1015 } 1016 1017 1018 1019 /** 1020 * Gets the constraint which should be enforced on the aggregating 1021 * managed object. 1022 * 1023 * @return Returns the constraint which should be enforced on the 1024 * aggregating managed object. 1025 */ 1026 public final Constraint getSourceConstraint() { 1027 return sourceConstraint; 1028 } 1029 1030 1031 1032 /** 1033 * Gets the optional constraint synopsis of this aggregation 1034 * property definition in the default locale. The constraint 1035 * synopsis describes when and how referenced managed objects must 1036 * be enabled. When there are no constraints between the source 1037 * managed object and the objects it references through this 1038 * aggregation, <code>null</code> is returned. 1039 * 1040 * @return Returns the optional constraint synopsis of this 1041 * aggregation property definition in the default locale, or 1042 * <code>null</code> if there is no constraint synopsis. 1043 */ 1044 public final Message getSourceConstraintSynopsis() { 1045 return getSourceConstraintSynopsis(Locale.getDefault()); 1046 } 1047 1048 1049 1050 /** 1051 * Gets the optional constraint synopsis of this aggregation 1052 * property definition in the specified locale.The constraint 1053 * synopsis describes when and how referenced managed objects must 1054 * be enabled. When there are no constraints between the source 1055 * managed object and the objects it references through this 1056 * aggregation, <code>null</code> is returned. 1057 * 1058 * @param locale 1059 * The locale. 1060 * @return Returns the optional constraint synopsis of this 1061 * aggregation property definition in the specified locale, 1062 * or <code>null</code> if there is no constraint 1063 * synopsis. 1064 */ 1065 public final Message getSourceConstraintSynopsis(Locale locale) { 1066 ManagedObjectDefinitionI18NResource resource = 1067 ManagedObjectDefinitionI18NResource.getInstance(); 1068 String property = "property." + getName() 1069 + ".syntax.aggregation.constraint-synopsis"; 1070 try { 1071 return resource 1072 .getMessage(getManagedObjectDefinition(), property, locale); 1073 } catch (MissingResourceException e) { 1074 return null; 1075 } 1076 } 1077 1078 1079 1080 /** 1081 * Gets the condition which is used to determine if a referenced 1082 * managed object is enabled. 1083 * 1084 * @return Returns the condition which is used to determine if a 1085 * referenced managed object is enabled. 1086 */ 1087 public final Condition getTargetIsEnabledCondition() { 1088 return targetIsEnabledCondition; 1089 } 1090 1091 1092 1093 /** 1094 * Gets the condition which is used to determine whether or not 1095 * referenced managed objects need to be enabled. 1096 * 1097 * @return Returns the condition which is used to determine whether 1098 * or not referenced managed objects need to be enabled. 1099 */ 1100 public final Condition getTargetNeedsEnablingCondition() { 1101 return targetNeedsEnablingCondition; 1102 } 1103 1104 1105 1106 /** 1107 * {@inheritDoc} 1108 */ 1109 @Override 1110 public String normalizeValue(String value) 1111 throws IllegalPropertyValueException { 1112 try { 1113 Reference<C, S> reference = Reference.parseName(parentPath, 1114 relationDefinition, value); 1115 return reference.getNormalizedName(); 1116 } catch (IllegalArgumentException e) { 1117 throw new IllegalPropertyValueException(this, value); 1118 } 1119 } 1120 1121 1122 1123 /** 1124 * {@inheritDoc} 1125 */ 1126 @Override 1127 public void toString(StringBuilder builder) { 1128 super.toString(builder); 1129 1130 builder.append(" parentPath="); 1131 builder.append(parentPath); 1132 1133 builder.append(" relationDefinition="); 1134 builder.append(relationDefinition.getName()); 1135 1136 builder.append(" targetNeedsEnablingCondition="); 1137 builder.append(String.valueOf(targetNeedsEnablingCondition)); 1138 1139 builder.append(" targetIsEnabledCondition="); 1140 builder.append(String.valueOf(targetIsEnabledCondition)); 1141 } 1142 1143 1144 1145 /** 1146 * {@inheritDoc} 1147 */ 1148 @Override 1149 public void validateValue(String value) throws IllegalPropertyValueException { 1150 try { 1151 Reference.parseName(parentPath, relationDefinition, value); 1152 } catch (IllegalArgumentException e) { 1153 throw new IllegalPropertyValueException(this, value); 1154 } 1155 } 1156 1157 1158 1159 /** 1160 * {@inheritDoc} 1161 */ 1162 @SuppressWarnings("unchecked") 1163 @Override 1164 public void initialize() throws Exception { 1165 // Decode the path. 1166 parentPath = ManagedObjectPath.valueOf(parentPathString); 1167 1168 // Decode the relation definition. 1169 AbstractManagedObjectDefinition<?, ?> parent = parentPath 1170 .getManagedObjectDefinition(); 1171 RelationDefinition<?, ?> rd = parent.getRelationDefinition(rdName); 1172 relationDefinition = (InstantiableRelationDefinition<C, S>) rd; 1173 1174 // Now decode the conditions. 1175 targetNeedsEnablingCondition.initialize(getManagedObjectDefinition()); 1176 targetIsEnabledCondition.initialize(rd.getChildDefinition()); 1177 1178 // Register a client-side constraint with the referenced 1179 // definition. This will be used to enforce referential integrity 1180 // for actions performed against referenced managed objects. 1181 Constraint constraint = new Constraint() { 1182 1183 /** 1184 * {@inheritDoc} 1185 */ 1186 public Collection<ClientConstraintHandler> getClientConstraintHandlers() { 1187 ClientConstraintHandler handler = new TargetClientHandler(); 1188 return Collections.singleton(handler); 1189 } 1190 1191 1192 1193 /** 1194 * {@inheritDoc} 1195 */ 1196 public Collection<ServerConstraintHandler> getServerConstraintHandlers() { 1197 return Collections.emptyList(); 1198 } 1199 }; 1200 1201 rd.getChildDefinition().registerConstraint(constraint); 1202 } 1203 1204 }