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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.forgerock.opendj.config.client.spi; 028 029import static org.forgerock.opendj.config.PropertyException.*; 030 031import java.util.ArrayList; 032import java.util.Collection; 033import java.util.Collections; 034import java.util.LinkedList; 035import java.util.List; 036import java.util.SortedSet; 037 038import org.forgerock.i18n.LocalizableMessage; 039import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider; 040import org.forgerock.opendj.config.AbstractManagedObjectDefinition; 041import org.forgerock.opendj.config.AliasDefaultBehaviorProvider; 042import org.forgerock.opendj.config.Configuration; 043import org.forgerock.opendj.config.ConfigurationClient; 044import org.forgerock.opendj.config.Constraint; 045import org.forgerock.opendj.config.DefaultBehaviorProviderVisitor; 046import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider; 047import org.forgerock.opendj.config.DefinitionDecodingException; 048import org.forgerock.opendj.config.DefinitionDecodingException.Reason; 049import org.forgerock.opendj.config.InstantiableRelationDefinition; 050import org.forgerock.opendj.config.ManagedObjectNotFoundException; 051import org.forgerock.opendj.config.ManagedObjectPath; 052import org.forgerock.opendj.config.OptionalRelationDefinition; 053import org.forgerock.opendj.config.PropertyDefinition; 054import org.forgerock.opendj.config.PropertyException; 055import org.forgerock.opendj.config.PropertyNotFoundException; 056import org.forgerock.opendj.config.PropertyOption; 057import org.forgerock.opendj.config.RelationDefinition; 058import org.forgerock.opendj.config.RelativeInheritedDefaultBehaviorProvider; 059import org.forgerock.opendj.config.SetRelationDefinition; 060import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider; 061import org.forgerock.opendj.config.client.ClientConstraintHandler; 062import org.forgerock.opendj.config.client.ManagedObject; 063import org.forgerock.opendj.config.client.ManagedObjectDecodingException; 064import org.forgerock.opendj.config.client.ManagementContext; 065import org.forgerock.opendj.config.client.OperationRejectedException; 066import org.forgerock.opendj.config.client.OperationRejectedException.OperationType; 067import org.forgerock.opendj.ldap.LdapException; 068import org.forgerock.opendj.server.config.client.RootCfgClient; 069 070/** 071 * An abstract management connection context driver which should form the basis 072 * of driver implementations. 073 */ 074public abstract class Driver { 075 076 /** 077 * A default behavior visitor used for retrieving the default values of a 078 * property. 079 * 080 * @param <T> 081 * The type of the property. 082 */ 083 private final class DefaultValueFinder<T> implements DefaultBehaviorProviderVisitor<T, Collection<T>, Void> { 084 085 /** 086 * Any exception that occurred whilst retrieving inherited default 087 * values. 088 */ 089 private PropertyException exception; 090 091 /** The path of the managed object containing the first property. */ 092 private final ManagedObjectPath<?, ?> firstPath; 093 094 /** Indicates whether the managed object has been created yet. */ 095 private final boolean isCreate; 096 097 /** The path of the managed object containing the next property. */ 098 private ManagedObjectPath<?, ?> nextPath; 099 100 /** The next property whose default values were required. */ 101 private PropertyDefinition<T> nextProperty; 102 103 /** Private constructor. */ 104 private DefaultValueFinder(ManagedObjectPath<?, ?> p, boolean isCreate) { 105 this.firstPath = p; 106 this.isCreate = isCreate; 107 } 108 109 /** {@inheritDoc} */ 110 @Override 111 public Collection<T> visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) { 112 try { 113 return getInheritedProperty(d.getManagedObjectPath(), d.getManagedObjectDefinition(), 114 d.getPropertyName()); 115 } catch (PropertyException e) { 116 exception = e; 117 return Collections.emptySet(); 118 } 119 } 120 121 /** {@inheritDoc} */ 122 @Override 123 public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) { 124 return Collections.emptySet(); 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d, Void p) { 130 Collection<String> stringValues = d.getDefaultValues(); 131 List<T> values = new ArrayList<>(stringValues.size()); 132 133 for (String stringValue : stringValues) { 134 try { 135 values.add(nextProperty.decodeValue(stringValue)); 136 } catch (PropertyException e) { 137 exception = PropertyException.defaultBehaviorException(nextProperty, e); 138 break; 139 } 140 } 141 142 return values; 143 } 144 145 /** {@inheritDoc} */ 146 @Override 147 public Collection<T> visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, Void p) { 148 try { 149 return getInheritedProperty(d.getManagedObjectPath(nextPath), d.getManagedObjectDefinition(), 150 d.getPropertyName()); 151 } catch (PropertyException e) { 152 exception = e; 153 return Collections.emptySet(); 154 } 155 } 156 157 /** {@inheritDoc} */ 158 @Override 159 public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d, Void p) { 160 return Collections.emptySet(); 161 } 162 163 /** Find the default values for the next path/property. */ 164 private Collection<T> find(ManagedObjectPath<?, ?> p, PropertyDefinition<T> pd) { 165 this.nextPath = p; 166 this.nextProperty = pd; 167 168 Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(this, null); 169 170 if (exception != null) { 171 throw exception; 172 } 173 174 if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) { 175 throw defaultBehaviorException(pd, propertyIsSingleValuedException(pd)); 176 } 177 178 return values; 179 } 180 181 /** Get an inherited property value. */ 182 @SuppressWarnings("unchecked") 183 private Collection<T> getInheritedProperty(ManagedObjectPath<?, ?> target, 184 AbstractManagedObjectDefinition<?, ?> definition, String propertyName) { 185 // First check that the requested type of managed object 186 // corresponds to the path. 187 AbstractManagedObjectDefinition<?, ?> actual = target.getManagedObjectDefinition(); 188 if (!definition.isParentOf(actual)) { 189 throw PropertyException.defaultBehaviorException(nextProperty, 190 new DefinitionDecodingException(actual, Reason.WRONG_TYPE_INFORMATION)); 191 } 192 193 // Save the current property in case of recursion. 194 PropertyDefinition<T> pd1 = nextProperty; 195 196 try { 197 // Determine the requested property definition. 198 PropertyDefinition<T> pd2; 199 try { 200 // FIXME: we use the definition taken from the default 201 // behavior here when we should really use the exact 202 // definition of the component being created. 203 PropertyDefinition<?> pdTmp = definition.getPropertyDefinition(propertyName); 204 pd2 = pd1.getClass().cast(pdTmp); 205 } catch (IllegalArgumentException | ClassCastException e) { 206 throw new PropertyNotFoundException(propertyName); 207 } 208 209 // If the path relates to the current managed object and the 210 // managed object is in the process of being created it won't 211 // exist, so we should just use the default values of the 212 // referenced property. 213 if (isCreate && firstPath.equals(target)) { 214 // Recursively retrieve this property's default values. 215 Collection<T> tmp = find(target, pd2); 216 Collection<T> values = new ArrayList<>(tmp.size()); 217 for (T value : tmp) { 218 pd1.validateValue(value); 219 values.add(value); 220 } 221 return values; 222 } else { 223 // FIXME: issue 2481 - this is broken if the referenced property 224 // inherits its defaults from the newly created managed object. 225 return getPropertyValues(target, pd2); 226 } 227 } catch (PropertyException | DefinitionDecodingException | PropertyNotFoundException 228 | LdapException | ManagedObjectNotFoundException e) { 229 throw PropertyException.defaultBehaviorException(pd1, e); 230 } 231 } 232 } 233 234 /** Creates a new abstract driver. */ 235 protected Driver() { 236 // Do nothing. 237 } 238 239 /** Closes any context associated with this management context driver. */ 240 public void close() { 241 // do nothing by default 242 } 243 244 /** 245 * Deletes the named instantiable child managed object from the named parent 246 * managed object. 247 * 248 * @param <C> 249 * The type of client managed object configuration that the 250 * relation definition refers to. 251 * @param <S> 252 * The type of server managed object configuration that the 253 * relation definition refers to. 254 * @param parent 255 * The path of the parent managed object. 256 * @param rd 257 * The instantiable relation definition. 258 * @param name 259 * The name of the child managed object to be removed. 260 * @return Returns <code>true</code> if the named instantiable child managed 261 * object was found, or <code>false</code> if it was not found. 262 * @throws IllegalArgumentException 263 * If the relation definition is not associated with the parent 264 * managed object's definition. 265 * @throws ManagedObjectNotFoundException 266 * If the parent managed object could not be found. 267 * @throws OperationRejectedException 268 * If the managed object cannot be removed due to some 269 * client-side or server-side constraint which cannot be 270 * satisfied (for example, if it is referenced by another 271 * managed object). 272 * @throws LdapException 273 * If any other error occurs. 274 */ 275 public final <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject( 276 ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd, String name) 277 throws ManagedObjectNotFoundException, OperationRejectedException, LdapException { 278 validateRelationDefinition(parent, rd); 279 ManagedObjectPath<?, ?> child = parent.child(rd, name); 280 return doDeleteManagedObject(child); 281 } 282 283 /** 284 * Deletes the optional child managed object from the named parent managed 285 * object. 286 * 287 * @param <C> 288 * The type of client managed object configuration that the 289 * relation definition refers to. 290 * @param <S> 291 * The type of server managed object configuration that the 292 * relation definition refers to. 293 * @param parent 294 * The path of the parent managed object. 295 * @param rd 296 * The optional relation definition. 297 * @return Returns <code>true</code> if the optional child managed object 298 * was found, or <code>false</code> if it was not found. 299 * @throws IllegalArgumentException 300 * If the relation definition is not associated with the parent 301 * managed object's definition. 302 * @throws ManagedObjectNotFoundException 303 * If the parent managed object could not be found. 304 * @throws OperationRejectedException 305 * If the managed object cannot be removed due to some 306 * client-side or server-side constraint which cannot be 307 * satisfied (for example, if it is referenced by another 308 * managed object). 309 * @throws LdapException 310 * If any other error occurs. 311 */ 312 public final <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject( 313 ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd) throws ManagedObjectNotFoundException, 314 OperationRejectedException, LdapException { 315 validateRelationDefinition(parent, rd); 316 ManagedObjectPath<?, ?> child = parent.child(rd); 317 return doDeleteManagedObject(child); 318 } 319 320 /** 321 * Deletes the named instantiable child managed object from the named parent 322 * managed object. 323 * 324 * @param <C> 325 * The type of client managed object configuration that the 326 * relation definition refers to. 327 * @param <S> 328 * The type of server managed object configuration that the 329 * relation definition refers to. 330 * @param parent 331 * The path of the parent managed object. 332 * @param rd 333 * The instantiable relation definition. 334 * @param name 335 * The name of the child managed object to be removed. 336 * @return Returns <code>true</code> if the named instantiable child managed 337 * object was found, or <code>false</code> if it was not found. 338 * @throws IllegalArgumentException 339 * If the relation definition is not associated with the parent 340 * managed object's definition. 341 * @throws ManagedObjectNotFoundException 342 * If the parent managed object could not be found. 343 * @throws OperationRejectedException 344 * If the managed object cannot be removed due to some 345 * client-side or server-side constraint which cannot be 346 * satisfied (for example, if it is referenced by another 347 * managed object). 348 * @throws LdapException 349 * If any other error occurs. 350 */ 351 public final <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject( 352 ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd, String name) 353 throws ManagedObjectNotFoundException, OperationRejectedException, LdapException { 354 validateRelationDefinition(parent, rd); 355 ManagedObjectPath<?, ?> child = parent.child(rd, name); 356 return doDeleteManagedObject(child); 357 } 358 359 /** 360 * Gets the named managed object. The path is guaranteed to be non-empty, so 361 * implementations do not need to worry about handling this special case. 362 * 363 * @param <C> 364 * The type of client managed object configuration that the path 365 * definition refers to. 366 * @param <S> 367 * The type of server managed object configuration that the path 368 * definition refers to. 369 * @param path 370 * The non-empty path of the managed object. 371 * @return Returns the named managed object. 372 * @throws DefinitionDecodingException 373 * If the managed object was found but its type could not be 374 * determined. 375 * @throws ManagedObjectDecodingException 376 * If the managed object was found but one or more of its 377 * properties could not be decoded. 378 * @throws ManagedObjectNotFoundException 379 * If the requested managed object could not be found on the 380 * server. 381 * @throws LdapException 382 * If any other error occurs. 383 */ 384 // @Checkstyle:ignore 385 public abstract <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getManagedObject( 386 ManagedObjectPath<C, S> path) throws DefinitionDecodingException, ManagedObjectDecodingException, 387 ManagedObjectNotFoundException, LdapException; 388 389 /** 390 * Gets the effective values of a property in the named managed object. 391 * <p> 392 * Implementations MUST NOT not use 393 * {@link #getManagedObject(ManagedObjectPath)} to read the referenced 394 * managed object in its entirety. Specifically, implementations MUST only 395 * attempt to resolve the default values for the requested property and its 396 * dependencies (if it uses inherited defaults). This is to avoid infinite 397 * recursion where a managed object contains a property which inherits 398 * default values from another property in the same managed object. 399 * 400 * @param <C> 401 * The type of client managed object configuration that the path 402 * definition refers to. 403 * @param <S> 404 * The type of server managed object configuration that the path 405 * definition refers to. 406 * @param <P> 407 * The type of the property to be retrieved. 408 * @param path 409 * The path of the managed object containing the property. 410 * @param pd 411 * The property to be retrieved. 412 * @return Returns the property's effective values, or an empty set if there 413 * are no values defined. 414 * @throws IllegalArgumentException 415 * If the property definition is not associated with the 416 * referenced managed object's definition. 417 * @throws DefinitionDecodingException 418 * If the managed object was found but its type could not be 419 * determined. 420 * @throws PropertyException 421 * If the managed object was found but the requested property 422 * could not be decoded. 423 * @throws ManagedObjectNotFoundException 424 * If the requested managed object could not be found on the 425 * server. 426 * @throws LdapException 427 * If any other error occurs. 428 */ 429 public abstract <C extends ConfigurationClient, S extends Configuration, P> SortedSet<P> getPropertyValues( 430 ManagedObjectPath<C, S> path, PropertyDefinition<P> pd) throws DefinitionDecodingException, 431 ManagedObjectNotFoundException, LdapException; 432 433 /** 434 * Gets the root configuration managed object associated with this 435 * management context driver. 436 * 437 * @return Returns the root configuration managed object associated with 438 * this management context driver. 439 */ 440 public abstract ManagedObject<RootCfgClient> getRootConfigurationManagedObject(); 441 442 /** 443 * Lists the child managed objects of the named parent managed object which 444 * are a sub-type of the specified managed object definition. 445 * 446 * @param <C> 447 * The type of client managed object configuration that the 448 * relation definition refers to. 449 * @param <S> 450 * The type of server managed object configuration that the 451 * relation definition refers to. 452 * @param parent 453 * The path of the parent managed object. 454 * @param rd 455 * The instantiable relation definition. 456 * @param d 457 * The managed object definition. 458 * @return Returns the names of the child managed objects which are a 459 * sub-type of the specified managed object definition. 460 * @throws IllegalArgumentException 461 * If the relation definition is not associated with the parent 462 * managed object's definition. 463 * @throws ManagedObjectNotFoundException 464 * If the parent managed object could not be found. 465 * @throws LdapException 466 * If any other error occurs. 467 */ 468 public abstract <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects( 469 ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd, 470 AbstractManagedObjectDefinition<? extends C, ? extends S> d) throws ManagedObjectNotFoundException, 471 LdapException; 472 473 /** 474 * Lists the child managed objects of the named parent managed object which 475 * are a sub-type of the specified managed object definition. 476 * 477 * @param <C> 478 * The type of client managed object configuration that the 479 * relation definition refers to. 480 * @param <S> 481 * The type of server managed object configuration that the 482 * relation definition refers to. 483 * @param parent 484 * The path of the parent managed object. 485 * @param rd 486 * The set relation definition. 487 * @param d 488 * The managed object definition. 489 * @return Returns the names of the child managed objects which are a 490 * sub-type of the specified managed object definition. 491 * @throws IllegalArgumentException 492 * If the relation definition is not associated with the parent 493 * managed object's definition. 494 * @throws ManagedObjectNotFoundException 495 * If the parent managed object could not be found. 496 * @throws LdapException 497 * If any other error occurs. 498 */ 499 public abstract <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects( 500 ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd, 501 AbstractManagedObjectDefinition<? extends C, ? extends S> d) throws ManagedObjectNotFoundException, 502 LdapException; 503 504 /** 505 * Determines whether or not the named managed object exists. 506 * <p> 507 * Implementations should always return <code>true</code> when the provided 508 * path is empty. 509 * 510 * @param path 511 * The path of the named managed object. 512 * @return Returns <code>true</code> if the named managed object exists, 513 * <code>false</code> otherwise. 514 * @throws ManagedObjectNotFoundException 515 * If the parent managed object could not be found. 516 * @throws LdapException 517 * If any other error occurs. 518 */ 519 public abstract boolean managedObjectExists(ManagedObjectPath<?, ?> path) throws ManagedObjectNotFoundException, 520 LdapException; 521 522 /** 523 * Deletes the named managed object. 524 * <p> 525 * Implementations do not need check whether the named managed object 526 * exists, nor do they need to enforce client constraints. 527 * 528 * @param <C> 529 * The type of client managed object configuration that the 530 * relation definition refers to. 531 * @param <S> 532 * The type of server managed object configuration that the 533 * relation definition refers to. 534 * @param path 535 * The path of the managed object to be deleted. 536 * @throws OperationRejectedException 537 * If the managed object cannot be removed due to some 538 * server-side constraint which cannot be satisfied (for 539 * example, if it is referenced by another managed object). 540 * @throws LdapException 541 * If any other error occurs. 542 */ 543 protected abstract <C extends ConfigurationClient, S extends Configuration> void deleteManagedObject( 544 ManagedObjectPath<C, S> path) throws OperationRejectedException, LdapException; 545 546 /** 547 * Gets the default values for the specified property. 548 * 549 * @param <P> 550 * The type of the property. 551 * @param p 552 * The managed object path of the current managed object. 553 * @param pd 554 * The property definition. 555 * @param isCreate 556 * Indicates whether the managed object has been created yet. 557 * @return Returns the default values for the specified property. 558 * @throws PropertyException 559 * If the default values could not be retrieved or decoded 560 * properly. 561 */ 562 protected final <P> Collection<P> findDefaultValues(ManagedObjectPath<?, ?> p, PropertyDefinition<P> pd, 563 boolean isCreate) { 564 DefaultValueFinder<P> v = new DefaultValueFinder<>(p, isCreate); 565 return v.find(p, pd); 566 } 567 568 /** 569 * Gets the management context associated with this driver. 570 * 571 * @return Returns the management context associated with this driver. 572 */ 573 protected abstract ManagementContext getManagementContext(); 574 575 /** 576 * Validate that a relation definition belongs to the managed object 577 * referenced by the provided path. 578 * 579 * @param path 580 * The parent managed object path. 581 * @param rd 582 * The relation definition. 583 * @throws IllegalArgumentException 584 * If the relation definition does not belong to the managed 585 * object definition. 586 */ 587 protected final void validateRelationDefinition(ManagedObjectPath<?, ?> path, RelationDefinition<?, ?> rd) { 588 AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition(); 589 RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName()); 590 if (tmp != rd) { 591 throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a " 592 + d.getName()); 593 } 594 } 595 596 /** 597 * Remove a managed object, first ensuring that the parent exists, 598 * then ensuring that the child exists, before ensuring that any 599 * constraints are satisfied. 600 */ 601 private <C extends ConfigurationClient, S extends Configuration> boolean doDeleteManagedObject( 602 ManagedObjectPath<C, S> path) throws ManagedObjectNotFoundException, OperationRejectedException, 603 LdapException { 604 // First make sure that the parent exists. 605 if (!managedObjectExists(path.parent())) { 606 throw new ManagedObjectNotFoundException(); 607 } 608 609 // Make sure that the targeted managed object exists. 610 if (!managedObjectExists(path)) { 611 return false; 612 } 613 614 // The targeted managed object is guaranteed to exist, so enforce 615 // any constraints. 616 AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition(); 617 List<LocalizableMessage> messages = new LinkedList<>(); 618 if (!isAcceptable(path, d, messages)) { 619 throw new OperationRejectedException(OperationType.DELETE, d.getUserFriendlyName(), messages); 620 } 621 622 deleteManagedObject(path); 623 return true; 624 } 625 626 private <C extends ConfigurationClient, S extends Configuration> 627 boolean isAcceptable(ManagedObjectPath<C, S> path, AbstractManagedObjectDefinition<?, ?> d, 628 List<LocalizableMessage> messages) throws LdapException { 629 for (Constraint constraint : d.getAllConstraints()) { 630 for (ClientConstraintHandler handler : constraint.getClientConstraintHandlers()) { 631 ManagementContext context = getManagementContext(); 632 if (!handler.isDeleteAcceptable(context, path, messages)) { 633 return false; 634 } 635 } 636 } 637 return true; 638 } 639}