001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.server.admin.server;
029
030
031
032 import static org.opends.messages.AdminMessages.*;
033 import static org.opends.server.loggers.debug.DebugLogger.*;
034 import static org.opends.server.util.StaticUtils.*;
035
036 import java.util.Collections;
037 import java.util.LinkedList;
038 import java.util.List;
039 import java.util.Map;
040 import java.util.Set;
041 import java.util.SortedSet;
042
043 import org.opends.messages.AdminMessages;
044 import org.opends.messages.Message;
045 import org.opends.server.admin.Configuration;
046 import org.opends.server.admin.Constraint;
047 import org.opends.server.admin.InstantiableRelationDefinition;
048 import org.opends.server.admin.ManagedObjectDefinition;
049 import org.opends.server.admin.ManagedObjectPath;
050 import org.opends.server.admin.OptionalRelationDefinition;
051 import org.opends.server.admin.PropertyDefinition;
052 import org.opends.server.admin.PropertyProvider;
053 import org.opends.server.admin.RelationDefinition;
054 import org.opends.server.admin.SingletonRelationDefinition;
055 import org.opends.server.api.ConfigAddListener;
056 import org.opends.server.api.ConfigChangeListener;
057 import org.opends.server.api.ConfigDeleteListener;
058 import org.opends.server.config.ConfigEntry;
059 import org.opends.server.config.ConfigException;
060 import org.opends.server.core.DirectoryServer;
061 import org.opends.server.loggers.debug.DebugTracer;
062 import org.opends.server.types.DN;
063 import org.opends.server.types.DebugLogLevel;
064
065
066
067 /**
068 * A server-side managed object.
069 *
070 * @param <S>
071 * The type of server configuration represented by the server
072 * managed object.
073 */
074 public final class ServerManagedObject<S extends Configuration> implements
075 PropertyProvider {
076
077 /**
078 * The tracer object for the debug logger.
079 */
080 private static final DebugTracer TRACER = getTracer();
081
082 // The configuration entry associated with this server managed
083 // object (null if root).
084 private ConfigEntry configEntry;
085
086 // The management context.
087 private final ServerManagementContext context = ServerManagementContext
088 .getInstance();
089
090 // The managed object's definition.
091 private final ManagedObjectDefinition<?, S> definition;
092
093 // The managed object path identifying this managed object's
094 // location.
095 private final ManagedObjectPath<?, S> path;
096
097 // The managed object's properties.
098 private final Map<PropertyDefinition<?>, SortedSet<?>> properties;
099
100
101
102 /**
103 * Creates an new server side managed object.
104 *
105 * @param path
106 * The managed object path.
107 * @param d
108 * The managed object definition.
109 * @param properties
110 * The managed object's properties.
111 * @param configEntry
112 * The configuration entry associated with the managed
113 * object.
114 */
115 ServerManagedObject(ManagedObjectPath<?, S> path,
116 ManagedObjectDefinition<?, S> d,
117 Map<PropertyDefinition<?>, SortedSet<?>> properties,
118 ConfigEntry configEntry) {
119 this.definition = d;
120 this.path = path;
121 this.properties = properties;
122 this.configEntry = configEntry;
123 }
124
125
126
127 /**
128 * Deregisters an existing configuration add listener.
129 *
130 * @param <M>
131 * The type of the child server configuration object.
132 * @param d
133 * The instantiable relation definition.
134 * @param listener
135 * The configuration add listener.
136 * @throws IllegalArgumentException
137 * If the instantiable relation definition is not
138 * associated with this managed object's definition.
139 */
140 public <M extends Configuration> void deregisterAddListener(
141 InstantiableRelationDefinition<?, M> d,
142 ConfigurationAddListener<M> listener) throws IllegalArgumentException {
143 validateRelationDefinition(d);
144
145 DN baseDN = DNBuilder.create(path, d);
146 deregisterAddListener(baseDN, listener);
147 }
148
149
150
151 /**
152 * Deregisters an existing server managed object add listener.
153 *
154 * @param <M>
155 * The type of the child server configuration object.
156 * @param d
157 * The instantiable relation definition.
158 * @param listener
159 * The server managed object add listener.
160 * @throws IllegalArgumentException
161 * If the instantiable relation definition is not
162 * associated with this managed object's definition.
163 */
164 public <M extends Configuration> void deregisterAddListener(
165 InstantiableRelationDefinition<?, M> d,
166 ServerManagedObjectAddListener<M> listener)
167 throws IllegalArgumentException {
168 validateRelationDefinition(d);
169
170 DN baseDN = DNBuilder.create(path, d);
171 deregisterAddListener(baseDN, listener);
172 }
173
174
175
176 /**
177 * Deregisters an existing configuration add listener.
178 *
179 * @param <M>
180 * The type of the child server configuration object.
181 * @param d
182 * The optional relation definition.
183 * @param listener
184 * The configuration add listener.
185 * @throws IllegalArgumentException
186 * If the optional relation definition is not associated
187 * with this managed object's definition.
188 */
189 public <M extends Configuration> void deregisterAddListener(
190 OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
191 throws IllegalArgumentException {
192 validateRelationDefinition(d);
193
194 DN baseDN = DNBuilder.create(path, d).getParent();
195 deregisterAddListener(baseDN, listener);
196 }
197
198
199
200 /**
201 * Deregisters an existing server managed object add listener.
202 *
203 * @param <M>
204 * The type of the child server configuration object.
205 * @param d
206 * The optional relation definition.
207 * @param listener
208 * The server managed object add listener.
209 * @throws IllegalArgumentException
210 * If the optional relation definition is not associated
211 * with this managed object's definition.
212 */
213 public <M extends Configuration> void deregisterAddListener(
214 OptionalRelationDefinition<?, M> d,
215 ServerManagedObjectAddListener<M> listener)
216 throws IllegalArgumentException {
217 validateRelationDefinition(d);
218
219 DN baseDN = DNBuilder.create(path, d).getParent();
220 deregisterAddListener(baseDN, listener);
221 }
222
223
224
225 /**
226 * Deregisters an existing configuration change listener.
227 *
228 * @param listener
229 * The configuration change listener.
230 */
231 public void deregisterChangeListener(
232 ConfigurationChangeListener<? super S> listener) {
233 for (ConfigChangeListener l : configEntry.getChangeListeners()) {
234 if (l instanceof ConfigChangeListenerAdaptor) {
235 ConfigChangeListenerAdaptor<?> adaptor =
236 (ConfigChangeListenerAdaptor<?>) l;
237 ServerManagedObjectChangeListener<?> l2 = adaptor
238 .getServerManagedObjectChangeListener();
239 if (l2 instanceof ServerManagedObjectChangeListenerAdaptor<?>) {
240 ServerManagedObjectChangeListenerAdaptor<?> adaptor2 =
241 (ServerManagedObjectChangeListenerAdaptor<?>) l2;
242 if (adaptor2.getConfigurationChangeListener() == listener) {
243 adaptor.finalizeChangeListener();
244 configEntry.deregisterChangeListener(adaptor);
245 }
246 }
247 }
248 }
249 }
250
251
252
253 /**
254 * Deregisters an existing server managed object change listener.
255 *
256 * @param listener
257 * The server managed object change listener.
258 */
259 public void deregisterChangeListener(
260 ServerManagedObjectChangeListener<? super S> listener) {
261 for (ConfigChangeListener l : configEntry.getChangeListeners()) {
262 if (l instanceof ConfigChangeListenerAdaptor) {
263 ConfigChangeListenerAdaptor<?> adaptor =
264 (ConfigChangeListenerAdaptor<?>) l;
265 if (adaptor.getServerManagedObjectChangeListener() == listener) {
266 adaptor.finalizeChangeListener();
267 configEntry.deregisterChangeListener(adaptor);
268 }
269 }
270 }
271 }
272
273
274
275 /**
276 * Deregisters an existing configuration delete listener.
277 *
278 * @param <M>
279 * The type of the child server configuration object.
280 * @param d
281 * The instantiable relation definition.
282 * @param listener
283 * The configuration delete listener.
284 * @throws IllegalArgumentException
285 * If the instantiable relation definition is not
286 * associated with this managed object's definition.
287 */
288 public <M extends Configuration> void deregisterDeleteListener(
289 InstantiableRelationDefinition<?, M> d,
290 ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
291 validateRelationDefinition(d);
292
293 DN baseDN = DNBuilder.create(path, d);
294 deregisterDeleteListener(baseDN, listener);
295 }
296
297
298
299 /**
300 * Deregisters an existing server managed object delete listener.
301 *
302 * @param <M>
303 * The type of the child server configuration object.
304 * @param d
305 * The instantiable relation definition.
306 * @param listener
307 * The server managed object delete listener.
308 * @throws IllegalArgumentException
309 * If the instantiable relation definition is not
310 * associated with this managed object's definition.
311 */
312 public <M extends Configuration> void deregisterDeleteListener(
313 InstantiableRelationDefinition<?, M> d,
314 ServerManagedObjectDeleteListener<M> listener)
315 throws IllegalArgumentException {
316 validateRelationDefinition(d);
317
318 DN baseDN = DNBuilder.create(path, d);
319 deregisterDeleteListener(baseDN, listener);
320 }
321
322
323
324 /**
325 * Deregisters an existing configuration delete listener.
326 *
327 * @param <M>
328 * The type of the child server configuration object.
329 * @param d
330 * The optional relation definition.
331 * @param listener
332 * The configuration delete listener.
333 * @throws IllegalArgumentException
334 * If the optional relation definition is not associated
335 * with this managed object's definition.
336 */
337 public <M extends Configuration> void deregisterDeleteListener(
338 OptionalRelationDefinition<?, M> d,
339 ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
340 validateRelationDefinition(d);
341
342 DN baseDN = DNBuilder.create(path, d).getParent();
343 deregisterDeleteListener(baseDN, listener);
344 }
345
346
347
348 /**
349 * Deregisters an existing server managed object delete listener.
350 *
351 * @param <M>
352 * The type of the child server configuration object.
353 * @param d
354 * The optional relation definition.
355 * @param listener
356 * The server managed object delete listener.
357 * @throws IllegalArgumentException
358 * If the optional relation definition is not associated
359 * with this managed object's definition.
360 */
361 public <M extends Configuration> void deregisterDeleteListener(
362 OptionalRelationDefinition<?, M> d,
363 ServerManagedObjectDeleteListener<M> listener)
364 throws IllegalArgumentException {
365 validateRelationDefinition(d);
366
367 DN baseDN = DNBuilder.create(path, d).getParent();
368 deregisterDeleteListener(baseDN, listener);
369 }
370
371
372
373 /**
374 * Retrieve an instantiable child managed object.
375 *
376 * @param <M>
377 * The requested type of the child server managed object
378 * configuration.
379 * @param d
380 * The instantiable relation definition.
381 * @param name
382 * The name of the child managed object.
383 * @return Returns the instantiable child managed object.
384 * @throws IllegalArgumentException
385 * If the relation definition is not associated with this
386 * managed object's definition.
387 * @throws ConfigException
388 * If the child managed object could not be found or if it
389 * could not be decoded.
390 */
391 public <M extends Configuration> ServerManagedObject<? extends M> getChild(
392 InstantiableRelationDefinition<?, M> d, String name)
393 throws IllegalArgumentException, ConfigException {
394 validateRelationDefinition(d);
395 return context.getManagedObject(path.child(d, name));
396 }
397
398
399
400 /**
401 * Retrieve an optional child managed object.
402 *
403 * @param <M>
404 * The requested type of the child server managed object
405 * configuration.
406 * @param d
407 * The optional relation definition.
408 * @return Returns the optional child managed object.
409 * @throws IllegalArgumentException
410 * If the optional relation definition is not associated
411 * with this managed object's definition.
412 * @throws ConfigException
413 * If the child managed object could not be found or if it
414 * could not be decoded.
415 */
416 public <M extends Configuration> ServerManagedObject<? extends M> getChild(
417 OptionalRelationDefinition<?, M> d) throws IllegalArgumentException,
418 ConfigException {
419 validateRelationDefinition(d);
420 return context.getManagedObject(path.child(d));
421 }
422
423
424
425 /**
426 * Retrieve a singleton child managed object.
427 *
428 * @param <M>
429 * The requested type of the child server managed object
430 * configuration.
431 * @param d
432 * The singleton relation definition.
433 * @return Returns the singleton child managed object.
434 * @throws IllegalArgumentException
435 * If the relation definition is not associated with this
436 * managed object's definition.
437 * @throws ConfigException
438 * If the child managed object could not be found or if it
439 * could not be decoded.
440 */
441 public <M extends Configuration> ServerManagedObject<? extends M> getChild(
442 SingletonRelationDefinition<?, M> d) throws IllegalArgumentException,
443 ConfigException {
444 validateRelationDefinition(d);
445 return context.getManagedObject(path.child(d));
446 }
447
448
449
450 /**
451 * Creates a server configuration view of this managed object.
452 *
453 * @return Returns the server configuration view of this managed
454 * object.
455 */
456 public S getConfiguration() {
457 return definition.createServerConfiguration(this);
458 }
459
460
461
462 /**
463 * Get the DN of the LDAP entry associated with this server managed
464 * object.
465 *
466 * @return Returns the DN of the LDAP entry associated with this
467 * server managed object, or an null DN if this is the root
468 * managed object.
469 */
470 public DN getDN() {
471 if (configEntry != null) {
472 return configEntry.getDN();
473 } else {
474 return DN.nullDN();
475 }
476 }
477
478
479
480 /**
481 * Get the definition associated with this server managed object.
482 *
483 * @return Returns the definition associated with this server
484 * managed object.
485 */
486 public ManagedObjectDefinition<?, S> getManagedObjectDefinition() {
487 return definition;
488 }
489
490
491
492 /**
493 * Get the path of this server managed object.
494 *
495 * @return Returns the path of this server managed object.
496 */
497 public ManagedObjectPath<?, S> getManagedObjectPath() {
498 return path;
499 }
500
501
502
503 /**
504 * Get the effective value of the specified property. If the
505 * property is multi-valued then just the first value is returned.
506 * If the property does not have a value then its default value is
507 * returned if it has one, or <code>null</code> indicating that
508 * any default behavior is applicable.
509 *
510 * @param <T>
511 * The type of the property to be retrieved.
512 * @param d
513 * The property to be retrieved.
514 * @return Returns the property's effective value, or
515 * <code>null</code> indicating that any default behavior
516 * is applicable.
517 * @throws IllegalArgumentException
518 * If the property definition is not associated with this
519 * managed object's definition.
520 */
521 public <T> T getPropertyValue(PropertyDefinition<T> d)
522 throws IllegalArgumentException {
523 Set<T> values = getPropertyValues(d);
524 if (values.isEmpty()) {
525 return null;
526 } else {
527 return values.iterator().next();
528 }
529 }
530
531
532
533 /**
534 * Get the effective values of the specified property. If the
535 * property does not have any values then its default values are
536 * returned if it has any, or an empty set indicating that any
537 * default behavior is applicable.
538 *
539 * @param <T>
540 * The type of the property to be retrieved.
541 * @param d
542 * The property to be retrieved.
543 * @return Returns an unmodifiable set containing the property's
544 * effective values. An empty set indicates that the
545 * property has no default values defined and any default
546 * behavior is applicable.
547 * @throws IllegalArgumentException
548 * If the property definition is not associated with this
549 * managed object's definition.
550 */
551 @SuppressWarnings("unchecked")
552 public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
553 throws IllegalArgumentException {
554 if (!properties.containsKey(d)) {
555 throw new IllegalArgumentException("Unknown property " + d.getName());
556 }
557 return Collections.unmodifiableSortedSet((SortedSet<T>) properties.get(d));
558 }
559
560
561
562 /**
563 * Determines whether or not the optional managed object associated
564 * with the specified optional relations exists.
565 *
566 * @param d
567 * The optional relation definition.
568 * @return Returns <code>true</code> if the optional managed
569 * object exists, <code>false</code> otherwise.
570 * @throws IllegalArgumentException
571 * If the optional relation definition is not associated
572 * with this managed object's definition.
573 */
574 public boolean hasChild(OptionalRelationDefinition<?, ?> d)
575 throws IllegalArgumentException {
576 validateRelationDefinition(d);
577 return context.managedObjectExists(path.child(d));
578 }
579
580
581
582 /**
583 * Lists the child managed objects associated with the specified
584 * instantiable relation.
585 *
586 * @param d
587 * The instantiable relation definition.
588 * @return Returns the names of the child managed objects.
589 * @throws IllegalArgumentException
590 * If the relation definition is not associated with this
591 * managed object's definition.
592 */
593 public String[] listChildren(InstantiableRelationDefinition<?, ?> d)
594 throws IllegalArgumentException {
595 validateRelationDefinition(d);
596 return context.listManagedObjects(path, d);
597 }
598
599
600
601 /**
602 * Register to be notified when new child configurations are added
603 * beneath an instantiable relation.
604 *
605 * @param <M>
606 * The type of the child server configuration object.
607 * @param d
608 * The instantiable relation definition.
609 * @param listener
610 * The configuration add listener.
611 * @throws IllegalArgumentException
612 * If the instantiable relation definition is not
613 * associated with this managed object's definition.
614 * @throws ConfigException
615 * If the configuration entry associated with the
616 * instantiable relation could not be retrieved.
617 */
618 public <M extends Configuration> void registerAddListener(
619 InstantiableRelationDefinition<?, M> d,
620 ConfigurationAddListener<M> listener) throws IllegalArgumentException,
621 ConfigException {
622 registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
623 listener));
624 }
625
626
627
628 /**
629 * Register to be notified when new child server managed object are
630 * added beneath an instantiable relation.
631 *
632 * @param <M>
633 * The type of the child server configuration object.
634 * @param d
635 * The instantiable relation definition.
636 * @param listener
637 * The server managed object add listener.
638 * @throws IllegalArgumentException
639 * If the instantiable relation definition is not
640 * associated with this managed object's definition.
641 * @throws ConfigException
642 * If the configuration entry associated with the
643 * instantiable relation could not be retrieved.
644 */
645 public <M extends Configuration> void registerAddListener(
646 InstantiableRelationDefinition<?, M> d,
647 ServerManagedObjectAddListener<M> listener)
648 throws IllegalArgumentException, ConfigException {
649 validateRelationDefinition(d);
650 DN baseDN = DNBuilder.create(path, d);
651 ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
652 listener);
653 registerAddListener(baseDN, adaptor);
654 }
655
656
657
658 /**
659 * Register to be notified when a new child configurations is added
660 * beneath an optional relation.
661 *
662 * @param <M>
663 * The type of the child server configuration object.
664 * @param d
665 * The optional relation definition.
666 * @param listener
667 * The configuration add listener.
668 * @throws IllegalArgumentException
669 * If the optional relation definition is not associated
670 * with this managed object's definition.
671 * @throws ConfigException
672 * If the configuration entry associated with the optional
673 * relation could not be retrieved.
674 */
675 public <M extends Configuration> void registerAddListener(
676 OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
677 throws IllegalArgumentException, ConfigException {
678 registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
679 listener));
680 }
681
682
683
684 /**
685 * Register to be notified when a new child server managed object is
686 * added beneath an optional relation.
687 *
688 * @param <M>
689 * The type of the child server configuration object.
690 * @param d
691 * The optional relation definition.
692 * @param listener
693 * The server managed object add listener.
694 * @throws IllegalArgumentException
695 * If the optional relation definition is not associated
696 * with this managed object's definition.
697 * @throws ConfigException
698 * If the configuration entry associated with the optional
699 * relation could not be retrieved.
700 */
701 public <M extends Configuration> void registerAddListener(
702 OptionalRelationDefinition<?, M> d,
703 ServerManagedObjectAddListener<M> listener)
704 throws IllegalArgumentException, ConfigException {
705 validateRelationDefinition(d);
706 DN baseDN = DNBuilder.create(path, d).getParent();
707 ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
708 listener);
709 registerAddListener(baseDN, adaptor);
710 }
711
712
713
714 /**
715 * Register to be notified when this server managed object is
716 * changed.
717 *
718 * @param listener
719 * The configuration change listener.
720 */
721 public void registerChangeListener(
722 ConfigurationChangeListener<? super S> listener) {
723 registerChangeListener(new ServerManagedObjectChangeListenerAdaptor<S>(
724 listener));
725 }
726
727
728
729 /**
730 * Register to be notified when this server managed object is
731 * changed.
732 *
733 * @param listener
734 * The server managed object change listener.
735 */
736 public void registerChangeListener(
737 ServerManagedObjectChangeListener<? super S> listener) {
738 ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path,
739 listener);
740 configEntry.registerChangeListener(adaptor);
741
742 // Change listener registration usually signifies that a managed
743 // object has been accepted and added to the server configuration
744 // during initialization post-add.
745
746 // FIXME: we should prevent multiple invocations in the case where
747 // multiple change listeners are registered for the same object.
748 for (Constraint constraint : definition.getAllConstraints()) {
749 for (ServerConstraintHandler handler : constraint
750 .getServerConstraintHandlers()) {
751 try {
752 handler.performPostAdd(this);
753 } catch (ConfigException e) {
754 if (debugEnabled()) {
755 TRACER.debugCaught(DebugLogLevel.ERROR, e);
756 }
757 }
758 }
759 }
760 }
761
762
763
764 /**
765 * Register to be notified when existing child configurations are
766 * deleted beneath an instantiable relation.
767 *
768 * @param <M>
769 * The type of the child server configuration object.
770 * @param d
771 * The instantiable relation definition.
772 * @param listener
773 * The configuration delete listener.
774 * @throws IllegalArgumentException
775 * If the instantiable relation definition is not
776 * associated with this managed object's definition.
777 * @throws ConfigException
778 * If the configuration entry associated with the
779 * instantiable relation could not be retrieved.
780 */
781 public <M extends Configuration> void registerDeleteListener(
782 InstantiableRelationDefinition<?, M> d,
783 ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
784 ConfigException {
785 registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
786 listener));
787 }
788
789
790
791 /**
792 * Register to be notified when existing child server managed
793 * objects are deleted beneath an instantiable relation.
794 *
795 * @param <M>
796 * The type of the child server configuration object.
797 * @param d
798 * The instantiable relation definition.
799 * @param listener
800 * The server managed objects delete listener.
801 * @throws IllegalArgumentException
802 * If the instantiable relation definition is not
803 * associated with this managed object's definition.
804 * @throws ConfigException
805 * If the configuration entry associated with the
806 * instantiable relation could not be retrieved.
807 */
808 public <M extends Configuration> void registerDeleteListener(
809 InstantiableRelationDefinition<?, M> d,
810 ServerManagedObjectDeleteListener<M> listener)
811 throws IllegalArgumentException, ConfigException {
812 validateRelationDefinition(d);
813 DN baseDN = DNBuilder.create(path, d);
814 ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
815 listener);
816 registerDeleteListener(baseDN, adaptor);
817 }
818
819
820
821 /**
822 * Register to be notified when an existing child configuration is
823 * deleted beneath an optional relation.
824 *
825 * @param <M>
826 * The type of the child server configuration object.
827 * @param d
828 * The optional relation definition.
829 * @param listener
830 * The configuration delete listener.
831 * @throws IllegalArgumentException
832 * If the optional relation definition is not associated
833 * with this managed object's definition.
834 * @throws ConfigException
835 * If the configuration entry associated with the optional
836 * relation could not be retrieved.
837 */
838 public <M extends Configuration> void registerDeleteListener(
839 OptionalRelationDefinition<?, M> d,
840 ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
841 ConfigException {
842 registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
843 listener));
844 }
845
846
847
848 /**
849 * Register to be notified when an existing child server managed
850 * object is deleted beneath an optional relation.
851 *
852 * @param <M>
853 * The type of the child server configuration object.
854 * @param d
855 * The optional relation definition.
856 * @param listener
857 * The server managed object delete listener.
858 * @throws IllegalArgumentException
859 * If the optional relation definition is not associated
860 * with this managed object's definition.
861 * @throws ConfigException
862 * If the configuration entry associated with the optional
863 * relation could not be retrieved.
864 */
865 public <M extends Configuration> void registerDeleteListener(
866 OptionalRelationDefinition<?, M> d,
867 ServerManagedObjectDeleteListener<M> listener)
868 throws IllegalArgumentException, ConfigException {
869 validateRelationDefinition(d);
870 DN baseDN = DNBuilder.create(path, d).getParent();
871 ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
872 listener);
873 registerDeleteListener(baseDN, adaptor);
874 }
875
876
877
878 /**
879 * {@inheritDoc}
880 */
881 @Override
882 public String toString() {
883 StringBuilder builder = new StringBuilder();
884
885 builder.append("{ TYPE=");
886 builder.append(definition.getName());
887 builder.append(", DN=\"");
888 builder.append(getDN());
889 builder.append('\"');
890 for (Map.Entry<PropertyDefinition<?>, SortedSet<?>> value : properties
891 .entrySet()) {
892 builder.append(", ");
893 builder.append(value.getKey().getName());
894 builder.append('=');
895 builder.append(value.getValue());
896 }
897 builder.append(" }");
898
899 return builder.toString();
900 }
901
902
903
904 /**
905 * Determines whether or not this managed object can be used by the
906 * server.
907 *
908 * @throws ConstraintViolationException
909 * If one or more constraints determined that this managed
910 * object cannot be used by the server.
911 */
912 void ensureIsUsable() throws ConstraintViolationException {
913 // Enforce any constraints.
914 boolean isUsable = true;
915 List<Message> reasons = new LinkedList<Message>();
916 for (Constraint constraint : definition.getAllConstraints()) {
917 for (ServerConstraintHandler handler : constraint
918 .getServerConstraintHandlers()) {
919 try {
920 if (!handler.isUsable(this, reasons)) {
921 isUsable = false;
922 }
923 } catch (ConfigException e) {
924 Message message = ERR_SERVER_CONSTRAINT_EXCEPTION.get(e
925 .getMessageObject());
926 reasons.add(message);
927 isUsable = false;
928 }
929 }
930 }
931
932 if (!isUsable) {
933 throw new ConstraintViolationException(this, reasons);
934 }
935 }
936
937
938
939 /**
940 * Update the config entry associated with this server managed
941 * object. This is only intended to be used by change listener call
942 * backs in order to update the managed object with the correct
943 * config entry.
944 *
945 * @param configEntry
946 * The configuration entry.
947 */
948 void setConfigEntry(ConfigEntry configEntry) {
949 this.configEntry = configEntry;
950 }
951
952
953
954 // Deregister an add listener.
955 private <M extends Configuration> void deregisterAddListener(DN baseDN,
956 ConfigurationAddListener<M> listener) {
957 try {
958 ConfigEntry configEntry = getListenerConfigEntry(baseDN);
959 if (configEntry != null) {
960 for (ConfigAddListener l : configEntry.getAddListeners()) {
961 if (l instanceof ConfigAddListenerAdaptor) {
962 ConfigAddListenerAdaptor<?> adaptor =
963 (ConfigAddListenerAdaptor<?>) l;
964 ServerManagedObjectAddListener<?> l2 = adaptor
965 .getServerManagedObjectAddListener();
966 if (l2 instanceof ServerManagedObjectAddListenerAdaptor<?>) {
967 ServerManagedObjectAddListenerAdaptor<?> adaptor2 =
968 (ServerManagedObjectAddListenerAdaptor<?>) l2;
969 if (adaptor2.getConfigurationAddListener() == listener) {
970 configEntry.deregisterAddListener(adaptor);
971 }
972 }
973 }
974 }
975 }
976 } catch (ConfigException e) {
977 // Ignore the exception since this implies deregistration.
978 if (debugEnabled()) {
979 TRACER.debugCaught(DebugLogLevel.ERROR, e);
980 }
981 }
982 }
983
984
985
986 // Deregister an add listener.
987 private <M extends Configuration> void deregisterAddListener(DN baseDN,
988 ServerManagedObjectAddListener<M> listener) {
989 try {
990 ConfigEntry configEntry = getListenerConfigEntry(baseDN);
991 if (configEntry != null) {
992 for (ConfigAddListener l : configEntry.getAddListeners()) {
993 if (l instanceof ConfigAddListenerAdaptor) {
994 ConfigAddListenerAdaptor<?> adaptor =
995 (ConfigAddListenerAdaptor<?>) l;
996 if (adaptor.getServerManagedObjectAddListener() == listener) {
997 configEntry.deregisterAddListener(adaptor);
998 }
999 }
1000 }
1001 }
1002 } catch (ConfigException e) {
1003 // Ignore the exception since this implies deregistration.
1004 if (debugEnabled()) {
1005 TRACER.debugCaught(DebugLogLevel.ERROR, e);
1006 }
1007 }
1008 }
1009
1010
1011
1012 // Deregister a delete listener.
1013 private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
1014 ConfigurationDeleteListener<M> listener) {
1015 try {
1016 ConfigEntry configEntry = getListenerConfigEntry(baseDN);
1017 if (configEntry != null) {
1018 for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
1019 if (l instanceof ConfigDeleteListenerAdaptor) {
1020 ConfigDeleteListenerAdaptor<?> adaptor =
1021 (ConfigDeleteListenerAdaptor<?>) l;
1022 ServerManagedObjectDeleteListener<?> l2 = adaptor
1023 .getServerManagedObjectDeleteListener();
1024 if (l2 instanceof ServerManagedObjectDeleteListenerAdaptor<?>) {
1025 ServerManagedObjectDeleteListenerAdaptor<?> adaptor2 =
1026 (ServerManagedObjectDeleteListenerAdaptor<?>) l2;
1027 if (adaptor2.getConfigurationDeleteListener() == listener) {
1028 configEntry.deregisterDeleteListener(adaptor);
1029 }
1030 }
1031 }
1032 }
1033 }
1034 } catch (ConfigException e) {
1035 // Ignore the exception since this implies deregistration.
1036 if (debugEnabled()) {
1037 TRACER.debugCaught(DebugLogLevel.ERROR, e);
1038 }
1039 }
1040 }
1041
1042
1043
1044 // Deregister a delete listener.
1045 private <M extends Configuration> void deregisterDeleteListener(DN baseDN,
1046 ServerManagedObjectDeleteListener<M> listener) {
1047 try {
1048 ConfigEntry configEntry = getListenerConfigEntry(baseDN);
1049 if (configEntry != null) {
1050 for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
1051 if (l instanceof ConfigDeleteListenerAdaptor) {
1052 ConfigDeleteListenerAdaptor<?> adaptor =
1053 (ConfigDeleteListenerAdaptor<?>) l;
1054 if (adaptor.getServerManagedObjectDeleteListener() == listener) {
1055 configEntry.deregisterDeleteListener(adaptor);
1056 }
1057 }
1058 }
1059 }
1060 } catch (ConfigException e) {
1061 // Ignore the exception since this implies deregistration.
1062 if (debugEnabled()) {
1063 TRACER.debugCaught(DebugLogLevel.ERROR, e);
1064 }
1065 }
1066 }
1067
1068
1069
1070 // Gets a config entry required for a listener and throws a config
1071 // exception on failure or returns null if the entry does not exist.
1072 private ConfigEntry getListenerConfigEntry(DN dn) throws ConfigException {
1073 // Attempt to retrieve the listener base entry.
1074 ConfigEntry configEntry;
1075 try {
1076 configEntry = DirectoryServer.getConfigEntry(dn);
1077 } catch (ConfigException e) {
1078 if (debugEnabled()) {
1079 TRACER.debugCaught(DebugLogLevel.ERROR, e);
1080 }
1081
1082 Message message = AdminMessages.ERR_ADMIN_CANNOT_GET_LISTENER_BASE.get(
1083 String.valueOf(dn), stackTraceToSingleLineString(e));
1084 throw new ConfigException(message, e);
1085 }
1086
1087 return configEntry;
1088 }
1089
1090
1091
1092 // Register an instantiable or optional relation add listener.
1093 private void registerAddListener(DN baseDN, ConfigAddListener adaptor)
1094 throws IllegalArgumentException, ConfigException {
1095 ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
1096
1097 if (relationEntry != null) {
1098 relationEntry.registerAddListener(adaptor);
1099 } else {
1100 // The relation entry does not exist yet so register a delayed
1101 // add listener.
1102 ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
1103 adaptor);
1104 registerDelayedListener(baseDN, delayedListener);
1105 }
1106 }
1107
1108
1109
1110 // Register a delayed listener with the nearest existing parent
1111 // entry to the provided base DN.
1112 private void registerDelayedListener(DN baseDN,
1113 ConfigAddListener delayedListener) throws ConfigException {
1114 DN parentDN = baseDN.getParent();
1115 while (parentDN != null) {
1116 ConfigEntry relationEntry = getListenerConfigEntry(parentDN);
1117 if (relationEntry == null) {
1118 delayedListener = new DelayedConfigAddListener(parentDN,
1119 delayedListener);
1120 parentDN = parentDN.getParent();
1121 } else {
1122 relationEntry.registerAddListener(delayedListener);
1123 return;
1124 }
1125 }
1126
1127 // No parent entry could be found.
1128 Message message = AdminMessages.ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER
1129 .get(String.valueOf(baseDN));
1130 throw new ConfigException(message);
1131 }
1132
1133
1134
1135 // Register an instantiable or optional relation delete listener.
1136 private void registerDeleteListener(DN baseDN, ConfigDeleteListener adaptor)
1137 throws ConfigException {
1138 ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
1139
1140 if (relationEntry != null) {
1141 relationEntry.registerDeleteListener(adaptor);
1142 } else {
1143 // The relation entry does not exist yet so register a delayed
1144 // add listener.
1145 ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN,
1146 adaptor);
1147 registerDelayedListener(baseDN, delayedListener);
1148 }
1149 }
1150
1151
1152
1153 // Validate that a relation definition belongs to this managed
1154 // object.
1155 private void validateRelationDefinition(RelationDefinition<?, ?> rd)
1156 throws IllegalArgumentException {
1157 RelationDefinition<?, ?> tmp = definition.getRelationDefinition(rd
1158 .getName());
1159 if (tmp != rd) {
1160 throw new IllegalArgumentException("The relation " + rd.getName()
1161 + " is not associated with a " + definition.getName());
1162 }
1163 }
1164 }