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 */ 017package org.opends.server.admin.server; 018 019import static org.opends.messages.AdminMessages.*; 020import static org.opends.server.admin.PropertyException.*; 021import static org.opends.server.util.StaticUtils.*; 022 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.LinkedList; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031import java.util.SortedSet; 032import java.util.TreeSet; 033 034import org.forgerock.i18n.LocalizableMessage; 035import org.forgerock.i18n.slf4j.LocalizedLogger; 036import org.forgerock.opendj.config.server.ConfigException; 037import org.forgerock.opendj.ldap.ByteString; 038import org.forgerock.opendj.ldap.DN; 039import org.forgerock.opendj.ldap.schema.AttributeType; 040import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider; 041import org.opends.server.admin.AbstractManagedObjectDefinition; 042import org.opends.server.admin.AggregationPropertyDefinition; 043import org.opends.server.admin.AliasDefaultBehaviorProvider; 044import org.opends.server.admin.Configuration; 045import org.opends.server.admin.ConfigurationClient; 046import org.opends.server.admin.DefaultBehaviorProviderVisitor; 047import org.opends.server.admin.DefinedDefaultBehaviorProvider; 048import org.opends.server.admin.DefinitionDecodingException; 049import org.opends.server.admin.DefinitionDecodingException.Reason; 050import org.opends.server.admin.DefinitionResolver; 051import org.opends.server.admin.InstantiableRelationDefinition; 052import org.opends.server.admin.LDAPProfile; 053import org.opends.server.admin.ManagedObjectDefinition; 054import org.opends.server.admin.ManagedObjectPath; 055import org.opends.server.admin.PropertyDefinition; 056import org.opends.server.admin.PropertyDefinitionVisitor; 057import org.opends.server.admin.PropertyException; 058import org.opends.server.admin.PropertyNotFoundException; 059import org.opends.server.admin.PropertyOption; 060import org.opends.server.admin.Reference; 061import org.opends.server.admin.RelationDefinition; 062import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider; 063import org.opends.server.admin.SetRelationDefinition; 064import org.opends.server.admin.UndefinedDefaultBehaviorProvider; 065import org.opends.server.admin.std.meta.RootCfgDefn; 066import org.opends.server.admin.std.server.RootCfg; 067import org.opends.server.config.ConfigEntry; 068import org.opends.server.core.DirectoryServer; 069import org.opends.server.types.Attribute; 070 071/** 072 * Server management connection context. 073 */ 074public final class ServerManagementContext { 075 076 /** 077 * A default behavior visitor used for retrieving the default values 078 * of a property. 079 * 080 * @param <T> 081 * The type of the property. 082 */ 083 private class DefaultValueFinder<T> implements 084 DefaultBehaviorProviderVisitor<T, Collection<T>, Void> { 085 086 /** 087 * Any exception that occurred whilst retrieving inherited default values. 088 */ 089 private PropertyException exception; 090 091 /** 092 * Optional new configuration entry which does not yet exist in 093 * the configuration back-end. 094 */ 095 private final ConfigEntry newConfigEntry; 096 097 /** The path of the managed object containing the next property. */ 098 private ManagedObjectPath<?, ?> nextPath; 099 100 /** The next property whose default values were required. */ 101 private PropertyDefinition<T> nextProperty; 102 103 104 105 /** Private constructor. */ 106 private DefaultValueFinder(ConfigEntry newConfigEntry) { 107 this.newConfigEntry = newConfigEntry; 108 } 109 110 111 112 /** {@inheritDoc} */ 113 @Override 114 public Collection<T> visitAbsoluteInherited( 115 AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) { 116 try { 117 return getInheritedProperty(d.getManagedObjectPath(), d 118 .getManagedObjectDefinition(), d.getPropertyName()); 119 } catch (PropertyException e) { 120 exception = e; 121 return Collections.emptySet(); 122 } 123 } 124 125 126 127 /** {@inheritDoc} */ 128 @Override 129 public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) { 130 return Collections.emptySet(); 131 } 132 133 134 135 /** {@inheritDoc} */ 136 @Override 137 public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d, 138 Void p) { 139 Collection<String> stringValues = d.getDefaultValues(); 140 List<T> values = new ArrayList<>(stringValues.size()); 141 142 for (String stringValue : stringValues) { 143 try { 144 values.add(nextProperty.decodeValue(stringValue)); 145 } catch (PropertyException e) { 146 exception = defaultBehaviorException(nextProperty, e); 147 break; 148 } 149 } 150 151 return values; 152 } 153 154 155 156 /** {@inheritDoc} */ 157 @Override 158 public Collection<T> visitRelativeInherited( 159 RelativeInheritedDefaultBehaviorProvider<T> d, Void p) { 160 try { 161 return getInheritedProperty(d.getManagedObjectPath(nextPath), d 162 .getManagedObjectDefinition(), d.getPropertyName()); 163 } catch (PropertyException e) { 164 exception = e; 165 return Collections.emptySet(); 166 } 167 } 168 169 170 171 /** {@inheritDoc} */ 172 @Override 173 public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d, 174 Void p) { 175 return Collections.emptySet(); 176 } 177 178 179 180 /** Find the default values for the next path/property. */ 181 private Collection<T> find(ManagedObjectPath<?, ?> p, 182 PropertyDefinition<T> pd) throws PropertyException { 183 nextPath = p; 184 nextProperty = pd; 185 186 Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept( 187 this, null); 188 189 if (exception != null) { 190 throw exception; 191 } 192 193 if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) { 194 throw PropertyException.defaultBehaviorException(pd, 195 PropertyException.propertyIsSingleValuedException(pd)); 196 } 197 198 return values; 199 } 200 201 202 203 /** Get an inherited property value. */ 204 @SuppressWarnings("unchecked") 205 private Collection<T> getInheritedProperty(ManagedObjectPath target, 206 AbstractManagedObjectDefinition<?, ?> d, String propertyName) 207 throws PropertyException { 208 // First check that the requested type of managed object 209 // corresponds to the path. 210 AbstractManagedObjectDefinition<?, ?> actual = target 211 .getManagedObjectDefinition(); 212 if (!d.isParentOf(actual)) { 213 throw PropertyException.defaultBehaviorException( 214 nextProperty, new DefinitionDecodingException(actual, 215 Reason.WRONG_TYPE_INFORMATION)); 216 } 217 218 // Save the current property in case of recursion. 219 PropertyDefinition<T> pd1 = nextProperty; 220 221 try { 222 // Get the actual managed object definition. 223 DN dn = DNBuilder.create(target); 224 ConfigEntry configEntry; 225 if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) { 226 configEntry = newConfigEntry; 227 } else { 228 configEntry = getManagedObjectConfigEntry(dn); 229 } 230 231 DefinitionResolver resolver = new MyDefinitionResolver(configEntry); 232 ManagedObjectDefinition<?, ?> mod = d 233 .resolveManagedObjectDefinition(resolver); 234 235 PropertyDefinition<T> pd2; 236 try { 237 PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName); 238 pd2 = pd1.getClass().cast(pdTmp); 239 } catch (IllegalArgumentException e) { 240 throw new PropertyNotFoundException(propertyName); 241 } catch (ClassCastException e) { 242 // FIXME: would be nice to throw a better exception here. 243 throw new PropertyNotFoundException(propertyName); 244 } 245 246 List<ByteString> values = getAttribute(mod, pd2, configEntry); 247 if (values.isEmpty()) { 248 // Recursively retrieve this property's default values. 249 Collection<T> tmp = find(target, pd2); 250 Collection<T> pvalues = new ArrayList<>(tmp.size()); 251 for (T value : tmp) { 252 pd1.validateValue(value); 253 pvalues.add(value); 254 } 255 return pvalues; 256 } else { 257 Collection<T> pvalues = new ArrayList<>(values.size()); 258 for (ByteString value : values) { 259 pvalues.add(ValueDecoder.decode(pd1, value)); 260 } 261 return pvalues; 262 } 263 } catch (DefinitionDecodingException | PropertyNotFoundException | PropertyException | ConfigException e) { 264 throw PropertyException.defaultBehaviorException(pd1, e); 265 } 266 } 267 } 268 269 270 271 /** 272 * A definition resolver that determines the managed object 273 * definition from the object classes of a ConfigEntry. 274 */ 275 private class MyDefinitionResolver implements DefinitionResolver { 276 277 /** The config entry. */ 278 private final ConfigEntry entry; 279 280 281 282 /** Private constructor. */ 283 private MyDefinitionResolver(ConfigEntry entry) { 284 this.entry = entry; 285 } 286 287 288 289 /** {@inheritDoc} */ 290 @Override 291 public boolean matches(AbstractManagedObjectDefinition<?, ?> d) { 292 String oc = LDAPProfile.getInstance().getObjectClass(d); 293 return entry.hasObjectClass(oc); 294 } 295 } 296 297 298 299 /** 300 * A visitor which is used to decode property LDAP values. 301 */ 302 private static final class ValueDecoder extends 303 PropertyDefinitionVisitor<Object, String> { 304 305 /** 306 * Decodes the provided property LDAP value. 307 * 308 * @param <PD> 309 * The type of the property. 310 * @param pd 311 * The property definition. 312 * @param value 313 * The LDAP string representation. 314 * @return Returns the decoded LDAP value. 315 * @throws PropertyException 316 * If the property value could not be decoded because it 317 * was invalid. 318 */ 319 public static <PD> PD decode(PropertyDefinition<PD> pd, 320 ByteString value) throws PropertyException { 321 return pd.castValue(pd.accept(new ValueDecoder(), value.toString())); 322 } 323 324 325 326 /** Prevent instantiation. */ 327 private ValueDecoder() { 328 // No implementation required. 329 } 330 331 332 333 /** {@inheritDoc} */ 334 @Override 335 public <C extends ConfigurationClient, S extends Configuration> 336 Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) { 337 // Aggregations values are stored as full DNs in LDAP, but 338 // just their common name is exposed in the admin framework. 339 try { 340 Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d 341 .getRelationDefinition(), p); 342 return reference.getName(); 343 } catch (IllegalArgumentException e) { 344 throw PropertyException.illegalPropertyValueException(d, p); 345 } 346 } 347 348 349 350 /** {@inheritDoc} */ 351 @Override 352 public <T> Object visitUnknown(PropertyDefinition<T> d, String p) 353 throws PropertyException { 354 // By default the property definition's decoder will do. 355 return d.decodeValue(p); 356 } 357 } 358 359 360 361 /** Singleton instance. */ 362 private static final ServerManagementContext INSTANCE = new ServerManagementContext(); 363 364 /** The root server managed object. */ 365 private static final ServerManagedObject<RootCfg> ROOT = new ServerManagedObject<>( 366 ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections 367 .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null); 368 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 369 370 371 372 /** 373 * Get the single server-side management context. 374 * 375 * @return Returns the single server-side management context. 376 */ 377 public static ServerManagementContext getInstance() { 378 return INSTANCE; 379 } 380 381 382 383 /** Private constructor. */ 384 private ServerManagementContext() { 385 // No implementation required. 386 } 387 388 389 390 /** 391 * Gets the named managed object. 392 * 393 * @param <C> 394 * The type of client managed object configuration that the 395 * path definition refers to. 396 * @param <S> 397 * The type of server managed object configuration that the 398 * path definition refers to. 399 * @param path 400 * The path of the managed object. 401 * @return Returns the named managed object. 402 * @throws ConfigException 403 * If the named managed object could not be found or if it 404 * could not be decoded. 405 */ 406 @SuppressWarnings("unchecked") 407 public <C extends ConfigurationClient, S extends Configuration> 408 ServerManagedObject<? extends S> getManagedObject( 409 ManagedObjectPath<C, S> path) throws ConfigException { 410 // Be careful to handle the root configuration. 411 if (path.isEmpty()) { 412 return (ServerManagedObject<S>) getRootConfigurationManagedObject(); 413 } 414 415 // Get the configuration entry. 416 DN targetDN = DNBuilder.create(path); 417 ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN); 418 try { 419 ServerManagedObject<? extends S> managedObject; 420 managedObject = decode(path, configEntry); 421 422 // Enforce any constraints. 423 managedObject.ensureIsUsable(); 424 425 return managedObject; 426 } catch (DefinitionDecodingException e) { 427 throw ConfigExceptionFactory.getInstance() 428 .createDecodingExceptionAdaptor(targetDN, e); 429 } catch (ServerManagedObjectDecodingException e) { 430 throw ConfigExceptionFactory.getInstance() 431 .createDecodingExceptionAdaptor(e); 432 } catch (ConstraintViolationException e) { 433 throw ConfigExceptionFactory.getInstance() 434 .createDecodingExceptionAdaptor(e); 435 } 436 } 437 438 439 440 /** 441 * Gets the effective value of a property in the named managed 442 * object. 443 * 444 * @param <C> 445 * The type of client managed object configuration that the 446 * path definition refers to. 447 * @param <S> 448 * The type of server managed object configuration that the 449 * path definition refers to. 450 * @param <PD> 451 * The type of the property to be retrieved. 452 * @param path 453 * The path of the managed object containing the property. 454 * @param pd 455 * The property to be retrieved. 456 * @return Returns the property's effective value, or 457 * <code>null</code> if there are no values defined. 458 * @throws IllegalArgumentException 459 * If the property definition is not associated with the 460 * referenced managed object's definition. 461 * @throws PropertyException 462 * If the managed object was found but the requested 463 * property could not be decoded. 464 * @throws ConfigException 465 * If the named managed object could not be found or if it 466 * could not be decoded. 467 */ 468 public <C extends ConfigurationClient, S extends Configuration, PD> 469 PD getPropertyValue(ManagedObjectPath<C, S> path, 470 PropertyDefinition<PD> pd) throws IllegalArgumentException, 471 ConfigException, PropertyException { 472 SortedSet<PD> values = getPropertyValues(path, pd); 473 if (values.isEmpty()) { 474 return null; 475 } else { 476 return values.first(); 477 } 478 } 479 480 481 482 /** 483 * Gets the effective values of a property in the named managed 484 * object. 485 * 486 * @param <C> 487 * The type of client managed object configuration that the 488 * path definition refers to. 489 * @param <S> 490 * The type of server managed object configuration that the 491 * path definition refers to. 492 * @param <PD> 493 * The type of the property to be retrieved. 494 * @param path 495 * The path of the managed object containing the property. 496 * @param pd 497 * The property to be retrieved. 498 * @return Returns the property's effective values, or an empty set 499 * if there are no values defined. 500 * @throws IllegalArgumentException 501 * If the property definition is not associated with the 502 * referenced managed object's definition. 503 * @throws PropertyException 504 * If the managed object was found but the requested 505 * property could not be decoded. 506 * @throws ConfigException 507 * If the named managed object could not be found or if it 508 * could not be decoded. 509 */ 510 @SuppressWarnings("unchecked") 511 public <C extends ConfigurationClient, S extends Configuration, PD> 512 SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path, 513 PropertyDefinition<PD> pd) throws IllegalArgumentException, 514 ConfigException, PropertyException { 515 // Check that the requested property is from the definition 516 // associated with the path. 517 AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition(); 518 PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName()); 519 if (tmp != pd) { 520 throw new IllegalArgumentException("The property " + pd.getName() 521 + " is not associated with a " + d.getName()); 522 } 523 524 // Determine the exact type of managed object referenced by the 525 // path. 526 DN dn = DNBuilder.create(path); 527 ConfigEntry configEntry = getManagedObjectConfigEntry(dn); 528 529 DefinitionResolver resolver = new MyDefinitionResolver(configEntry); 530 ManagedObjectDefinition<? extends C, ? extends S> mod; 531 532 try { 533 mod = d.resolveManagedObjectDefinition(resolver); 534 } catch (DefinitionDecodingException e) { 535 throw ConfigExceptionFactory.getInstance() 536 .createDecodingExceptionAdaptor(dn, e); 537 } 538 539 // Make sure we use the correct property definition, the 540 // provided one might have been overridden in the resolved 541 // definition. 542 pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName()); 543 544 List<ByteString> values = getAttribute(mod, pd, configEntry); 545 return decodeProperty(path.asSubType(mod), pd, values, null); 546 } 547 548 549 550 /** 551 * Get the root configuration manager associated with this 552 * management context. 553 * 554 * @return Returns the root configuration manager associated with 555 * this management context. 556 */ 557 public RootCfg getRootConfiguration() { 558 return getRootConfigurationManagedObject().getConfiguration(); 559 } 560 561 562 563 /** 564 * Get the root configuration server managed object associated with 565 * this management context. 566 * 567 * @return Returns the root configuration server managed object 568 * associated with this management context. 569 */ 570 public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() { 571 return ROOT; 572 } 573 574 575 576 /** 577 * Lists the child managed objects of the named parent managed 578 * object. 579 * 580 * @param <C> 581 * The type of client managed object configuration that the 582 * relation definition refers to. 583 * @param <S> 584 * The type of server managed object configuration that the 585 * relation definition refers to. 586 * @param parent 587 * The path of the parent managed object. 588 * @param rd 589 * The instantiable relation definition. 590 * @return Returns the names of the child managed objects. 591 * @throws IllegalArgumentException 592 * If the relation definition is not associated with the 593 * parent managed object's definition. 594 */ 595 public <C extends ConfigurationClient, S extends Configuration> 596 String[] listManagedObjects( 597 ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd) 598 throws IllegalArgumentException { 599 validateRelationDefinition(parent, rd); 600 601 // Get the target entry. 602 DN targetDN = DNBuilder.create(parent, rd); 603 ConfigEntry configEntry; 604 try { 605 configEntry = DirectoryServer.getConfigEntry(targetDN); 606 } catch (ConfigException e) { 607 return new String[0]; 608 } 609 610 if (configEntry == null) { 611 return new String[0]; 612 } 613 614 // Retrieve the children. 615 Set<DN> children = configEntry.getChildren().keySet(); 616 ArrayList<String> names = new ArrayList<>(children.size()); 617 for (DN child : children) { 618 // Assume that RDNs are single-valued and can be trimmed. 619 ByteString av = child.rdn().getFirstAVA().getAttributeValue(); 620 names.add(av.toString().trim()); 621 } 622 623 return names.toArray(new String[names.size()]); 624 } 625 626 627 628 /** 629 * Lists the child managed objects of the named parent managed 630 * object. 631 * 632 * @param <C> 633 * The type of client managed object configuration that the 634 * relation definition refers to. 635 * @param <S> 636 * The type of server managed object configuration that the 637 * relation definition refers to. 638 * @param parent 639 * The path of the parent managed object. 640 * @param rd 641 * The set relation definition. 642 * @return Returns the names of the child managed objects. 643 * @throws IllegalArgumentException 644 * If the relation definition is not associated with the 645 * parent managed object's definition. 646 */ 647 public <C extends ConfigurationClient, S extends Configuration> 648 String[] listManagedObjects( 649 ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd) 650 throws IllegalArgumentException { 651 validateRelationDefinition(parent, rd); 652 653 // Get the target entry. 654 DN targetDN = DNBuilder.create(parent, rd); 655 ConfigEntry configEntry; 656 try { 657 configEntry = DirectoryServer.getConfigEntry(targetDN); 658 } catch (ConfigException e) { 659 return new String[0]; 660 } 661 662 if (configEntry == null) { 663 return new String[0]; 664 } 665 666 // Retrieve the children. 667 Set<DN> children = configEntry.getChildren().keySet(); 668 ArrayList<String> names = new ArrayList<>(children.size()); 669 for (DN child : children) { 670 // Assume that RDNs are single-valued and can be trimmed. 671 ByteString av = child.rdn().getFirstAVA().getAttributeValue(); 672 names.add(av.toString().trim()); 673 } 674 675 return names.toArray(new String[names.size()]); 676 } 677 678 679 680 /** 681 * Determines whether or not the named managed object exists. 682 * 683 * @param path 684 * The path of the named managed object. 685 * @return Returns <code>true</code> if the named managed object 686 * exists, <code>false</code> otherwise. 687 */ 688 public boolean managedObjectExists(ManagedObjectPath<?, ?> path) { 689 // Get the configuration entry. 690 DN targetDN = DNBuilder.create(path); 691 try { 692 return getManagedObjectConfigEntry(targetDN) != null; 693 } catch (ConfigException e) { 694 // Assume it doesn't exist. 695 return false; 696 } 697 } 698 699 700 701 /** 702 * Decodes a configuration entry into the required type of server 703 * managed object. 704 * 705 * @param <C> 706 * The type of client managed object configuration that the 707 * path definition refers to. 708 * @param <S> 709 * The type of server managed object configuration that the 710 * path definition refers to. 711 * @param path 712 * The location of the server managed object. 713 * @param configEntry 714 * The configuration entry that should be decoded. 715 * @return Returns the new server-side managed object from the 716 * provided definition and configuration entry. 717 * @throws DefinitionDecodingException 718 * If the managed object's type could not be determined. 719 * @throws ServerManagedObjectDecodingException 720 * If one or more of the managed object's properties could 721 * not be decoded. 722 */ 723 <C extends ConfigurationClient, S extends Configuration> 724 ServerManagedObject<? extends S> decode( 725 ManagedObjectPath<C, S> path, ConfigEntry configEntry) 726 throws DefinitionDecodingException, ServerManagedObjectDecodingException { 727 return decode(path, configEntry, null); 728 } 729 730 731 732 /** 733 * Decodes a configuration entry into the required type of server 734 * managed object. 735 * 736 * @param <C> 737 * The type of client managed object configuration that the 738 * path definition refers to. 739 * @param <S> 740 * The type of server managed object configuration that the 741 * path definition refers to. 742 * @param path 743 * The location of the server managed object. 744 * @param configEntry 745 * The configuration entry that should be decoded. 746 * @param newConfigEntry 747 * Optional new configuration that does not exist yet in 748 * the configuration back-end. This will be used for 749 * resolving inherited default values. 750 * @return Returns the new server-side managed object from the 751 * provided definition and configuration entry. 752 * @throws DefinitionDecodingException 753 * If the managed object's type could not be determined. 754 * @throws ServerManagedObjectDecodingException 755 * If one or more of the managed object's properties could 756 * not be decoded. 757 */ 758 <C extends ConfigurationClient, S extends Configuration> 759 ServerManagedObject<? extends S> decode( 760 ManagedObjectPath<C, S> path, ConfigEntry configEntry, 761 ConfigEntry newConfigEntry) throws DefinitionDecodingException, 762 ServerManagedObjectDecodingException { 763 // First determine the correct definition to use for the entry. 764 // This could either be the provided definition, or one of its 765 // sub-definitions. 766 DefinitionResolver resolver = new MyDefinitionResolver(configEntry); 767 AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition(); 768 ManagedObjectDefinition<? extends C, ? extends S> mod = d 769 .resolveManagedObjectDefinition(resolver); 770 771 // Build the managed object's properties. 772 List<PropertyException> exceptions = new LinkedList<>(); 773 Map<PropertyDefinition<?>, SortedSet<?>> properties = new HashMap<>(); 774 for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) { 775 List<ByteString> values = getAttribute(mod, pd, configEntry); 776 try { 777 SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry); 778 properties.put(pd, pvalues); 779 } catch (PropertyException e) { 780 exceptions.add(e); 781 } 782 } 783 784 // If there were no decoding problems then return the managed 785 // object, otherwise throw an operations exception. 786 ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties, configEntry); 787 if (!exceptions.isEmpty()) { 788 throw new ServerManagedObjectDecodingException(mo, exceptions); 789 } 790 return mo; 791 } 792 793 794 795 /** Decode helper method required to avoid generics warning. */ 796 private <C extends ConfigurationClient, S extends Configuration> 797 ServerManagedObject<S> decodeAux( 798 ManagedObjectPath<? super C, ? super S> path, 799 ManagedObjectDefinition<C, S> d, 800 Map<PropertyDefinition<?>, SortedSet<?>> properties, 801 ConfigEntry configEntry) { 802 ManagedObjectPath<C, S> newPath = path.asSubType(d); 803 return new ServerManagedObject<>(newPath, d, properties, configEntry); 804 } 805 806 807 808 /** Create a property using the provided string values. */ 809 private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path, 810 PropertyDefinition<T> pd, List<ByteString> values, 811 ConfigEntry newConfigEntry) throws PropertyException { 812 PropertyException exception = null; 813 SortedSet<T> pvalues = new TreeSet<>(pd); 814 815 if (!values.isEmpty()) { 816 // The property has values defined for it. 817 for (ByteString value : values) { 818 try { 819 pvalues.add(ValueDecoder.decode(pd, value)); 820 } catch (PropertyException e) { 821 exception = e; 822 } 823 } 824 } else { 825 // No values defined so get the defaults. 826 try { 827 pvalues.addAll(getDefaultValues(path, pd, newConfigEntry)); 828 } catch (PropertyException e) { 829 exception = e; 830 } 831 } 832 833 if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) { 834 // This exception takes precedence over previous exceptions. 835 exception = PropertyException.propertyIsSingleValuedException(pd); 836 T value = pvalues.first(); 837 pvalues.clear(); 838 pvalues.add(value); 839 } 840 841 if (pvalues.isEmpty() 842 && pd.hasOption(PropertyOption.MANDATORY) 843 // The values maybe empty because of a previous exception. 844 && exception == null) { 845 exception = PropertyException.propertyIsMandatoryException(pd); 846 } 847 848 if (exception != null) { 849 throw exception; 850 } 851 return pvalues; 852 } 853 854 855 856 /** Gets the attribute associated with a property from a ConfigEntry. */ 857 private List<ByteString> getAttribute(ManagedObjectDefinition<?, ?> d, 858 PropertyDefinition<?> pd, ConfigEntry configEntry) { 859 // TODO: we create a default attribute type if it is 860 // undefined. We should log a warning here if this is the case 861 // since the attribute should have been defined. 862 String attrID = LDAPProfile.getInstance().getAttributeName(d, pd); 863 AttributeType type = DirectoryServer.getAttributeType(attrID); 864 List<Attribute> attributes = configEntry.getEntry().getAttribute(type); 865 866 List<ByteString> results = new LinkedList<>(); 867 for (Attribute a : attributes) 868 { 869 for (ByteString v : a) 870 { 871 results.add(v); 872 } 873 } 874 return results; 875 } 876 877 878 879 /** Get the default values for the specified property. */ 880 private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p, 881 PropertyDefinition<T> pd, ConfigEntry newConfigEntry) 882 throws PropertyException { 883 DefaultValueFinder<T> v = new DefaultValueFinder<>(newConfigEntry); 884 return v.find(p, pd); 885 } 886 887 888 889 /** 890 * Gets a config entry required for a managed object and throws a 891 * config exception on failure. 892 */ 893 private ConfigEntry getManagedObjectConfigEntry( 894 DN dn) throws ConfigException { 895 ConfigEntry configEntry; 896 try { 897 configEntry = DirectoryServer.getConfigEntry(dn); 898 } catch (ConfigException e) { 899 logger.traceException(e); 900 901 LocalizableMessage message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get( 902 dn, stackTraceToSingleLineString(e)); 903 throw new ConfigException(message, e); 904 } 905 906 // The configuration handler is free to return null indicating 907 // that the entry does not exist. 908 if (configEntry == null) { 909 LocalizableMessage message = ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST.get(dn); 910 throw new ConfigException(message); 911 } 912 913 return configEntry; 914 } 915 916 917 918 /** Validate that a relation definition belongs to the path. */ 919 private void validateRelationDefinition(ManagedObjectPath<?, ?> path, 920 RelationDefinition<?, ?> rd) throws IllegalArgumentException { 921 AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition(); 922 RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName()); 923 if (tmp != rd) { 924 throw new IllegalArgumentException("The relation " + rd.getName() 925 + " is not associated with a " + d.getName()); 926 } 927 } 928}