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