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 java.util.Collection; 020import java.util.Collections; 021import java.util.LinkedList; 022import java.util.List; 023import java.util.Set; 024import java.util.SortedSet; 025import java.util.TreeSet; 026 027import org.forgerock.i18n.LocalizableMessage; 028import org.forgerock.opendj.config.AbstractManagedObjectDefinition; 029import org.forgerock.opendj.config.Configuration; 030import org.forgerock.opendj.config.ConfigurationClient; 031import org.forgerock.opendj.config.Constraint; 032import org.forgerock.opendj.config.PropertyException; 033import org.forgerock.opendj.config.DefaultManagedObject; 034import org.forgerock.opendj.config.DefinitionDecodingException; 035import org.forgerock.opendj.config.InstantiableRelationDefinition; 036import org.forgerock.opendj.config.ManagedObjectAlreadyExistsException; 037import org.forgerock.opendj.config.ManagedObjectDefinition; 038import org.forgerock.opendj.config.ManagedObjectNotFoundException; 039import org.forgerock.opendj.config.ManagedObjectPath; 040import org.forgerock.opendj.config.OptionalRelationDefinition; 041import org.forgerock.opendj.config.PropertyDefinition; 042import org.forgerock.opendj.config.PropertyOption; 043import org.forgerock.opendj.config.RelationDefinition; 044import org.forgerock.opendj.config.RelationDefinitionVisitor; 045import org.forgerock.opendj.config.SetRelationDefinition; 046import org.forgerock.opendj.config.SingletonRelationDefinition; 047import org.forgerock.opendj.config.DefinitionDecodingException.Reason; 048import org.forgerock.opendj.config.client.ClientConstraintHandler; 049import org.forgerock.opendj.config.client.ConcurrentModificationException; 050import org.forgerock.opendj.config.client.IllegalManagedObjectNameException; 051import org.forgerock.opendj.config.client.ManagedObject; 052import org.forgerock.opendj.config.client.ManagedObjectDecodingException; 053import org.forgerock.opendj.config.client.ManagementContext; 054import org.forgerock.opendj.config.client.MissingMandatoryPropertiesException; 055import org.forgerock.opendj.config.client.OperationRejectedException; 056import org.forgerock.opendj.config.client.OperationRejectedException.OperationType; 057import org.forgerock.opendj.ldap.LdapException; 058 059/** 060 * An abstract managed object implementation. 061 * 062 * @param <T> 063 * The type of client configuration represented by the client managed 064 * object. 065 */ 066public abstract class AbstractManagedObject<T extends ConfigurationClient> implements ManagedObject<T> { 067 068 /** Creates any default managed objects associated with a relation definition. */ 069 private final class DefaultManagedObjectFactory implements RelationDefinitionVisitor<Void, Void> { 070 071 /** Possible exceptions. */ 072 private ManagedObjectAlreadyExistsException moaee; 073 private MissingMandatoryPropertiesException mmpe; 074 private ConcurrentModificationException cme; 075 private OperationRejectedException ore; 076 private LdapException ere; 077 078 @Override 079 public <C extends ConfigurationClient, S extends Configuration> Void visitInstantiable( 080 InstantiableRelationDefinition<C, S> rd, Void p) { 081 for (String name : rd.getDefaultManagedObjectNames()) { 082 DefaultManagedObject<? extends C, ? extends S> dmo = rd.getDefaultManagedObject(name); 083 ManagedObjectDefinition<? extends C, ? extends S> d = dmo.getManagedObjectDefinition(); 084 ManagedObject<? extends C> child; 085 try { 086 child = createChild(rd, d, name, null); 087 } catch (IllegalManagedObjectNameException e) { 088 // This should not happen. 089 throw new RuntimeException(e); 090 } 091 createDefaultManagedObject(d, child, dmo); 092 } 093 return null; 094 } 095 096 @Override 097 public <C extends ConfigurationClient, S extends Configuration> Void visitOptional( 098 OptionalRelationDefinition<C, S> rd, Void p) { 099 if (rd.getDefaultManagedObject() != null) { 100 DefaultManagedObject<? extends C, ? extends S> dmo = rd.getDefaultManagedObject(); 101 ManagedObjectDefinition<? extends C, ? extends S> d = dmo.getManagedObjectDefinition(); 102 ManagedObject<? extends C> child = createChild(rd, d, null); 103 createDefaultManagedObject(d, child, dmo); 104 } 105 return null; 106 } 107 108 @Override 109 public <C extends ConfigurationClient, S extends Configuration> Void visitSingleton( 110 SingletonRelationDefinition<C, S> rd, Void p) { 111 // Do nothing - not possible to create singletons 112 // dynamically. 113 return null; 114 } 115 116 @Override 117 public <C extends ConfigurationClient, S extends Configuration> Void visitSet(SetRelationDefinition<C, S> rd, 118 Void p) { 119 for (String name : rd.getDefaultManagedObjectNames()) { 120 DefaultManagedObject<? extends C, ? extends S> dmo = rd.getDefaultManagedObject(name); 121 ManagedObjectDefinition<? extends C, ? extends S> d = dmo.getManagedObjectDefinition(); 122 ManagedObject<? extends C> child = createChild(rd, d, null); 123 createDefaultManagedObject(d, child, dmo); 124 } 125 return null; 126 } 127 128 /** Create the child managed object. */ 129 private void createDefaultManagedObject(ManagedObjectDefinition<?, ?> d, ManagedObject<?> child, 130 DefaultManagedObject<?, ?> dmo) { 131 for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) { 132 setPropertyValues(child, pd, dmo); 133 } 134 135 try { 136 child.commit(); 137 } catch (ManagedObjectAlreadyExistsException e) { 138 moaee = e; 139 } catch (MissingMandatoryPropertiesException e) { 140 mmpe = e; 141 } catch (ConcurrentModificationException e) { 142 cme = e; 143 } catch (OperationRejectedException e) { 144 ore = e; 145 } catch (LdapException e) { 146 ere = e; 147 } 148 } 149 150 /** 151 * Creates the default managed objects associated with the provided 152 * relation definition. 153 * 154 * @param rd 155 * The relation definition. 156 */ 157 private void createDefaultManagedObjects(RelationDefinition<?, ?> rd) throws LdapException, 158 ConcurrentModificationException, MissingMandatoryPropertiesException, 159 ManagedObjectAlreadyExistsException, OperationRejectedException { 160 rd.accept(this, null); 161 162 if (ere != null) { 163 throw ere; 164 } else if (cme != null) { 165 throw cme; 166 } else if (mmpe != null) { 167 throw mmpe; 168 } else if (moaee != null) { 169 throw moaee; 170 } else if (ore != null) { 171 throw ore; 172 } 173 } 174 175 /** Set property values. */ 176 private <P> void setPropertyValues(ManagedObject<?> mo, PropertyDefinition<P> pd, 177 DefaultManagedObject<?, ?> dmo) { 178 mo.setPropertyValues(pd, dmo.getPropertyValues(pd)); 179 } 180 } 181 182 /** The managed object definition associated with this managed object. */ 183 private final ManagedObjectDefinition<T, ? extends Configuration> definition; 184 185 /** 186 * Indicates whether this managed object exists on the server 187 * (false means the managed object is new and has not been committed). 188 */ 189 private boolean existsOnServer; 190 191 /** Optional naming property definition. */ 192 private final PropertyDefinition<?> namingPropertyDefinition; 193 194 /** The path associated with this managed object. */ 195 private ManagedObjectPath<T, ? extends Configuration> path; 196 197 /** The managed object's properties. */ 198 private final PropertySet properties; 199 200 /** 201 * Creates a new abstract managed object. 202 * 203 * @param d 204 * The managed object's definition. 205 * @param path 206 * The managed object's path. 207 * @param properties 208 * The managed object's properties. 209 * @param existsOnServer 210 * Indicates whether the managed object exists on the server 211 * (false means the managed object is new and has not been committed). 212 * @param namingPropertyDefinition 213 * Optional naming property definition. 214 */ 215 protected AbstractManagedObject(ManagedObjectDefinition<T, ? extends Configuration> d, 216 ManagedObjectPath<T, ? extends Configuration> path, PropertySet properties, boolean existsOnServer, 217 PropertyDefinition<?> namingPropertyDefinition) { 218 this.definition = d; 219 this.path = path; 220 this.properties = properties; 221 this.existsOnServer = existsOnServer; 222 this.namingPropertyDefinition = namingPropertyDefinition; 223 } 224 225 @Override 226 public final void commit() throws ManagedObjectAlreadyExistsException, MissingMandatoryPropertiesException, 227 ConcurrentModificationException, OperationRejectedException, LdapException { 228 // First make sure all mandatory properties are defined. 229 List<PropertyException> exceptions = new LinkedList<>(); 230 231 for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) { 232 Property<?> p = getProperty(pd); 233 if (pd.hasOption(PropertyOption.MANDATORY) && p.getEffectiveValues().isEmpty()) { 234 exceptions.add(PropertyException.propertyIsMandatoryException(pd)); 235 } 236 } 237 238 if (!exceptions.isEmpty()) { 239 throw new MissingMandatoryPropertiesException(definition.getUserFriendlyName(), exceptions, 240 !existsOnServer); 241 } 242 243 // Now enforce any constraints. 244 List<LocalizableMessage> messages = new LinkedList<>(); 245 boolean isAcceptable = true; 246 ManagementContext context = getDriver().getManagementContext(); 247 248 for (Constraint constraint : definition.getAllConstraints()) { 249 for (ClientConstraintHandler handler : constraint.getClientConstraintHandlers()) { 250 if (existsOnServer) { 251 if (!handler.isModifyAcceptable(context, this, messages)) { 252 isAcceptable = false; 253 } 254 } else { 255 if (!handler.isAddAcceptable(context, this, messages)) { 256 isAcceptable = false; 257 } 258 } 259 } 260 if (!isAcceptable) { 261 break; 262 } 263 } 264 265 if (!isAcceptable) { 266 if (existsOnServer) { 267 throw new OperationRejectedException(OperationType.MODIFY, definition.getUserFriendlyName(), messages); 268 } else { 269 throw new OperationRejectedException(OperationType.CREATE, definition.getUserFriendlyName(), messages); 270 } 271 } 272 273 // Commit the managed object. 274 if (existsOnServer) { 275 modifyExistingManagedObject(); 276 } else { 277 addNewManagedObject(); 278 } 279 280 // Make all pending property values active. 281 properties.commit(); 282 283 // If the managed object was created make sure that any default 284 // subordinate managed objects are also created. 285 if (!existsOnServer) { 286 DefaultManagedObjectFactory factory = new DefaultManagedObjectFactory(); 287 for (RelationDefinition<?, ?> rd : definition.getAllRelationDefinitions()) { 288 factory.createDefaultManagedObjects(rd); 289 } 290 291 existsOnServer = true; 292 } 293 } 294 295 @Override 296 public final <C extends ConfigurationClient, S extends Configuration, C1 extends C> ManagedObject<C1> createChild( 297 InstantiableRelationDefinition<C, S> r, ManagedObjectDefinition<C1, ? extends S> d, String name, 298 Collection<PropertyException> exceptions) throws IllegalManagedObjectNameException { 299 validateRelationDefinition(r); 300 301 // Empty names are not allowed. 302 if (name.trim().length() == 0) { 303 throw new IllegalManagedObjectNameException(name); 304 } 305 306 // If the relation uses a naming property definition then it must 307 // be a valid value. 308 PropertyDefinition<?> pd = r.getNamingPropertyDefinition(); 309 if (pd != null) { 310 try { 311 pd.decodeValue(name); 312 } catch (PropertyException e) { 313 throw new IllegalManagedObjectNameException(name, pd); 314 } 315 } 316 317 ManagedObjectPath<C1, ? extends S> childPath = path.child(r, d, name); 318 return createNewManagedObject(d, childPath, pd, name, exceptions); 319 } 320 321 @Override 322 public final <C extends ConfigurationClient, S extends Configuration, C1 extends C> ManagedObject<C1> createChild( 323 OptionalRelationDefinition<C, S> r, ManagedObjectDefinition<C1, ? extends S> d, 324 Collection<PropertyException> exceptions) { 325 validateRelationDefinition(r); 326 ManagedObjectPath<C1, ? extends S> childPath = path.child(r, d); 327 return createNewManagedObject(d, childPath, null, null, exceptions); 328 } 329 330 @Override 331 public final <C extends ConfigurationClient, S extends Configuration, C1 extends C> ManagedObject<C1> createChild( 332 SetRelationDefinition<C, S> r, ManagedObjectDefinition<C1, ? extends S> d, 333 Collection<PropertyException> exceptions) { 334 validateRelationDefinition(r); 335 336 ManagedObjectPath<C1, ? extends S> childPath = path.child(r, d); 337 return createNewManagedObject(d, childPath, null, null, exceptions); 338 } 339 340 @Override 341 public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild( 342 InstantiableRelationDefinition<C, S> r, String name) throws DefinitionDecodingException, 343 ManagedObjectDecodingException, ManagedObjectNotFoundException, ConcurrentModificationException, 344 LdapException { 345 validateRelationDefinition(r); 346 ensureThisManagedObjectExists(); 347 Driver ctx = getDriver(); 348 return ctx.getManagedObject(path.child(r, name)); 349 } 350 351 @Override 352 public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild( 353 OptionalRelationDefinition<C, S> r) throws DefinitionDecodingException, ManagedObjectDecodingException, 354 ManagedObjectNotFoundException, ConcurrentModificationException, LdapException { 355 validateRelationDefinition(r); 356 ensureThisManagedObjectExists(); 357 Driver ctx = getDriver(); 358 return ctx.getManagedObject(path.child(r)); 359 } 360 361 @Override 362 public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild( 363 SingletonRelationDefinition<C, S> r) throws DefinitionDecodingException, ManagedObjectDecodingException, 364 ManagedObjectNotFoundException, ConcurrentModificationException, LdapException { 365 validateRelationDefinition(r); 366 ensureThisManagedObjectExists(); 367 Driver ctx = getDriver(); 368 return ctx.getManagedObject(path.child(r)); 369 } 370 371 @Override 372 public final <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getChild( 373 SetRelationDefinition<C, S> r, String name) throws DefinitionDecodingException, 374 ManagedObjectDecodingException, ManagedObjectNotFoundException, ConcurrentModificationException, 375 LdapException { 376 validateRelationDefinition(r); 377 ensureThisManagedObjectExists(); 378 Driver ctx = getDriver(); 379 380 AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition(); 381 AbstractManagedObjectDefinition<? extends C, ? extends S> cd; 382 383 try { 384 cd = d.getChild(name); 385 } catch (IllegalArgumentException e) { 386 // Unrecognized definition name - report this as a decoding 387 // exception. 388 throw new DefinitionDecodingException(d, Reason.WRONG_TYPE_INFORMATION); 389 } 390 391 return ctx.getManagedObject(path.child(r, cd)); 392 } 393 394 @Override 395 public final T getConfiguration() { 396 return definition.createClientConfiguration(this); 397 } 398 399 @Override 400 public final ManagedObjectDefinition<T, ? extends Configuration> getManagedObjectDefinition() { 401 return definition; 402 } 403 404 @Override 405 public final ManagedObjectPath<T, ? extends Configuration> getManagedObjectPath() { 406 return path; 407 } 408 409 @Override 410 public final <P> SortedSet<P> getPropertyDefaultValues(PropertyDefinition<P> pd) { 411 return new TreeSet<>(getProperty(pd).getDefaultValues()); 412 } 413 414 @Override 415 public final <P> P getPropertyValue(PropertyDefinition<P> pd) { 416 Set<P> values = getProperty(pd).getEffectiveValues(); 417 if (!values.isEmpty()) { 418 return values.iterator().next(); 419 } 420 return null; 421 } 422 423 @Override 424 public final <P> SortedSet<P> getPropertyValues(PropertyDefinition<P> pd) { 425 return new TreeSet<>(getProperty(pd).getEffectiveValues()); 426 } 427 428 @Override 429 public final <C extends ConfigurationClient, S extends Configuration> boolean hasChild( 430 OptionalRelationDefinition<C, S> r) throws ConcurrentModificationException, LdapException { 431 validateRelationDefinition(r); 432 Driver ctx = getDriver(); 433 try { 434 return ctx.managedObjectExists(path.child(r)); 435 } catch (ManagedObjectNotFoundException e) { 436 throw new ConcurrentModificationException(); 437 } 438 } 439 440 @Override 441 public final boolean isPropertyPresent(PropertyDefinition<?> pd) { 442 return !getProperty(pd).isEmpty(); 443 } 444 445 @Override 446 public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren( 447 InstantiableRelationDefinition<C, S> r) throws ConcurrentModificationException, LdapException { 448 return listChildren(r, r.getChildDefinition()); 449 } 450 451 @Override 452 public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren( 453 InstantiableRelationDefinition<C, S> r, AbstractManagedObjectDefinition<? extends C, ? extends S> d) 454 throws ConcurrentModificationException, LdapException { 455 validateRelationDefinition(r); 456 Driver ctx = getDriver(); 457 try { 458 return ctx.listManagedObjects(path, r, d); 459 } catch (ManagedObjectNotFoundException e) { 460 throw new ConcurrentModificationException(); 461 } 462 } 463 464 @Override 465 public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren( 466 SetRelationDefinition<C, S> r) throws ConcurrentModificationException, LdapException { 467 return listChildren(r, r.getChildDefinition()); 468 } 469 470 @Override 471 public final <C extends ConfigurationClient, S extends Configuration> String[] listChildren( 472 SetRelationDefinition<C, S> r, AbstractManagedObjectDefinition<? extends C, ? extends S> d) 473 throws ConcurrentModificationException, LdapException { 474 validateRelationDefinition(r); 475 Driver ctx = getDriver(); 476 try { 477 return ctx.listManagedObjects(path, r, d); 478 } catch (ManagedObjectNotFoundException e) { 479 throw new ConcurrentModificationException(); 480 } 481 } 482 483 @Override 484 public final <C extends ConfigurationClient, S extends Configuration> void removeChild( 485 InstantiableRelationDefinition<C, S> r, String name) throws ManagedObjectNotFoundException, 486 OperationRejectedException, ConcurrentModificationException, LdapException { 487 validateRelationDefinition(r); 488 Driver ctx = getDriver(); 489 boolean found; 490 491 try { 492 found = ctx.deleteManagedObject(path, r, name); 493 } catch (ManagedObjectNotFoundException e) { 494 throw new ConcurrentModificationException(); 495 } 496 497 if (!found) { 498 throw new ManagedObjectNotFoundException(); 499 } 500 } 501 502 @Override 503 public final <C extends ConfigurationClient, S extends Configuration> void removeChild( 504 OptionalRelationDefinition<C, S> r) throws ManagedObjectNotFoundException, OperationRejectedException, 505 ConcurrentModificationException, LdapException { 506 validateRelationDefinition(r); 507 Driver ctx = getDriver(); 508 boolean found; 509 510 try { 511 found = ctx.deleteManagedObject(path, r); 512 } catch (ManagedObjectNotFoundException e) { 513 throw new ConcurrentModificationException(); 514 } 515 516 if (!found) { 517 throw new ManagedObjectNotFoundException(); 518 } 519 } 520 521 @Override 522 public final <C extends ConfigurationClient, S extends Configuration> void removeChild( 523 SetRelationDefinition<C, S> r, String name) throws ManagedObjectNotFoundException, 524 OperationRejectedException, ConcurrentModificationException, LdapException { 525 validateRelationDefinition(r); 526 Driver ctx = getDriver(); 527 boolean found; 528 529 try { 530 found = ctx.deleteManagedObject(path, r, name); 531 } catch (ManagedObjectNotFoundException e) { 532 throw new ConcurrentModificationException(); 533 } 534 535 if (!found) { 536 throw new ManagedObjectNotFoundException(); 537 } 538 } 539 540 @Override 541 public final <P> void setPropertyValue(PropertyDefinition<P> pd, P value) { 542 if (value != null) { 543 setPropertyValues(pd, Collections.singleton(value)); 544 } else { 545 setPropertyValues(pd, Collections.<P> emptySet()); 546 } 547 } 548 549 @Override 550 public final <P> void setPropertyValues(PropertyDefinition<P> pd, Collection<P> values) { 551 if (pd.hasOption(PropertyOption.MONITORING)) { 552 throw PropertyException.propertyIsReadOnlyException(pd); 553 } 554 555 if (existsOnServer && pd.hasOption(PropertyOption.READ_ONLY)) { 556 throw PropertyException.propertyIsReadOnlyException(pd); 557 } 558 559 properties.setPropertyValues(pd, values); 560 561 // If this is a naming property then update the name. 562 if (pd.equals(namingPropertyDefinition)) { 563 // The property must be single-valued and mandatory. 564 String newName = pd.encodeValue(values.iterator().next()); 565 path = path.rename(newName); 566 } 567 } 568 569 @Override 570 public String toString() { 571 StringBuilder builder = new StringBuilder(); 572 573 builder.append("{ TYPE="); 574 builder.append(definition.getName()); 575 builder.append(", PATH=\""); 576 builder.append(path); 577 builder.append('\"'); 578 for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) { 579 builder.append(", "); 580 builder.append(pd.getName()); 581 builder.append('='); 582 builder.append(getPropertyValues(pd)); 583 } 584 builder.append(" }"); 585 586 return builder.toString(); 587 } 588 589 /** 590 * Adds this new managed object. 591 * 592 * @throws ManagedObjectAlreadyExistsException 593 * If the managed object cannot be added to the server because 594 * it already exists. 595 * @throws ConcurrentModificationException 596 * If the managed object's parent has been removed by another 597 * client. 598 * @throws OperationRejectedException 599 * If the managed object cannot be added due to some client-side 600 * or server-side constraint which cannot be satisfied. 601 * @throws LdapException 602 * If any other error occurs. 603 */ 604 protected abstract void addNewManagedObject() throws LdapException, OperationRejectedException, 605 ConcurrentModificationException, ManagedObjectAlreadyExistsException; 606 607 /** 608 * Gets the management context driver associated with this managed object. 609 * 610 * @return Returns the management context driver associated with this 611 * managed object. 612 */ 613 protected abstract Driver getDriver(); 614 615 /** 616 * Gets the naming property definition associated with this managed object. 617 * 618 * @return Returns the naming property definition associated with this 619 * managed object, or <code>null</code> if this managed object does 620 * not have a naming property. 621 */ 622 protected final PropertyDefinition<?> getNamingPropertyDefinition() { 623 return namingPropertyDefinition; 624 } 625 626 /** 627 * Gets the property associated with the specified property definition. 628 * 629 * @param <P> 630 * The underlying type of the property. 631 * @param pd 632 * The Property definition. 633 * @return Returns the property associated with the specified property 634 * definition. 635 * @throws IllegalArgumentException 636 * If this property provider does not recognize the requested 637 * property definition. 638 */ 639 protected final <P> Property<P> getProperty(PropertyDefinition<P> pd) { 640 return properties.getProperty(pd); 641 } 642 643 /** 644 * Applies changes made to this managed object. 645 * 646 * @throws ConcurrentModificationException 647 * If this managed object has been removed from the server by 648 * another client. 649 * @throws OperationRejectedException 650 * If the managed object cannot be added due to some client-side 651 * or server-side constraint which cannot be satisfied. 652 * @throws LdapException 653 * If any other error occurs. 654 */ 655 protected abstract void modifyExistingManagedObject() throws ConcurrentModificationException, 656 OperationRejectedException, LdapException; 657 658 /** 659 * Creates a new managed object. 660 * 661 * @param <M> 662 * The type of client configuration represented by the client 663 * managed object. 664 * @param d 665 * The managed object's definition. 666 * @param path 667 * The managed object's path. 668 * @param properties 669 * The managed object's properties. 670 * @param existsOnServer 671 * Indicates whether the managed object exists on the server 672 * (false means the managed object is new and has not been committed). 673 * @param namingPropertyDefinition 674 * Optional naming property definition. 675 * @return Returns the new managed object. 676 */ 677 protected abstract <M extends ConfigurationClient> ManagedObject<M> newInstance(ManagedObjectDefinition<M, ?> d, 678 ManagedObjectPath<M, ?> path, PropertySet properties, boolean existsOnServer, 679 PropertyDefinition<?> namingPropertyDefinition); 680 681 /** Creates a new managed object with no active values, just default values. */ 682 private <M extends ConfigurationClient, P> ManagedObject<M> createNewManagedObject( 683 ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> p, PropertyDefinition<P> namingPropertyDefinition, 684 String name, Collection<PropertyException> exceptions) { 685 PropertySet childProperties = new PropertySet(); 686 for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) { 687 try { 688 createProperty(childProperties, p, pd); 689 } catch (PropertyException e) { 690 // Add the exception if requested. 691 if (exceptions != null) { 692 exceptions.add(e); 693 } 694 } 695 } 696 697 // Set the naming property if there is one. 698 if (namingPropertyDefinition != null) { 699 P value = namingPropertyDefinition.decodeValue(name); 700 childProperties.setPropertyValues(namingPropertyDefinition, Collections.singleton(value)); 701 } 702 703 return newInstance(d, p, childProperties, false, namingPropertyDefinition); 704 } 705 706 /** Create an empty property. */ 707 private <P> void createProperty(PropertySet properties, ManagedObjectPath<?, ?> p, PropertyDefinition<P> pd) { 708 try { 709 Driver context = getDriver(); 710 Collection<P> defaultValues = context.findDefaultValues(p, pd, true); 711 properties.addProperty(pd, defaultValues, Collections.<P> emptySet()); 712 } catch (PropertyException e) { 713 // Make sure that we have still created the property. 714 properties.addProperty(pd, Collections.<P> emptySet(), Collections.<P> emptySet()); 715 throw e; 716 } 717 } 718 719 /** Makes sure that this managed object exists. */ 720 private void ensureThisManagedObjectExists() throws ConcurrentModificationException, LdapException { 721 if (!path.isEmpty()) { 722 Driver ctx = getDriver(); 723 724 try { 725 if (!ctx.managedObjectExists(path)) { 726 throw new ConcurrentModificationException(); 727 } 728 } catch (ManagedObjectNotFoundException e) { 729 throw new ConcurrentModificationException(); 730 } 731 } 732 } 733 734 /** Validate that a relation definition belongs to this managed object. */ 735 private void validateRelationDefinition(RelationDefinition<?, ?> rd) { 736 ManagedObjectDefinition<T, ?> d = getManagedObjectDefinition(); 737 RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName()); 738 if (tmp != rd) { 739 throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a " 740 + d.getName()); 741 } 742 } 743 744}