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}