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}