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