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