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