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