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