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