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