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 2006-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017
018package org.opends.server.admin.server;
019
020
021
022import static org.opends.messages.AdminMessages.*;
023import static org.opends.server.util.StaticUtils.*;
024
025import java.util.Collections;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030import java.util.SortedSet;
031
032import org.forgerock.i18n.LocalizableMessage;
033import org.opends.server.admin.Configuration;
034import org.opends.server.admin.Constraint;
035import org.opends.server.admin.InstantiableRelationDefinition;
036import org.opends.server.admin.ManagedObjectDefinition;
037import org.opends.server.admin.ManagedObjectPath;
038import org.opends.server.admin.OptionalRelationDefinition;
039import org.opends.server.admin.PropertyDefinition;
040import org.opends.server.admin.PropertyProvider;
041import org.opends.server.admin.RelationDefinition;
042import org.opends.server.admin.SetRelationDefinition;
043import org.opends.server.admin.SingletonRelationDefinition;
044import org.opends.server.api.ConfigAddListener;
045import org.opends.server.api.ConfigChangeListener;
046import org.opends.server.api.ConfigDeleteListener;
047import org.opends.server.config.ConfigEntry;
048import org.forgerock.opendj.config.server.ConfigException;
049import org.opends.server.core.DirectoryServer;
050import org.forgerock.i18n.slf4j.LocalizedLogger;
051import org.forgerock.opendj.ldap.DN;
052
053
054
055/**
056 * A server-side managed object.
057 *
058 * @param <S>
059 *          The type of server configuration represented by the server
060 *          managed object.
061 */
062public final class ServerManagedObject<S extends Configuration> implements
063    PropertyProvider {
064  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
065
066  /**
067   * The configuration entry associated with this server managed
068   * object (null if root).
069   */
070  private ConfigEntry configEntry;
071
072  /** The management context. */
073  private final ServerManagementContext context = ServerManagementContext
074      .getInstance();
075
076  /** The managed object's definition. */
077  private final ManagedObjectDefinition<?, S> definition;
078
079  /** The managed object path identifying this managed object's location. */
080  private final ManagedObjectPath<?, S> path;
081
082  /** The managed object's properties. */
083  private final Map<PropertyDefinition<?>, SortedSet<?>> properties;
084
085
086
087  /**
088   * Creates an new server side managed object.
089   *
090   * @param path
091   *          The managed object path.
092   * @param d
093   *          The managed object definition.
094   * @param properties
095   *          The managed object's properties.
096   * @param configEntry
097   *          The configuration entry associated with the managed
098   *          object.
099   */
100  ServerManagedObject(ManagedObjectPath<?, S> path,
101      ManagedObjectDefinition<?, S> d,
102      Map<PropertyDefinition<?>, SortedSet<?>> properties,
103      ConfigEntry configEntry) {
104    this.definition = d;
105    this.path = path;
106    this.properties = properties;
107    this.configEntry = configEntry;
108  }
109
110
111
112  /**
113   * Deregisters an existing configuration add listener.
114   *
115   * @param <M>
116   *          The type of the child server configuration object.
117   * @param d
118   *          The instantiable relation definition.
119   * @param listener
120   *          The configuration add listener.
121   * @throws IllegalArgumentException
122   *           If the instantiable relation definition is not
123   *           associated with this managed object's definition.
124   */
125  public <M extends Configuration> void deregisterAddListener(
126      InstantiableRelationDefinition<?, M> d,
127      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
128    validateRelationDefinition(d);
129
130    DN baseDN = DNBuilder.create(path, d);
131    deregisterAddListener(baseDN, listener);
132  }
133
134
135
136  /**
137   * Deregisters an existing server managed object add listener.
138   *
139   * @param <M>
140   *          The type of the child server configuration object.
141   * @param d
142   *          The instantiable relation definition.
143   * @param listener
144   *          The server managed object add listener.
145   * @throws IllegalArgumentException
146   *           If the instantiable relation definition is not
147   *           associated with this managed object's definition.
148   */
149  public <M extends Configuration> void deregisterAddListener(
150      InstantiableRelationDefinition<?, M> d,
151      ServerManagedObjectAddListener<M> listener)
152      throws IllegalArgumentException {
153    validateRelationDefinition(d);
154
155    DN baseDN = DNBuilder.create(path, d);
156    deregisterAddListener(baseDN, listener);
157  }
158
159
160
161  /**
162   * Deregisters an existing configuration add listener.
163   *
164   * @param <M>
165   *          The type of the child server configuration object.
166   * @param d
167   *          The optional relation definition.
168   * @param listener
169   *          The configuration add listener.
170   * @throws IllegalArgumentException
171   *           If the optional relation definition is not associated
172   *           with this managed object's definition.
173   */
174  public <M extends Configuration> void deregisterAddListener(
175      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
176      throws IllegalArgumentException {
177    validateRelationDefinition(d);
178
179    DN baseDN = DNBuilder.create(path, d).parent();
180    deregisterAddListener(baseDN, listener);
181  }
182
183
184
185  /**
186   * Deregisters an existing server managed object add listener.
187   *
188   * @param <M>
189   *          The type of the child server configuration object.
190   * @param d
191   *          The optional relation definition.
192   * @param listener
193   *          The server managed object add listener.
194   * @throws IllegalArgumentException
195   *           If the optional relation definition is not associated
196   *           with this managed object's definition.
197   */
198  public <M extends Configuration> void deregisterAddListener(
199      OptionalRelationDefinition<?, M> d,
200      ServerManagedObjectAddListener<M> listener)
201      throws IllegalArgumentException {
202    validateRelationDefinition(d);
203
204    DN baseDN = DNBuilder.create(path, d).parent();
205    deregisterAddListener(baseDN, listener);
206  }
207
208
209
210  /**
211   * Deregisters an existing configuration add listener.
212   *
213   * @param <M>
214   *          The type of the child server configuration object.
215   * @param d
216   *          The set relation definition.
217   * @param listener
218   *          The configuration add listener.
219   * @throws IllegalArgumentException
220   *           If the set relation definition is not
221   *           associated with this managed object's definition.
222   */
223  public <M extends Configuration> void deregisterAddListener(
224      SetRelationDefinition<?, M> d,
225      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
226    validateRelationDefinition(d);
227
228    DN baseDN = DNBuilder.create(path, d);
229    deregisterAddListener(baseDN, listener);
230  }
231
232
233
234  /**
235   * Deregisters an existing server managed object add listener.
236   *
237   * @param <M>
238   *          The type of the child server configuration object.
239   * @param d
240   *          The set relation definition.
241   * @param listener
242   *          The server managed object add listener.
243   * @throws IllegalArgumentException
244   *           If the set relation definition is not
245   *           associated with this managed object's definition.
246   */
247  public <M extends Configuration> void deregisterAddListener(
248      SetRelationDefinition<?, M> d,
249      ServerManagedObjectAddListener<M> listener)
250      throws IllegalArgumentException {
251    validateRelationDefinition(d);
252
253    DN baseDN = DNBuilder.create(path, d);
254    deregisterAddListener(baseDN, listener);
255  }
256
257
258
259  /**
260   * Deregisters an existing configuration change listener.
261   *
262   * @param listener
263   *          The configuration change listener.
264   */
265  public void deregisterChangeListener(
266      ConfigurationChangeListener<? super S> listener) {
267    for (ConfigChangeListener l : configEntry.getChangeListeners()) {
268      if (l instanceof ConfigChangeListenerAdaptor) {
269        ConfigChangeListenerAdaptor<?> adaptor =
270          (ConfigChangeListenerAdaptor<?>) l;
271        ServerManagedObjectChangeListener<?> l2 = adaptor
272            .getServerManagedObjectChangeListener();
273        if (l2 instanceof ServerManagedObjectChangeListenerAdaptor<?>) {
274          ServerManagedObjectChangeListenerAdaptor<?> adaptor2 =
275            (ServerManagedObjectChangeListenerAdaptor<?>) l2;
276          if (adaptor2.getConfigurationChangeListener() == listener) {
277            adaptor.finalizeChangeListener();
278            configEntry.deregisterChangeListener(adaptor);
279          }
280        }
281      }
282    }
283  }
284
285
286
287  /**
288   * Deregisters an existing server managed object change listener.
289   *
290   * @param listener
291   *          The server managed object change listener.
292   */
293  public void deregisterChangeListener(
294      ServerManagedObjectChangeListener<? super S> listener) {
295    for (ConfigChangeListener l : configEntry.getChangeListeners()) {
296      if (l instanceof ConfigChangeListenerAdaptor) {
297        ConfigChangeListenerAdaptor<?> adaptor =
298          (ConfigChangeListenerAdaptor<?>) l;
299        if (adaptor.getServerManagedObjectChangeListener() == listener) {
300          adaptor.finalizeChangeListener();
301          configEntry.deregisterChangeListener(adaptor);
302        }
303      }
304    }
305  }
306
307
308
309  /**
310   * Deregisters an existing configuration delete listener.
311   *
312   * @param <M>
313   *          The type of the child server configuration object.
314   * @param d
315   *          The instantiable relation definition.
316   * @param listener
317   *          The configuration delete listener.
318   * @throws IllegalArgumentException
319   *           If the instantiable relation definition is not
320   *           associated with this managed object's definition.
321   */
322  public <M extends Configuration> void deregisterDeleteListener(
323      InstantiableRelationDefinition<?, M> d,
324      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
325    validateRelationDefinition(d);
326
327    DN baseDN = DNBuilder.create(path, d);
328    deregisterDeleteListener(baseDN, listener);
329  }
330
331
332
333  /**
334   * Deregisters an existing server managed object delete listener.
335   *
336   * @param <M>
337   *          The type of the child server configuration object.
338   * @param d
339   *          The instantiable relation definition.
340   * @param listener
341   *          The server managed object delete listener.
342   * @throws IllegalArgumentException
343   *           If the instantiable relation definition is not
344   *           associated with this managed object's definition.
345   */
346  public <M extends Configuration> void deregisterDeleteListener(
347      InstantiableRelationDefinition<?, M> d,
348      ServerManagedObjectDeleteListener<M> listener)
349      throws IllegalArgumentException {
350    validateRelationDefinition(d);
351
352    DN baseDN = DNBuilder.create(path, d);
353    deregisterDeleteListener(baseDN, listener);
354  }
355
356
357
358  /**
359   * Deregisters an existing configuration delete listener.
360   *
361   * @param <M>
362   *          The type of the child server configuration object.
363   * @param d
364   *          The optional relation definition.
365   * @param listener
366   *          The configuration delete listener.
367   * @throws IllegalArgumentException
368   *           If the optional relation definition is not associated
369   *           with this managed object's definition.
370   */
371  public <M extends Configuration> void deregisterDeleteListener(
372      OptionalRelationDefinition<?, M> d,
373      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
374    validateRelationDefinition(d);
375
376    DN baseDN = DNBuilder.create(path, d).parent();
377    deregisterDeleteListener(baseDN, listener);
378  }
379
380
381
382  /**
383   * Deregisters an existing server managed object delete listener.
384   *
385   * @param <M>
386   *          The type of the child server configuration object.
387   * @param d
388   *          The optional relation definition.
389   * @param listener
390   *          The server managed object delete listener.
391   * @throws IllegalArgumentException
392   *           If the optional relation definition is not associated
393   *           with this managed object's definition.
394   */
395  public <M extends Configuration> void deregisterDeleteListener(
396      OptionalRelationDefinition<?, M> d,
397      ServerManagedObjectDeleteListener<M> listener)
398      throws IllegalArgumentException {
399    validateRelationDefinition(d);
400
401    DN baseDN = DNBuilder.create(path, d).parent();
402    deregisterDeleteListener(baseDN, listener);
403  }
404
405
406
407  /**
408   * Deregisters an existing configuration delete listener.
409   *
410   * @param <M>
411   *          The type of the child server configuration object.
412   * @param d
413   *          The set relation definition.
414   * @param listener
415   *          The configuration delete listener.
416   * @throws IllegalArgumentException
417   *           If the set relation definition is not
418   *           associated with this managed object's definition.
419   */
420  public <M extends Configuration> void deregisterDeleteListener(
421      SetRelationDefinition<?, M> d,
422      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
423    validateRelationDefinition(d);
424
425    DN baseDN = DNBuilder.create(path, d);
426    deregisterDeleteListener(baseDN, listener);
427  }
428
429
430
431  /**
432   * Deregisters an existing server managed object delete listener.
433   *
434   * @param <M>
435   *          The type of the child server configuration object.
436   * @param d
437   *          The set relation definition.
438   * @param listener
439   *          The server managed object delete listener.
440   * @throws IllegalArgumentException
441   *           If the set relation definition is not
442   *           associated with this managed object's definition.
443   */
444  public <M extends Configuration> void deregisterDeleteListener(
445      SetRelationDefinition<?, M> d,
446      ServerManagedObjectDeleteListener<M> listener)
447      throws IllegalArgumentException {
448    validateRelationDefinition(d);
449
450    DN baseDN = DNBuilder.create(path, d);
451    deregisterDeleteListener(baseDN, listener);
452  }
453
454
455
456  /**
457   * Retrieve an instantiable child managed object.
458   *
459   * @param <M>
460   *          The requested type of the child server managed object
461   *          configuration.
462   * @param d
463   *          The instantiable relation definition.
464   * @param name
465   *          The name of the child managed object.
466   * @return Returns the instantiable child managed object.
467   * @throws IllegalArgumentException
468   *           If the relation definition is not associated with this
469   *           managed object's definition.
470   * @throws ConfigException
471   *           If the child managed object could not be found or if it
472   *           could not be decoded.
473   */
474  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
475      InstantiableRelationDefinition<?, M> d, String name)
476      throws IllegalArgumentException, ConfigException {
477    validateRelationDefinition(d);
478    return context.getManagedObject(path.child(d, name));
479  }
480
481
482
483  /**
484   * Retrieve an optional child managed object.
485   *
486   * @param <M>
487   *          The requested type of the child server managed object
488   *          configuration.
489   * @param d
490   *          The optional relation definition.
491   * @return Returns the optional child managed object.
492   * @throws IllegalArgumentException
493   *           If the optional relation definition is not associated
494   *           with this managed object's definition.
495   * @throws ConfigException
496   *           If the child managed object could not be found or if it
497   *           could not be decoded.
498   */
499  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
500      OptionalRelationDefinition<?, M> d) throws IllegalArgumentException,
501      ConfigException {
502    validateRelationDefinition(d);
503    return context.getManagedObject(path.child(d));
504  }
505
506
507
508  /**
509   * Retrieve a set child managed object.
510   *
511   * @param <M>
512   *          The requested type of the child server managed object
513   *          configuration.
514   * @param d
515   *          The set relation definition.
516   * @param name
517   *          The name of the child managed object.
518   * @return Returns the set child managed object.
519   * @throws IllegalArgumentException
520   *           If the relation definition is not associated with this
521   *           managed object's definition or if {@code name} specifies
522   *           a managed object definition which is not a sub-type of
523   *           the relation's child definition.
524   * @throws ConfigException
525   *           If the child managed object could not be found or if it
526   *           could not be decoded.
527   */
528  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
529      SetRelationDefinition<?, M> d, String name)
530      throws IllegalArgumentException, ConfigException
531  {
532    validateRelationDefinition(d);
533
534    return context.getManagedObject(path.child(d, name));
535  }
536
537
538
539  /**
540   * Retrieve a singleton child managed object.
541   *
542   * @param <M>
543   *          The requested type of the child server managed object
544   *          configuration.
545   * @param d
546   *          The singleton relation definition.
547   * @return Returns the singleton child managed object.
548   * @throws IllegalArgumentException
549   *           If the relation definition is not associated with this
550   *           managed object's definition.
551   * @throws ConfigException
552   *           If the child managed object could not be found or if it
553   *           could not be decoded.
554   */
555  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
556      SingletonRelationDefinition<?, M> d) throws IllegalArgumentException,
557      ConfigException {
558    validateRelationDefinition(d);
559    return context.getManagedObject(path.child(d));
560  }
561
562
563
564  /**
565   * Creates a server configuration view of this managed object.
566   *
567   * @return Returns the server configuration view of this managed
568   *         object.
569   */
570  public S getConfiguration() {
571    return definition.createServerConfiguration(this);
572  }
573
574
575
576  /**
577   * Get the DN of the LDAP entry associated with this server managed
578   * object.
579   *
580   * @return Returns the DN of the LDAP entry associated with this
581   *         server managed object, or an null DN if this is the root
582   *         managed object.
583   */
584  public DN getDN() {
585    if (configEntry != null) {
586      return configEntry.getDN();
587    } else {
588      return DN.rootDN();
589    }
590  }
591
592
593
594  /**
595   * Get the definition associated with this server managed object.
596   *
597   * @return Returns the definition associated with this server
598   *         managed object.
599   */
600  public ManagedObjectDefinition<?, S> getManagedObjectDefinition() {
601    return definition;
602  }
603
604
605
606  /**
607   * Get the path of this server managed object.
608   *
609   * @return Returns the path of this server managed object.
610   */
611  public ManagedObjectPath<?, S> getManagedObjectPath() {
612    return path;
613  }
614
615
616
617  /**
618   * Get the effective value of the specified property. If the
619   * property is multi-valued then just the first value is returned.
620   * If the property does not have a value then its default value is
621   * returned if it has one, or <code>null</code> indicating that
622   * any default behavior is applicable.
623   *
624   * @param <T>
625   *          The type of the property to be retrieved.
626   * @param d
627   *          The property to be retrieved.
628   * @return Returns the property's effective value, or
629   *         <code>null</code> indicating that any default behavior
630   *         is applicable.
631   * @throws IllegalArgumentException
632   *           If the property definition is not associated with this
633   *           managed object's definition.
634   */
635  public <T> T getPropertyValue(PropertyDefinition<T> d)
636      throws IllegalArgumentException {
637    Set<T> values = getPropertyValues(d);
638    if (values.isEmpty()) {
639      return null;
640    } else {
641      return values.iterator().next();
642    }
643  }
644
645
646
647  /**
648   * Get the effective values of the specified property. If the
649   * property does not have any values then its default values are
650   * returned if it has any, or an empty set indicating that any
651   * default behavior is applicable.
652   *
653   * @param <T>
654   *          The type of the property to be retrieved.
655   * @param d
656   *          The property to be retrieved.
657   * @return Returns an unmodifiable set containing the property's
658   *         effective values. An empty set indicates that the
659   *         property has no default values defined and any default
660   *         behavior is applicable.
661   * @throws IllegalArgumentException
662   *           If the property definition is not associated with this
663   *           managed object's definition.
664   */
665  @SuppressWarnings("unchecked")
666  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
667      throws IllegalArgumentException {
668    if (!properties.containsKey(d)) {
669      throw new IllegalArgumentException("Unknown property " + d.getName());
670    }
671    return Collections.unmodifiableSortedSet((SortedSet<T>) properties.get(d));
672  }
673
674
675
676  /**
677   * Determines whether or not the optional managed object associated
678   * with the specified optional relations exists.
679   *
680   * @param d
681   *          The optional relation definition.
682   * @return Returns <code>true</code> if the optional managed
683   *         object exists, <code>false</code> otherwise.
684   * @throws IllegalArgumentException
685   *           If the optional relation definition is not associated
686   *           with this managed object's definition.
687   */
688  public boolean hasChild(OptionalRelationDefinition<?, ?> d)
689      throws IllegalArgumentException {
690    validateRelationDefinition(d);
691    return context.managedObjectExists(path.child(d));
692  }
693
694
695
696  /**
697   * Lists the child managed objects associated with the specified
698   * instantiable relation.
699   *
700   * @param d
701   *          The instantiable relation definition.
702   * @return Returns the names of the child managed objects.
703   * @throws IllegalArgumentException
704   *           If the relation definition is not associated with this
705   *           managed object's definition.
706   */
707  public String[] listChildren(InstantiableRelationDefinition<?, ?> d)
708      throws IllegalArgumentException {
709    validateRelationDefinition(d);
710    return context.listManagedObjects(path, d);
711  }
712
713
714
715  /**
716   * Lists the child managed objects associated with the specified
717   * set relation.
718   *
719   * @param d
720   *          The set relation definition.
721   * @return Returns the names of the child managed objects.
722   * @throws IllegalArgumentException
723   *           If the relation definition is not associated with this
724   *           managed object's definition.
725   */
726  public String[] listChildren(SetRelationDefinition<?, ?> d)
727      throws IllegalArgumentException {
728    validateRelationDefinition(d);
729    return context.listManagedObjects(path, d);
730  }
731
732
733
734  /**
735   * Register to be notified when new child configurations are added
736   * beneath an instantiable relation.
737   *
738   * @param <M>
739   *          The type of the child server configuration object.
740   * @param d
741   *          The instantiable relation definition.
742   * @param listener
743   *          The configuration add listener.
744   * @throws IllegalArgumentException
745   *           If the instantiable relation definition is not
746   *           associated with this managed object's definition.
747   * @throws ConfigException
748   *           If the configuration entry associated with the
749   *           instantiable relation could not be retrieved.
750   */
751  public <M extends Configuration> void registerAddListener(
752      InstantiableRelationDefinition<?, M> d,
753      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
754      ConfigException {
755    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
756        listener));
757  }
758
759
760
761  /**
762   * Register to be notified when new child server managed object are
763   * added beneath an instantiable relation.
764   *
765   * @param <M>
766   *          The type of the child server configuration object.
767   * @param d
768   *          The instantiable relation definition.
769   * @param listener
770   *          The server managed object add listener.
771   * @throws IllegalArgumentException
772   *           If the instantiable relation definition is not
773   *           associated with this managed object's definition.
774   * @throws ConfigException
775   *           If the configuration entry associated with the
776   *           instantiable relation could not be retrieved.
777   */
778  public <M extends Configuration> void registerAddListener(
779      InstantiableRelationDefinition<?, M> d,
780      ServerManagedObjectAddListener<M> listener)
781      throws IllegalArgumentException, ConfigException {
782    validateRelationDefinition(d);
783    DN baseDN = DNBuilder.create(path, d);
784    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<>(path, d, listener);
785    registerAddListener(baseDN, adaptor);
786  }
787
788
789
790  /**
791   * Register to be notified when a new child configurations is added
792   * beneath an optional relation.
793   *
794   * @param <M>
795   *          The type of the child server configuration object.
796   * @param d
797   *          The optional relation definition.
798   * @param listener
799   *          The configuration add listener.
800   * @throws IllegalArgumentException
801   *           If the optional relation definition is not associated
802   *           with this managed object's definition.
803   * @throws ConfigException
804   *           If the configuration entry associated with the optional
805   *           relation could not be retrieved.
806   */
807  public <M extends Configuration> void registerAddListener(
808      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
809      throws IllegalArgumentException, ConfigException {
810    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(listener));
811  }
812
813
814
815  /**
816   * Register to be notified when a new child server managed object is
817   * added beneath an optional relation.
818   *
819   * @param <M>
820   *          The type of the child server configuration object.
821   * @param d
822   *          The optional relation definition.
823   * @param listener
824   *          The server managed object add listener.
825   * @throws IllegalArgumentException
826   *           If the optional relation definition is not associated
827   *           with this managed object's definition.
828   * @throws ConfigException
829   *           If the configuration entry associated with the optional
830   *           relation could not be retrieved.
831   */
832  public <M extends Configuration> void registerAddListener(
833      OptionalRelationDefinition<?, M> d,
834      ServerManagedObjectAddListener<M> listener)
835      throws IllegalArgumentException, ConfigException {
836    validateRelationDefinition(d);
837    DN baseDN = DNBuilder.create(path, d).parent();
838    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<>(path, d, listener);
839    registerAddListener(baseDN, adaptor);
840  }
841
842
843
844  /**
845   * Register to be notified when new child configurations are added
846   * beneath a set relation.
847   *
848   * @param <M>
849   *          The type of the child server configuration object.
850   * @param d
851   *          The set relation definition.
852   * @param listener
853   *          The configuration add listener.
854   * @throws IllegalArgumentException
855   *           If the set relation definition is not
856   *           associated with this managed object's definition.
857   * @throws ConfigException
858   *           If the configuration entry associated with the
859   *           set relation could not be retrieved.
860   */
861  public <M extends Configuration> void registerAddListener(
862      SetRelationDefinition<?, M> d,
863      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
864      ConfigException {
865    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
866        listener));
867  }
868
869
870
871  /**
872   * Register to be notified when new child server managed object are
873   * added beneath a set relation.
874   *
875   * @param <M>
876   *          The type of the child server configuration object.
877   * @param d
878   *          The set relation definition.
879   * @param listener
880   *          The server managed object add listener.
881   * @throws IllegalArgumentException
882   *           If the set relation definition is not
883   *           associated with this managed object's definition.
884   * @throws ConfigException
885   *           If the configuration entry associated with the
886   *           set relation could not be retrieved.
887   */
888  public <M extends Configuration> void registerAddListener(
889      SetRelationDefinition<?, M> d,
890      ServerManagedObjectAddListener<M> listener)
891      throws IllegalArgumentException, ConfigException {
892    validateRelationDefinition(d);
893    DN baseDN = DNBuilder.create(path, d);
894    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<>(path, d, listener);
895    registerAddListener(baseDN, adaptor);
896  }
897
898
899
900  /**
901   * Register to be notified when this server managed object is
902   * changed.
903   *
904   * @param listener
905   *          The configuration change listener.
906   */
907  public void registerChangeListener(
908      ConfigurationChangeListener<? super S> listener) {
909    registerChangeListener(new ServerManagedObjectChangeListenerAdaptor<S>(
910        listener));
911  }
912
913
914
915  /**
916   * Register to be notified when this server managed object is
917   * changed.
918   *
919   * @param listener
920   *          The server managed object change listener.
921   */
922  public void registerChangeListener(
923      ServerManagedObjectChangeListener<? super S> listener) {
924    ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<>(path, listener);
925    configEntry.registerChangeListener(adaptor);
926
927    // Change listener registration usually signifies that a managed
928    // object has been accepted and added to the server configuration
929    // during initialization post-add.
930
931    // FIXME: we should prevent multiple invocations in the case where
932    // multiple change listeners are registered for the same object.
933    for (Constraint constraint : definition.getAllConstraints()) {
934      for (ServerConstraintHandler handler : constraint
935          .getServerConstraintHandlers()) {
936        try {
937          handler.performPostAdd(this);
938        } catch (ConfigException e) {
939          logger.traceException(e);
940        }
941      }
942    }
943  }
944
945
946
947  /**
948   * Register to be notified when existing child configurations are
949   * deleted beneath an instantiable relation.
950   *
951   * @param <M>
952   *          The type of the child server configuration object.
953   * @param d
954   *          The instantiable relation definition.
955   * @param listener
956   *          The configuration delete listener.
957   * @throws IllegalArgumentException
958   *           If the instantiable relation definition is not
959   *           associated with this managed object's definition.
960   * @throws ConfigException
961   *           If the configuration entry associated with the
962   *           instantiable relation could not be retrieved.
963   */
964  public <M extends Configuration> void registerDeleteListener(
965      InstantiableRelationDefinition<?, M> d,
966      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
967      ConfigException {
968    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
969        listener));
970  }
971
972
973
974  /**
975   * Register to be notified when existing child server managed
976   * objects are deleted beneath an instantiable relation.
977   *
978   * @param <M>
979   *          The type of the child server configuration object.
980   * @param d
981   *          The instantiable relation definition.
982   * @param listener
983   *          The server managed objects delete listener.
984   * @throws IllegalArgumentException
985   *           If the instantiable relation definition is not
986   *           associated with this managed object's definition.
987   * @throws ConfigException
988   *           If the configuration entry associated with the
989   *           instantiable relation could not be retrieved.
990   */
991  public <M extends Configuration> void registerDeleteListener(
992      InstantiableRelationDefinition<?, M> d,
993      ServerManagedObjectDeleteListener<M> listener)
994      throws IllegalArgumentException, ConfigException {
995    validateRelationDefinition(d);
996    DN baseDN = DNBuilder.create(path, d);
997    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<>(path, d, listener);
998    registerDeleteListener(baseDN, adaptor);
999  }
1000
1001
1002
1003  /**
1004   * Register to be notified when an existing child configuration is
1005   * deleted beneath an optional relation.
1006   *
1007   * @param <M>
1008   *          The type of the child server configuration object.
1009   * @param d
1010   *          The optional relation definition.
1011   * @param listener
1012   *          The configuration delete listener.
1013   * @throws IllegalArgumentException
1014   *           If the optional relation definition is not associated
1015   *           with this managed object's definition.
1016   * @throws ConfigException
1017   *           If the configuration entry associated with the optional
1018   *           relation could not be retrieved.
1019   */
1020  public <M extends Configuration> void registerDeleteListener(
1021      OptionalRelationDefinition<?, M> d,
1022      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
1023      ConfigException {
1024    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
1025        listener));
1026  }
1027
1028
1029
1030  /**
1031   * Register to be notified when an existing child server managed
1032   * object is deleted beneath an optional relation.
1033   *
1034   * @param <M>
1035   *          The type of the child server configuration object.
1036   * @param d
1037   *          The optional relation definition.
1038   * @param listener
1039   *          The server managed object delete listener.
1040   * @throws IllegalArgumentException
1041   *           If the optional relation definition is not associated
1042   *           with this managed object's definition.
1043   * @throws ConfigException
1044   *           If the configuration entry associated with the optional
1045   *           relation could not be retrieved.
1046   */
1047  public <M extends Configuration> void registerDeleteListener(
1048      OptionalRelationDefinition<?, M> d,
1049      ServerManagedObjectDeleteListener<M> listener)
1050      throws IllegalArgumentException, ConfigException {
1051    validateRelationDefinition(d);
1052    DN baseDN = DNBuilder.create(path, d).parent();
1053    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<>(path, d, listener);
1054    registerDeleteListener(baseDN, adaptor);
1055  }
1056
1057
1058
1059  /**
1060   * Register to be notified when existing child configurations are
1061   * deleted beneath a set relation.
1062   *
1063   * @param <M>
1064   *          The type of the child server configuration object.
1065   * @param d
1066   *          The set relation definition.
1067   * @param listener
1068   *          The configuration delete listener.
1069   * @throws IllegalArgumentException
1070   *           If the set relation definition is not
1071   *           associated with this managed object's definition.
1072   * @throws ConfigException
1073   *           If the configuration entry associated with the
1074   *           set relation could not be retrieved.
1075   */
1076  public <M extends Configuration> void registerDeleteListener(
1077      SetRelationDefinition<?, M> d,
1078      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
1079      ConfigException {
1080    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
1081        listener));
1082  }
1083
1084
1085
1086  /**
1087   * Register to be notified when existing child server managed
1088   * objects are deleted beneath a set relation.
1089   *
1090   * @param <M>
1091   *          The type of the child server configuration object.
1092   * @param d
1093   *          The set relation definition.
1094   * @param listener
1095   *          The server managed objects delete listener.
1096   * @throws IllegalArgumentException
1097   *           If the set relation definition is not
1098   *           associated with this managed object's definition.
1099   * @throws ConfigException
1100   *           If the configuration entry associated with the
1101   *           set relation could not be retrieved.
1102   */
1103  public <M extends Configuration> void registerDeleteListener(
1104      SetRelationDefinition<?, M> d,
1105      ServerManagedObjectDeleteListener<M> listener)
1106      throws IllegalArgumentException, ConfigException {
1107    validateRelationDefinition(d);
1108    DN baseDN = DNBuilder.create(path, d);
1109    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<>(path, d, listener);
1110    registerDeleteListener(baseDN, adaptor);
1111  }
1112
1113
1114
1115  /** {@inheritDoc} */
1116  @Override
1117  public String toString() {
1118    StringBuilder builder = new StringBuilder();
1119
1120    builder.append("{ TYPE=");
1121    builder.append(definition.getName());
1122    builder.append(", DN=\"");
1123    builder.append(getDN());
1124    builder.append('\"');
1125    for (Map.Entry<PropertyDefinition<?>, SortedSet<?>> value : properties
1126        .entrySet()) {
1127      builder.append(", ");
1128      builder.append(value.getKey().getName());
1129      builder.append('=');
1130      builder.append(value.getValue());
1131    }
1132    builder.append(" }");
1133
1134    return builder.toString();
1135  }
1136
1137
1138
1139  /**
1140   * Determines whether or not this managed object can be used by the
1141   * server.
1142   *
1143   * @throws ConstraintViolationException
1144   *           If one or more constraints determined that this managed
1145   *           object cannot be used by the server.
1146   */
1147  void ensureIsUsable() throws ConstraintViolationException {
1148    // Enforce any constraints.
1149    boolean isUsable = true;
1150    List<LocalizableMessage> reasons = new LinkedList<>();
1151    for (Constraint constraint : definition.getAllConstraints()) {
1152      for (ServerConstraintHandler handler : constraint
1153          .getServerConstraintHandlers()) {
1154        try {
1155          if (!handler.isUsable(this, reasons)) {
1156            isUsable = false;
1157          }
1158        } catch (ConfigException e) {
1159          LocalizableMessage message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
1160              .getMessageObject());
1161          reasons.add(message);
1162          isUsable = false;
1163        }
1164      }
1165    }
1166
1167    if (!isUsable) {
1168      throw new ConstraintViolationException(this, reasons);
1169    }
1170  }
1171
1172
1173
1174  /**
1175   * Update the config entry associated with this server managed
1176   * object. This is only intended to be used by change listener call
1177   * backs in order to update the managed object with the correct
1178   * config entry.
1179   *
1180   * @param configEntry
1181   *          The configuration entry.
1182   */
1183  void setConfigEntry(ConfigEntry configEntry) {
1184    this.configEntry = configEntry;
1185  }
1186
1187
1188
1189  /** Deregister an add listener. */
1190  private <M extends Configuration> void deregisterAddListener(DN baseDN,
1191      ConfigurationAddListener<M> listener) {
1192    try {
1193      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
1194      if (configEntry != null) {
1195        for (ConfigAddListener l : configEntry.getAddListeners()) {
1196          if (l instanceof ConfigAddListenerAdaptor) {
1197            ConfigAddListenerAdaptor<?> adaptor =
1198              (ConfigAddListenerAdaptor<?>) l;
1199            ServerManagedObjectAddListener<?> l2 = adaptor
1200                .getServerManagedObjectAddListener();
1201            if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
1202              ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
1203                (ServerManagedObjectAddListenerAdaptor<?>) l2;
1204              if (adaptor2.getConfigurationAddListener() == listener) {
1205                configEntry.deregisterAddListener(adaptor);
1206              }
1207            }
1208          }
1209        }
1210      }
1211      else
1212      {
1213        // The relation entry does not exist so check for and deregister
1214        // delayed add listener.
1215        deregisterDelayedAddListener(baseDN, listener);
1216      }
1217    } catch (ConfigException e) {
1218      // Ignore the exception since this implies deregistration.
1219      logger.traceException(e);
1220    }
1221  }
1222
1223
1224
1225  /** Deregister an add listener. */
1226  private <M extends Configuration> void deregisterAddListener(DN baseDN,
1227      ServerManagedObjectAddListener<M> listener) {
1228    try {
1229      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
1230      if (configEntry != null) {
1231        for (ConfigAddListener l : configEntry.getAddListeners()) {
1232          if (l instanceof ConfigAddListenerAdaptor) {
1233            ConfigAddListenerAdaptor<?> adaptor =
1234              (ConfigAddListenerAdaptor<?>) l;
1235            if (adaptor.getServerManagedObjectAddListener() == listener) {
1236              configEntry.deregisterAddListener(adaptor);
1237            }
1238          }
1239        }
1240      }
1241      else
1242      {
1243        // The relation entry does not exist so check for and deregister
1244        // delayed add listener.
1245        deregisterDelayedAddListener(baseDN, listener);
1246      }
1247    } catch (ConfigException e) {
1248      // Ignore the exception since this implies deregistration.
1249      logger.traceException(e);
1250    }
1251  }
1252
1253
1254
1255  /** Deregister a delete listener. */
1256  private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
1257      ConfigurationDeleteListener<M> listener) {
1258    try {
1259      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
1260      if (configEntry != null) {
1261        for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
1262          if (l instanceof ConfigDeleteListenerAdaptor) {
1263            ConfigDeleteListenerAdaptor<?> adaptor =
1264              (ConfigDeleteListenerAdaptor<?>) l;
1265            ServerManagedObjectDeleteListener<?> l2 = adaptor
1266                .getServerManagedObjectDeleteListener();
1267            if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
1268              ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
1269                (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
1270              if (adaptor2.getConfigurationDeleteListener() == listener) {
1271                configEntry.deregisterDeleteListener(adaptor);
1272              }
1273            }
1274          }
1275        }
1276      }
1277      else
1278      {
1279        // The relation entry does not exist so check for and deregister
1280        // delayed add listener.
1281        deregisterDelayedDeleteListener(baseDN, listener);
1282      }
1283    } catch (ConfigException e) {
1284      // Ignore the exception since this implies deregistration.
1285      logger.traceException(e);
1286    }
1287  }
1288
1289
1290
1291  /** Deregister a delete listener. */
1292  private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
1293      ServerManagedObjectDeleteListener<M> listener) {
1294    try {
1295      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
1296      if (configEntry != null) {
1297        for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
1298          if (l instanceof ConfigDeleteListenerAdaptor) {
1299            ConfigDeleteListenerAdaptor<?> adaptor =
1300              (ConfigDeleteListenerAdaptor<?>) l;
1301            if (adaptor.getServerManagedObjectDeleteListener() == listener) {
1302              configEntry.deregisterDeleteListener(adaptor);
1303            }
1304          }
1305        }
1306      }
1307      else
1308      {
1309        // The relation entry does not exist so check for and deregister
1310        // delayed add listener.
1311        deregisterDelayedDeleteListener(baseDN, listener);
1312      }
1313    } catch (ConfigException e) {
1314      // Ignore the exception since this implies deregistration.
1315      logger.traceException(e);
1316    }
1317  }
1318
1319
1320
1321  /**
1322   * Gets a config entry required for a listener and throws a config
1323   * exception on failure or returns null if the entry does not exist.
1324   */
1325  private ConfigEntry getListenerConfigEntry(DN dn) throws ConfigException {
1326    // Attempt to retrieve the listener base entry.
1327    ConfigEntry configEntry;
1328    try {
1329      configEntry = DirectoryServer.getConfigEntry(dn);
1330    } catch (ConfigException e) {
1331      logger.traceException(e);
1332
1333      LocalizableMessage message = ERR_ADMIN_CANNOT_GET_LISTENER_BASE.get(
1334          dn, stackTraceToSingleLineString(e));
1335      throw new ConfigException(message, e);
1336    }
1337
1338    return configEntry;
1339  }
1340
1341
1342
1343  /** Register an instantiable or optional relation add listener. */
1344  private void registerAddListener(DN baseDN, ConfigAddListener adaptor)
1345      throws IllegalArgumentException, ConfigException {
1346    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
1347
1348    if (relationEntry != null) {
1349      relationEntry.registerAddListener(adaptor);
1350    } else {
1351      // The relation entry does not exist yet so register a delayed
1352      // add listener.
1353      ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
1354          adaptor);
1355      registerDelayedListener(baseDN, delayedListener);
1356    }
1357  }
1358
1359
1360
1361  /**
1362   * Register a delayed listener with the nearest existing parent
1363   * entry to the provided base DN.
1364   */
1365  private void registerDelayedListener(DN baseDN,
1366      ConfigAddListener delayedListener) throws ConfigException {
1367    DN parentDN = baseDN.parent();
1368    while (parentDN != null) {
1369      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
1370      if (relationEntry == null) {
1371        delayedListener = new DelayedConfigAddListener(parentDN,
1372            delayedListener);
1373        parentDN = parentDN.parent();
1374      } else {
1375        relationEntry.registerAddListener(delayedListener);
1376        return;
1377      }
1378    }
1379
1380    // No parent entry could be found.
1381    LocalizableMessage message = ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER.get(baseDN);
1382    throw new ConfigException(message);
1383  }
1384
1385  /**
1386   * Deregister a delayed listener with the nearest existing parent
1387   * entry to the provided base DN.
1388   */
1389  private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
1390      ConfigurationAddListener<M> listener) throws ConfigException {
1391    DN parentDN = baseDN.parent();
1392    int delayWrappers = 0;
1393    while (parentDN != null) {
1394      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
1395      if (relationEntry == null) {
1396        parentDN = parentDN.parent();
1397        delayWrappers++;
1398      } else {
1399        for (ConfigAddListener l : relationEntry.getAddListeners()) {
1400          if(l instanceof DelayedConfigAddListener)
1401          {
1402            DelayedConfigAddListener delayListener =
1403                (DelayedConfigAddListener) l;
1404            ConfigAddListener wrappedListener;
1405
1406            int i = delayWrappers;
1407            for(; i > 0; i--)
1408            {
1409              wrappedListener = delayListener.getDelayedAddListener();
1410              if(wrappedListener instanceof DelayedConfigAddListener)
1411              {
1412                delayListener = (DelayedConfigAddListener) l;
1413              }
1414              else
1415              {
1416                break;
1417              }
1418            }
1419
1420            if(i > 0)
1421            {
1422              // There are not enough level of wrapping so this can't be
1423              // the listener we are looking for.
1424              continue;
1425            }
1426
1427            ConfigAddListener delayedListener =
1428                delayListener.getDelayedAddListener();
1429
1430            if (delayedListener instanceof ConfigAddListenerAdaptor) {
1431              ConfigAddListenerAdaptor<?> adaptor =
1432                  (ConfigAddListenerAdaptor<?>) delayedListener;
1433              ServerManagedObjectAddListener<?> l2 = adaptor
1434                  .getServerManagedObjectAddListener();
1435              if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
1436                ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
1437                    (ServerManagedObjectAddListenerAdaptor<?>) l2;
1438                if (adaptor2.getConfigurationAddListener() == listener) {
1439                  relationEntry.deregisterAddListener(l);
1440                }
1441              }
1442            }
1443          }
1444        }
1445        return;
1446      }
1447    }
1448  }
1449
1450
1451  /**
1452   * Deregister a delayed listener with the nearest existing parent
1453   * entry to the provided base DN.
1454   */
1455  private <M extends Configuration> void deregisterDelayedDeleteListener(
1456      DN baseDN, ConfigurationDeleteListener<M> listener)
1457      throws ConfigException {
1458    DN parentDN = baseDN.parent();
1459    int delayWrappers = 0;
1460    while (parentDN != null) {
1461      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
1462      if (relationEntry == null) {
1463        parentDN = parentDN.parent();
1464        delayWrappers++;
1465      } else {
1466        for (ConfigAddListener l : relationEntry.getAddListeners()) {
1467          if(l instanceof DelayedConfigAddListener)
1468          {
1469            DelayedConfigAddListener delayListener =
1470                (DelayedConfigAddListener) l;
1471            ConfigAddListener wrappedListener;
1472
1473            int i = delayWrappers;
1474            for(; i > 0; i--)
1475            {
1476              wrappedListener = delayListener.getDelayedAddListener();
1477              if(wrappedListener instanceof DelayedConfigAddListener)
1478              {
1479                delayListener = (DelayedConfigAddListener) l;
1480              }
1481              else
1482              {
1483                break;
1484              }
1485            }
1486
1487            if(i > 0)
1488            {
1489              // There are not enough level of wrapping so this can't be
1490              // the listener we are looking for.
1491              continue;
1492            }
1493
1494            ConfigDeleteListener delayedListener =
1495                delayListener.getDelayedDeleteListener();
1496
1497            if (delayedListener instanceof ConfigDeleteListenerAdaptor) {
1498              ConfigDeleteListenerAdaptor<?> adaptor =
1499                  (ConfigDeleteListenerAdaptor<?>) delayedListener;
1500              ServerManagedObjectDeleteListener<?> l2 = adaptor
1501                  .getServerManagedObjectDeleteListener();
1502              if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
1503                ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
1504                    (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
1505                if (adaptor2.getConfigurationDeleteListener() == listener) {
1506                  relationEntry.deregisterAddListener(l);
1507                }
1508              }
1509            }
1510          }
1511        }
1512        return;
1513      }
1514    }
1515  }
1516
1517  /**
1518   * Deregister a delayed listener with the nearest existing parent
1519   * entry to the provided base DN.
1520   */
1521  private <M extends Configuration> void deregisterDelayedAddListener(DN baseDN,
1522      ServerManagedObjectAddListener<M> listener) throws ConfigException {
1523    DN parentDN = baseDN.parent();
1524    int delayWrappers = 0;
1525    while (parentDN != null) {
1526      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
1527      if (relationEntry == null) {
1528        parentDN = parentDN.parent();
1529        delayWrappers++;
1530      } else {
1531        for (ConfigAddListener l : relationEntry.getAddListeners()) {
1532          if(l instanceof DelayedConfigAddListener)
1533          {
1534            DelayedConfigAddListener delayListener =
1535                (DelayedConfigAddListener) l;
1536            ConfigAddListener wrappedListener;
1537
1538            int i = delayWrappers;
1539            for(; i > 0; i--)
1540            {
1541              wrappedListener = delayListener.getDelayedAddListener();
1542              if(wrappedListener instanceof DelayedConfigAddListener)
1543              {
1544                delayListener = (DelayedConfigAddListener) l;
1545              }
1546              else
1547              {
1548                break;
1549              }
1550            }
1551
1552            if(i > 0)
1553            {
1554              // There are not enough level of wrapping so this can't be
1555              // the listener we are looking for.
1556              continue;
1557            }
1558
1559            ConfigAddListener delayedListener =
1560                delayListener.getDelayedAddListener();
1561
1562            if (delayedListener instanceof ConfigAddListenerAdaptor) {
1563              ConfigAddListenerAdaptor<?> adaptor =
1564                  (ConfigAddListenerAdaptor<?>) delayedListener;
1565              if (adaptor.getServerManagedObjectAddListener() == listener) {
1566                relationEntry.deregisterAddListener(l);
1567              }
1568            }
1569          }
1570        }
1571        return;
1572      }
1573    }
1574  }
1575
1576
1577  /**
1578   * Deregister a delayed listener with the nearest existing parent
1579   * entry to the provided base DN.
1580   */
1581  private <M extends Configuration> void deregisterDelayedDeleteListener(
1582      DN baseDN, ServerManagedObjectDeleteListener<M> listener)
1583      throws ConfigException {
1584    DN parentDN = baseDN.parent();
1585    int delayWrappers = 0;
1586    while (parentDN != null) {
1587      ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
1588      if (relationEntry == null) {
1589        parentDN = parentDN.parent();
1590        delayWrappers++;
1591      } else {
1592        for (ConfigAddListener l : relationEntry.getAddListeners()) {
1593          if(l instanceof DelayedConfigAddListener)
1594          {
1595            DelayedConfigAddListener delayListener =
1596                (DelayedConfigAddListener) l;
1597            ConfigAddListener wrappedListener;
1598
1599            int i = delayWrappers;
1600            for(; i > 0; i--)
1601            {
1602              wrappedListener = delayListener.getDelayedAddListener();
1603              if(wrappedListener instanceof DelayedConfigAddListener)
1604              {
1605                delayListener = (DelayedConfigAddListener) l;
1606              }
1607              else
1608              {
1609                break;
1610              }
1611            }
1612
1613            if(i > 0)
1614            {
1615              // There are not enough level of wrapping so this can't be
1616              // the listener we are looking for.
1617              continue;
1618            }
1619
1620            ConfigDeleteListener delayedListener =
1621                delayListener.getDelayedDeleteListener();
1622
1623            if (delayedListener instanceof ConfigDeleteListenerAdaptor) {
1624              ConfigDeleteListenerAdaptor<?> adaptor =
1625                  (ConfigDeleteListenerAdaptor<?>) delayedListener;
1626              if (adaptor.getServerManagedObjectDeleteListener() == listener) {
1627                relationEntry.deregisterAddListener(l);
1628              }
1629            }
1630          }
1631        }
1632        return;
1633      }
1634    }
1635  }
1636
1637
1638  /** Register an instantiable or optional relation delete listener. */
1639  private void registerDeleteListener(DN baseDN, ConfigDeleteListener adaptor)
1640      throws ConfigException {
1641    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
1642
1643    if (relationEntry != null) {
1644      relationEntry.registerDeleteListener(adaptor);
1645    } else {
1646      // The relation entry does not exist yet so register a delayed
1647      // add listener.
1648      ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
1649          adaptor);
1650      registerDelayedListener(baseDN, delayedListener);
1651    }
1652  }
1653
1654
1655
1656  /** Validate that a relation definition belongs to this managed object. */
1657  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
1658      throws IllegalArgumentException {
1659    RelationDefinition<?, ?> tmp = definition.getRelationDefinition(rd
1660        .getName());
1661    if (tmp != rd) {
1662      throw new IllegalArgumentException("The relation " + rd.getName()
1663          + " is not associated with a " + definition.getName());
1664    }
1665  }
1666}