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