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 2009 Sun Microsystems, Inc. 015 * Portions copyright 2014-2016 ForgeRock AS. 016 */ 017 018package org.forgerock.opendj.config.server; 019 020import static com.forgerock.opendj.ldap.config.ConfigMessages.*; 021import static com.forgerock.opendj.util.StaticUtils.*; 022import static org.forgerock.opendj.config.PropertyException.defaultBehaviorException; 023import static org.forgerock.opendj.config.PropertyException.propertyIsSingleValuedException; 024 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Map; 032import java.util.Set; 033import java.util.SortedSet; 034import java.util.TreeSet; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.opendj.server.config.meta.RootCfgDefn; 038import org.forgerock.opendj.server.config.server.RootCfg; 039import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider; 040import org.forgerock.opendj.config.AbstractManagedObjectDefinition; 041import org.forgerock.opendj.config.AggregationPropertyDefinition; 042import org.forgerock.opendj.config.AliasDefaultBehaviorProvider; 043import org.forgerock.opendj.config.Configuration; 044import org.forgerock.opendj.config.ConfigurationClient; 045import org.forgerock.opendj.config.PropertyException; 046import org.forgerock.opendj.config.DefaultBehaviorProviderVisitor; 047import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider; 048import org.forgerock.opendj.config.DefinitionDecodingException; 049import org.forgerock.opendj.config.DefinitionDecodingException.Reason; 050import org.forgerock.opendj.config.DefinitionResolver; 051import org.forgerock.opendj.config.LDAPProfile; 052import org.forgerock.opendj.config.ManagedObjectDefinition; 053import org.forgerock.opendj.config.ManagedObjectPath; 054import org.forgerock.opendj.config.PropertyDefinition; 055import org.forgerock.opendj.config.PropertyDefinitionVisitor; 056import org.forgerock.opendj.config.PropertyNotFoundException; 057import org.forgerock.opendj.config.PropertyOption; 058import org.forgerock.opendj.config.Reference; 059import org.forgerock.opendj.config.RelationDefinition; 060import org.forgerock.opendj.config.RelativeInheritedDefaultBehaviorProvider; 061import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider; 062import org.forgerock.opendj.config.server.spi.ConfigurationRepository; 063import org.forgerock.opendj.ldap.Attribute; 064import org.forgerock.opendj.ldap.AttributeDescription; 065import org.forgerock.opendj.ldap.ByteString; 066import org.forgerock.opendj.ldap.DN; 067import org.forgerock.opendj.ldap.Entry; 068import org.forgerock.opendj.ldap.schema.AttributeType; 069import org.forgerock.opendj.ldap.schema.Schema; 070import org.slf4j.Logger; 071import org.slf4j.LoggerFactory; 072 073/** Server management connection context. */ 074public final class ServerManagementContext { 075 076 /** 077 * A default behavior visitor used for retrieving the default values of a 078 * property. 079 * 080 * @param <T> 081 * The type of the property. 082 */ 083 private final class DefaultValueFinder<T> implements DefaultBehaviorProviderVisitor<T, Collection<T>, Void> { 084 085 /** Any exception that occurred whilst retrieving inherited default values. */ 086 private PropertyException exception; 087 088 /** Optional new configuration entry which does not yet exist in the configuration back-end. */ 089 private final Entry newConfigEntry; 090 091 /** The path of the managed object containing the next property. */ 092 private ManagedObjectPath<?, ?> nextPath; 093 094 /** The next property whose default values were required. */ 095 private PropertyDefinition<T> nextProperty; 096 097 /** Private constructor. */ 098 private DefaultValueFinder(Entry newConfigEntry) { 099 this.newConfigEntry = newConfigEntry; 100 } 101 102 @Override 103 public Collection<T> visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) { 104 try { 105 return getInheritedProperty(d.getManagedObjectPath(), d.getManagedObjectDefinition(), 106 d.getPropertyName()); 107 } catch (PropertyException e) { 108 exception = e; 109 return Collections.emptySet(); 110 } 111 } 112 113 @Override 114 public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) { 115 return Collections.emptySet(); 116 } 117 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 @Override 136 public Collection<T> visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, Void p) { 137 try { 138 return getInheritedProperty(d.getManagedObjectPath(nextPath), d.getManagedObjectDefinition(), 139 d.getPropertyName()); 140 } catch (PropertyException e) { 141 exception = e; 142 return Collections.emptySet(); 143 } 144 } 145 146 @Override 147 public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d, Void p) { 148 return Collections.emptySet(); 149 } 150 151 /** Find the default values for the next path/property. */ 152 private Collection<T> find(ManagedObjectPath<?, ?> path, PropertyDefinition<T> propertyDef) { 153 nextPath = path; 154 nextProperty = propertyDef; 155 156 Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(this, null); 157 158 if (exception != null) { 159 throw exception; 160 } 161 162 if (values.size() > 1 && !propertyDef.hasOption(PropertyOption.MULTI_VALUED)) { 163 throw defaultBehaviorException(propertyDef, propertyIsSingleValuedException(propertyDef)); 164 } 165 166 return values; 167 } 168 169 /** Get an inherited property value. */ 170 @SuppressWarnings("unchecked") 171 private Collection<T> getInheritedProperty(ManagedObjectPath<?, ?> target, 172 AbstractManagedObjectDefinition<?, ?> definition, String propertyName) { 173 // First check that the requested type of managed object corresponds to the path. 174 AbstractManagedObjectDefinition<?, ?> actual = target.getManagedObjectDefinition(); 175 if (!definition.isParentOf(actual)) { 176 throw PropertyException.defaultBehaviorException(nextProperty, new DefinitionDecodingException(actual, 177 Reason.WRONG_TYPE_INFORMATION)); 178 } 179 180 // Save the current property in case of recursion. 181 PropertyDefinition<T> propDef1 = nextProperty; 182 183 try { 184 // Get the actual managed object definition. 185 DN dn = DNBuilder.create(target); 186 Entry configEntry; 187 if (newConfigEntry != null && newConfigEntry.getName().equals(dn)) { 188 configEntry = newConfigEntry; 189 } else { 190 configEntry = getManagedObjectConfigEntry(dn); 191 } 192 193 DefinitionResolver resolver = new MyDefinitionResolver(configEntry); 194 ManagedObjectDefinition<?, ?> mod = definition.resolveManagedObjectDefinition(resolver); 195 196 PropertyDefinition<T> propDef2; 197 try { 198 PropertyDefinition<?> propDefTmp = mod.getPropertyDefinition(propertyName); 199 propDef2 = propDef1.getClass().cast(propDefTmp); 200 } catch (IllegalArgumentException | ClassCastException e) { 201 throw new PropertyNotFoundException(propertyName); 202 } 203 204 List<String> attributeValues = getAttributeValues(mod, propDef2, configEntry); 205 if (attributeValues.size() > 0) { 206 Collection<T> pvalues = new ArrayList<>(); 207 for (String value : attributeValues) { 208 pvalues.add(ValueDecoder.decode(propDef1, value)); 209 } 210 return pvalues; 211 } else { 212 // Recursively retrieve this property's default values. 213 Collection<T> tmp = find(target, propDef2); 214 Collection<T> pvalues = new ArrayList<>(tmp.size()); 215 for (T value : tmp) { 216 propDef1.validateValue(value); 217 pvalues.add(value); 218 } 219 return pvalues; 220 } 221 } catch (Exception e) { 222 throw PropertyException.defaultBehaviorException(propDef1, e); 223 } 224 } 225 } 226 227 /** 228 * A definition resolver that determines the managed object definition from 229 * the object classes of a ConfigEntry. 230 */ 231 private static final class MyDefinitionResolver implements DefinitionResolver { 232 233 /** The config entry. */ 234 private final Entry entry; 235 236 /** Private constructor. */ 237 private MyDefinitionResolver(Entry entry) { 238 this.entry = entry; 239 } 240 241 @Override 242 public boolean matches(AbstractManagedObjectDefinition<?, ?> d) { 243 String oc = LDAPProfile.getInstance().getObjectClass(d); 244 // TODO : use the schema to get object class and check it in the entry 245 // Commented because reject any config entry without proper schema loading 246 // Previous code was 247// ObjectClass oc = DirectoryServer.getObjectClass(name.toLowerCase()); 248// if (oc == null) { 249// oc = DirectoryServer.getDefaultObjectClass(name); 250// } 251// return Entries.containsObjectClass(entry, oc); 252 return entry.containsAttribute("objectClass", oc); 253 } 254 } 255 256 /** A visitor which is used to decode property LDAP values. */ 257 private static final class ValueDecoder extends PropertyDefinitionVisitor<Object, String> { 258 259 /** 260 * Decodes the provided property LDAP value. 261 * 262 * @param <P> 263 * The type of the property. 264 * @param propertyDef 265 * The property definition. 266 * @param value 267 * The LDAP string representation. 268 * @return Returns the decoded LDAP value. 269 * @throws PropertyException 270 * If the property value could not be decoded because it was 271 * invalid. 272 */ 273 public static <P> P decode(PropertyDefinition<P> propertyDef, String value) { 274 return propertyDef.castValue(propertyDef.accept(new ValueDecoder(), value)); 275 } 276 277 /** Prevent instantiation. */ 278 private ValueDecoder() { 279 // Do nothing. 280 } 281 282 @Override 283 public <C extends ConfigurationClient, S extends Configuration> Object visitAggregation( 284 AggregationPropertyDefinition<C, S> d, String p) { 285 // Aggregations values are stored as full DNs in LDAP, but 286 // just their common name is exposed in the admin framework. 287 try { 288 Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d.getRelationDefinition(), p); 289 return reference.getName(); 290 } catch (IllegalArgumentException e) { 291 throw PropertyException.illegalPropertyValueException(d, p); 292 } 293 } 294 295 @Override 296 public <T> Object visitUnknown(PropertyDefinition<T> d, String p) { 297 // By default the property definition's decoder will do. 298 return d.decodeValue(p); 299 } 300 } 301 302 private static final Logger debugLogger = LoggerFactory.getLogger(ServerManagementContext.class); 303 304 /** The root server managed object, lazily initialized. */ 305 private volatile ServerManagedObject<RootCfg> root; 306 307 /** Repository of configuration entries. */ 308 private final ConfigurationRepository configRepository; 309 310 /** 311 * Creates a context from the provided configuration repository. 312 * 313 * @param repository 314 * The repository of configuration entries. 315 */ 316 public ServerManagementContext(ConfigurationRepository repository) { 317 configRepository = repository; 318 } 319 320 /** 321 * Gets the named managed object. 322 * 323 * @param <C> 324 * The type of client managed object configuration that the path 325 * definition refers to. 326 * @param <S> 327 * The type of server managed object configuration that the path 328 * definition refers to. 329 * @param path 330 * The path of the managed object. 331 * @return Returns the named managed object. 332 * @throws ConfigException 333 * If the named managed object could not be found or if it could 334 * not be decoded. 335 */ 336 @SuppressWarnings("unchecked") 337 public <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> getManagedObject( 338 ManagedObjectPath<C, S> path) throws ConfigException { 339 // Be careful to handle the root configuration. 340 if (path.isEmpty()) { 341 return (ServerManagedObject<S>) getRootConfigurationManagedObject(); 342 } 343 344 // Get the configuration entry. 345 DN targetDN = DNBuilder.create(path); 346 Entry configEntry = getManagedObjectConfigEntry(targetDN); 347 try { 348 ServerManagedObject<? extends S> managedObject; 349 managedObject = decode(path, configEntry); 350 351 // Enforce any constraints. 352 managedObject.ensureIsUsable(); 353 354 return managedObject; 355 } catch (DefinitionDecodingException e) { 356 throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(targetDN, e); 357 } catch (ServerManagedObjectDecodingException e) { 358 throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e); 359 } catch (ConstraintViolationException e) { 360 throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e); 361 } 362 } 363 364 /** 365 * Gets the effective value of a property in the named managed object. 366 * 367 * @param <C> 368 * The type of client managed object configuration that the path 369 * definition refers to. 370 * @param <S> 371 * The type of server managed object configuration that the path 372 * definition refers to. 373 * @param <P> 374 * The type of the property to be retrieved. 375 * @param path 376 * The path of the managed object containing the property. 377 * @param pd 378 * The property to be retrieved. 379 * @return Returns the property's effective value, or <code>null</code> if 380 * there are no values defined. 381 * @throws IllegalArgumentException 382 * If the property definition is not associated with the 383 * referenced managed object's definition. 384 * @throws PropertyException 385 * If the managed object was found but the requested property 386 * could not be decoded. 387 * @throws ConfigException 388 * If the named managed object could not be found or if it could 389 * not be decoded. 390 */ 391 public <C extends ConfigurationClient, S extends Configuration, P> P getPropertyValue( 392 ManagedObjectPath<C, S> path, PropertyDefinition<P> pd) throws ConfigException { 393 SortedSet<P> values = getPropertyValues(path, pd); 394 if (!values.isEmpty()) { 395 return values.first(); 396 } 397 return null; 398 } 399 400 /** 401 * Gets the effective values of a property in the named managed object. 402 * 403 * @param <C> 404 * The type of client managed object configuration that the path 405 * definition refers to. 406 * @param <S> 407 * The type of server managed object configuration that the path 408 * definition refers to. 409 * @param <P> 410 * The type of the property to be retrieved. 411 * @param path 412 * The path of the managed object containing the property. 413 * @param propertyDef 414 * The property to be retrieved. 415 * @return Returns the property's effective values, or an empty set if there 416 * are no values defined. 417 * @throws IllegalArgumentException 418 * If the property definition is not associated with the 419 * referenced managed object's definition. 420 * @throws PropertyException 421 * If the managed object was found but the requested property 422 * could not be decoded. 423 * @throws ConfigException 424 * If the named managed object could not be found or if it could 425 * not be decoded. 426 */ 427 @SuppressWarnings("unchecked") 428 public <C extends ConfigurationClient, S extends Configuration, P> SortedSet<P> getPropertyValues( 429 ManagedObjectPath<C, S> path, PropertyDefinition<P> propertyDef) throws ConfigException { 430 // Check that the requested property is from the definition 431 // associated with the path. 432 AbstractManagedObjectDefinition<C, S> definition = path.getManagedObjectDefinition(); 433 PropertyDefinition<?> tmpPropertyDef = definition.getPropertyDefinition(propertyDef.getName()); 434 if (tmpPropertyDef != propertyDef) { 435 throw new IllegalArgumentException("The property " + propertyDef.getName() + " is not associated with a " 436 + definition.getName()); 437 } 438 439 // Determine the exact type of managed object referenced by the path. 440 DN dn = DNBuilder.create(path); 441 Entry configEntry = getManagedObjectConfigEntry(dn); 442 443 DefinitionResolver resolver = new MyDefinitionResolver(configEntry); 444 ManagedObjectDefinition<? extends C, ? extends S> managedObjDef; 445 446 try { 447 managedObjDef = definition.resolveManagedObjectDefinition(resolver); 448 } catch (DefinitionDecodingException e) { 449 throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(dn, e); 450 } 451 452 // Make sure we use the correct property definition, the 453 // provided one might have been overridden in the resolved definition. 454 propertyDef = (PropertyDefinition<P>) managedObjDef.getPropertyDefinition(propertyDef.getName()); 455 456 List<String> attributeValues = getAttributeValues(managedObjDef, propertyDef, configEntry); 457 return decodeProperty(path.asSubType(managedObjDef), propertyDef, attributeValues, null); 458 } 459 460 /** 461 * Get the root configuration manager associated with this management 462 * context. 463 * 464 * @return the root configuration manager associated with this 465 * management context. 466 */ 467 public RootCfg getRootConfiguration() { 468 return getRootConfigurationManagedObject().getConfiguration(); 469 } 470 471 /** 472 * Get the root configuration server managed object associated with this 473 * management context. 474 * 475 * @return the root configuration server managed object 476 */ 477 public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() { 478 // Use lazy initialisation 479 // because it needs a reference to this server context. 480 ServerManagedObject<RootCfg> rootObject = root; 481 if (rootObject == null) { 482 synchronized (this) { 483 rootObject = root; 484 if (rootObject == null) { 485 root = rootObject = 486 new ServerManagedObject<>(ManagedObjectPath.emptyPath(), 487 RootCfgDefn.getInstance(), Collections.<PropertyDefinition<?>, 488 SortedSet<?>> emptyMap(), null, this); 489 } 490 } 491 } 492 return rootObject; 493 } 494 495 /** 496 * Lists the child managed objects of the named parent managed object. 497 * 498 * @param <C> 499 * The type of client managed object configuration that the 500 * relation definition refers to. 501 * @param <S> 502 * The type of server managed object configuration that the 503 * relation definition refers to. 504 * @param parent 505 * The path of the parent managed object. 506 * @param relationDef 507 * The relation definition. 508 * @return Returns the names of the child managed objects. 509 * @throws IllegalArgumentException 510 * If the relation definition is not associated with the parent 511 * managed object's definition. 512 */ 513 public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects( 514 ManagedObjectPath<?, ?> parent, RelationDefinition<C, S> relationDef) { 515 validateRelationDefinition(parent, relationDef); 516 517 // Get the target entry. 518 DN targetDN = DNBuilder.create(parent, relationDef); 519 Set<DN> children; 520 try { 521 children = configRepository.getChildren(targetDN); 522 } catch (ConfigException e) { 523 return new String[0]; 524 } 525 List<String> names = new ArrayList<>(children.size()); 526 for (DN child : children) { 527 // Assume that RDNs are single-valued and can be trimmed. 528 String name = child.rdn().getFirstAVA().getAttributeValue().toString().trim(); 529 names.add(name); 530 } 531 532 return names.toArray(new String[names.size()]); 533 } 534 535 /** 536 * Determines whether the named managed object exists. 537 * 538 * @param path 539 * The path of the named managed object. 540 * @return Returns <code>true</code> if the named managed object exists, 541 * <code>false</code> otherwise. 542 */ 543 public boolean managedObjectExists(ManagedObjectPath<?, ?> path) { 544 // Get the configuration entry. 545 DN targetDN = DNBuilder.create(path); 546 try { 547 return configRepository.getEntry(targetDN) != null; 548 } catch (ConfigException e) { 549 // Assume it doesn't exist. 550 return false; 551 } 552 } 553 554 /** 555 * Decodes a configuration entry into the required type of server managed 556 * object. 557 * 558 * @param <C> 559 * The type of client managed object configuration that the path 560 * definition refers to. 561 * @param <S> 562 * The type of server managed object configuration that the path 563 * definition refers to. 564 * @param path 565 * The location of the server managed object. 566 * @param configEntry 567 * The configuration entry that should be decoded. 568 * @return Returns the new server-side managed object from the provided 569 * definition and configuration entry. 570 * @throws DefinitionDecodingException 571 * If the managed object's type could not be determined. 572 * @throws ServerManagedObjectDecodingException 573 * If one or more of the managed object's properties could not 574 * be decoded. 575 */ 576 <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode( 577 ManagedObjectPath<C, S> path, Entry configEntry) throws DefinitionDecodingException, 578 ServerManagedObjectDecodingException { 579 return decode(path, configEntry, null); 580 } 581 582 /** 583 * Decodes a configuration entry into the required type of server managed 584 * object. 585 * 586 * @param <C> 587 * The type of client managed object configuration that the path 588 * definition refers to. 589 * @param <S> 590 * The type of server managed object configuration that the path 591 * definition refers to. 592 * @param path 593 * The location of the server managed object. 594 * @param configEntry 595 * The configuration entry that should be decoded. 596 * @param newConfigEntry 597 * Optional new configuration that does not exist yet in the 598 * configuration back-end. This will be used for resolving 599 * inherited default values. 600 * @return Returns the new server-side managed object from the provided 601 * definition and configuration entry. 602 * @throws DefinitionDecodingException 603 * If the managed object's type could not be determined. 604 * @throws ServerManagedObjectDecodingException 605 * If one or more of the managed object's properties could not 606 * be decoded. 607 */ 608 <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode( 609 ManagedObjectPath<C, S> path, Entry configEntry, Entry newConfigEntry) 610 throws DefinitionDecodingException, ServerManagedObjectDecodingException { 611 // First determine the correct definition to use for the entry. 612 // This could either be the provided definition, or one of its 613 // sub-definitions. 614 DefinitionResolver resolver = new MyDefinitionResolver(configEntry); 615 AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition(); 616 ManagedObjectDefinition<? extends C, ? extends S> mod = d.resolveManagedObjectDefinition(resolver); 617 618 // Build the managed object's properties. 619 List<PropertyException> exceptions = new LinkedList<>(); 620 Map<PropertyDefinition<?>, SortedSet<?>> properties = new HashMap<>(); 621 for (PropertyDefinition<?> propertyDef : mod.getAllPropertyDefinitions()) { 622 List<String> attributeValues = getAttributeValues(mod, propertyDef, configEntry); 623 try { 624 SortedSet<?> pvalues = decodeProperty(path, propertyDef, attributeValues, newConfigEntry); 625 properties.put(propertyDef, pvalues); 626 } catch (PropertyException e) { 627 exceptions.add(e); 628 } 629 } 630 631 // If there were no decoding problems then return the managed 632 // object, otherwise throw an operations exception. 633 ServerManagedObject<? extends S> managedObject = decodeAux(path, mod, properties, configEntry.getName()); 634 if (exceptions.isEmpty()) { 635 return managedObject; 636 } else { 637 throw new ServerManagedObjectDecodingException(managedObject, exceptions); 638 } 639 } 640 641 /** Decode helper method required to avoid generics warning. */ 642 private <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<S> decodeAux( 643 ManagedObjectPath<? super C, ? super S> path, ManagedObjectDefinition<C, S> d, 644 Map<PropertyDefinition<?>, SortedSet<?>> properties, DN configDN) { 645 ManagedObjectPath<C, S> newPath = path.asSubType(d); 646 return new ServerManagedObject<>(newPath, d, properties, configDN, this); 647 } 648 649 /** Decode a property using the provided attribute values. */ 650 private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path, PropertyDefinition<T> propertyDef, 651 List<String> attributeValues, Entry newConfigEntry) { 652 PropertyException exception = null; 653 SortedSet<T> pvalues = new TreeSet<>(propertyDef); 654 655 if (attributeValues.size() > 0) { 656 // The property has values defined for it. 657 for (String value : attributeValues) { 658 try { 659 pvalues.add(ValueDecoder.decode(propertyDef, value)); 660 } catch (PropertyException e) { 661 exception = e; 662 } 663 } 664 } else { 665 // No values defined so get the defaults. 666 try { 667 pvalues.addAll(getDefaultValues(path, propertyDef, newConfigEntry)); 668 } catch (PropertyException e) { 669 exception = e; 670 } 671 } 672 673 if (pvalues.size() > 1 && !propertyDef.hasOption(PropertyOption.MULTI_VALUED)) { 674 // This exception takes precedence over previous exceptions. 675 exception = PropertyException.propertyIsSingleValuedException(propertyDef); 676 T value = pvalues.first(); 677 pvalues.clear(); 678 pvalues.add(value); 679 } 680 681 if (pvalues.isEmpty() && propertyDef.hasOption(PropertyOption.MANDATORY) && exception == null) { 682 exception = PropertyException.propertyIsMandatoryException(propertyDef); 683 } 684 685 if (exception != null) { 686 throw exception; 687 } 688 return pvalues; 689 } 690 691 /** Gets the attribute values associated with a property from a ConfigEntry. */ 692 private List<String> getAttributeValues(ManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd, 693 Entry configEntry) { 694 // TODO: we create a default attribute type if it is undefined. 695 // We should log a warning here if this is the case 696 // since the attribute should have been defined. 697 String attrID = LDAPProfile.getInstance().getAttributeName(d, pd); 698 AttributeType type = Schema.getDefaultSchema().getAttributeType(attrID); 699 Iterable<Attribute> attributes = configEntry.getAllAttributes(AttributeDescription.create(type)); 700 List<String> values = new ArrayList<>(); 701 for (Attribute attribute : attributes) { 702 for (ByteString byteValue : attribute) { 703 values.add(byteValue.toString()); 704 } 705 } 706 return values; 707 } 708 709 /** Get the default values for the specified property. */ 710 private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p, PropertyDefinition<T> pd, 711 Entry newConfigEntry) { 712 DefaultValueFinder<T> v = new DefaultValueFinder<>(newConfigEntry); 713 return v.find(p, pd); 714 } 715 716 /** 717 * Retrieves a configuration entry corresponding to the provided DN. 718 * 719 * @param dn 720 * DN of the configuration entry. 721 * @return the configuration entry 722 * @throws ConfigException 723 * If a problem occurs. 724 */ 725 public Entry getConfigEntry(DN dn) throws ConfigException { 726 return configRepository.getEntry(dn); 727 } 728 729 /** 730 * Returns the repository containing all configuration entries. 731 * 732 * @return the repository 733 */ 734 public ConfigurationRepository getConfigRepository() { 735 return configRepository; 736 } 737 738 /** Gets a config entry required for a managed object and throws a config exception on failure. */ 739 private Entry getManagedObjectConfigEntry(DN dn) throws ConfigException { 740 Entry configEntry; 741 try { 742 configEntry = configRepository.getEntry(dn); 743 } catch (ConfigException e) { 744 debugLogger.trace("Unable to perform post add", e); 745 746 LocalizableMessage message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get( 747 dn, stackTraceToSingleLineString(e, true)); 748 throw new ConfigException(message, e); 749 } 750 751 // The configuration handler is free to return null indicating 752 // that the entry does not exist. 753 if (configEntry == null) { 754 throw new ConfigException(ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST.get(dn)); 755 } 756 757 return configEntry; 758 } 759 760 /** Validate that a relation definition belongs to the path. */ 761 private void validateRelationDefinition(ManagedObjectPath<?, ?> path, RelationDefinition<?, ?> rd) { 762 AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition(); 763 RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName()); 764 if (tmp != rd) { 765 throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a " 766 + d.getName()); 767 } 768 } 769}