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