/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.admin.server; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.util.StaticUtils.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider; import org.opends.server.admin.AbstractManagedObjectDefinition; import org.opends.server.admin.AliasDefaultBehaviorProvider; import org.opends.server.admin.Configuration; import org.opends.server.admin.DefaultBehaviorException; import org.opends.server.admin.DefaultBehaviorProviderVisitor; import org.opends.server.admin.DefinedDefaultBehaviorProvider; import org.opends.server.admin.DefinitionDecodingException; import org.opends.server.admin.DefinitionResolver; import org.opends.server.admin.IllegalPropertyValueException; import org.opends.server.admin.IllegalPropertyValueStringException; import org.opends.server.admin.InstantiableRelationDefinition; import org.opends.server.admin.LDAPProfile; import org.opends.server.admin.ManagedObjectDefinition; import org.opends.server.admin.ManagedObjectPath; import org.opends.server.admin.OptionalRelationDefinition; import org.opends.server.admin.PropertyDefinition; import org.opends.server.admin.PropertyException; import org.opends.server.admin.PropertyIsMandatoryException; import org.opends.server.admin.PropertyIsSingleValuedException; import org.opends.server.admin.PropertyNotFoundException; import org.opends.server.admin.PropertyOption; import org.opends.server.admin.PropertyProvider; import org.opends.server.admin.RelationDefinition; import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider; import org.opends.server.admin.SingletonRelationDefinition; import org.opends.server.admin.UndefinedDefaultBehaviorProvider; import org.opends.server.admin.DefinitionDecodingException.Reason; import org.opends.server.admin.std.meta.RootCfgDefn; import org.opends.server.admin.std.server.RootCfg; import org.opends.server.api.AttributeValueDecoder; import org.opends.server.api.ConfigAddListener; import org.opends.server.api.ConfigChangeListener; import org.opends.server.api.ConfigDeleteListener; import org.opends.server.config.ConfigEntry; import org.opends.server.config.ConfigException; import org.opends.server.core.DirectoryServer; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.messages.AdminMessages; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.DN; import org.opends.server.types.DebugLogLevel; import org.opends.server.types.DirectoryException; /** * A server-side managed object. * * @param * The type of server configuration represented by the server * managed object. */ public final class ServerManagedObject implements PropertyProvider { /** * A default behavior visitor used for retrieving the default values * of a property. * * @param * The type of the property. */ private static class DefaultValueFinder implements DefaultBehaviorProviderVisitor, Void> { /** * Get the default values for the specified property. * * @param * The type of the property. * @param p * The managed object path of the current managed object. * @param pd * The property definition. * @param newConfigEntry * Optional new configuration entry which does not yet * exist in the configuration back-end. * @return Returns the default values for the specified property. * @throws DefaultBehaviorException * If the default values could not be retrieved or * decoded properly. */ public static Collection getDefaultValues(ManagedObjectPath p, PropertyDefinition pd, ConfigEntry newConfigEntry) throws DefaultBehaviorException { DefaultValueFinder v = new DefaultValueFinder(newConfigEntry); return v.find(p, pd); } // Any exception that occurred whilst retrieving inherited default // values. private DefaultBehaviorException exception = null; // The path of the managed object containing the next property. private ManagedObjectPath nextPath = null; // The next property whose default values were required. private PropertyDefinition nextProperty = null; // Optional new configuration entry which does not yet exist in // the configuration back-end. private ConfigEntry newConfigEntry; // Private constructor. private DefaultValueFinder(ConfigEntry newConfigEntry) { this.newConfigEntry = newConfigEntry; } /** * {@inheritDoc} */ public Collection visitAbsoluteInherited( AbsoluteInheritedDefaultBehaviorProvider d, Void p) { try { return getInheritedProperty(d.getManagedObjectPath(), d .getManagedObjectDefinition(), d.getPropertyName()); } catch (DefaultBehaviorException e) { exception = e; return Collections.emptySet(); } } /** * {@inheritDoc} */ public Collection visitAlias(AliasDefaultBehaviorProvider d, Void p) { return Collections.emptySet(); } /** * {@inheritDoc} */ public Collection visitDefined(DefinedDefaultBehaviorProvider d, Void p) { Collection stringValues = d.getDefaultValues(); List values = new ArrayList(stringValues.size()); for (String stringValue : stringValues) { try { values.add(nextProperty.decodeValue(stringValue)); } catch (IllegalPropertyValueStringException e) { exception = new DefaultBehaviorException(nextProperty, e); break; } } return values; } /** * {@inheritDoc} */ public Collection visitRelativeInherited( RelativeInheritedDefaultBehaviorProvider d, Void p) { try { return getInheritedProperty(d.getManagedObjectPath(nextPath), d .getManagedObjectDefinition(), d.getPropertyName()); } catch (DefaultBehaviorException e) { exception = e; return Collections.emptySet(); } } /** * {@inheritDoc} */ public Collection visitUndefined(UndefinedDefaultBehaviorProvider d, Void p) { return Collections.emptySet(); } // Find the default values for the next path/property. private Collection find(ManagedObjectPath p, PropertyDefinition pd) throws DefaultBehaviorException { nextPath = p; nextProperty = pd; Collection values = nextProperty.getDefaultBehaviorProvider().accept( this, null); if (exception != null) { throw exception; } if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) { throw new DefaultBehaviorException(pd, new PropertyIsSingleValuedException(pd)); } return values; } // Get an inherited property value. @SuppressWarnings("unchecked") private Collection getInheritedProperty(ManagedObjectPath target, AbstractManagedObjectDefinition d, String propertyName) throws DefaultBehaviorException { // First check that the requested type of managed object // corresponds to the path. AbstractManagedObjectDefinition supr = target .getManagedObjectDefinition(); if (!supr.isParentOf(d)) { throw new DefaultBehaviorException(nextProperty, new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION)); } // Save the current property in case of recursion. PropertyDefinition pd1 = nextProperty; try { // Get the actual managed object definition. DN dn = DNBuilder.create(target); ConfigEntry configEntry; if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) { configEntry = newConfigEntry; } else { configEntry = getManagedObjectConfigEntry(dn); } DefinitionResolver resolver = new MyDefinitionResolver(configEntry); ManagedObjectDefinition mod = d .resolveManagedObjectDefinition(resolver); PropertyDefinition pd2; try { PropertyDefinition pdTmp = mod.getPropertyDefinition(propertyName); pd2 = pd1.getClass().cast(pdTmp); } catch (IllegalArgumentException e) { throw new PropertyNotFoundException(propertyName); } catch (ClassCastException e) { // FIXME: would be nice to throw a better exception here. throw new PropertyNotFoundException(propertyName); } List stringValues = getAttribute(mod, pd2, configEntry); if (stringValues.isEmpty()) { // Recursively retrieve this property's default values. Collection tmp = find(target, pd2); Collection values = new ArrayList(tmp.size()); for (T value : tmp) { pd1.validateValue(value); values.add(value); } return values; } else { Collection values = new ArrayList(stringValues.size()); for (String s : stringValues) { values.add(pd1.decodeValue(s)); } return values; } } catch (DefinitionDecodingException e) { throw new DefaultBehaviorException(pd1, e); } catch (PropertyNotFoundException e) { throw new DefaultBehaviorException(pd1, e); } catch (IllegalPropertyValueException e) { throw new DefaultBehaviorException(pd1, e); } catch (IllegalPropertyValueStringException e) { throw new DefaultBehaviorException(pd1, e); } catch (ConfigException e) { throw new DefaultBehaviorException(pd1, e); } } } /** * A definition resolver that determines the managed object * definition from the object classes of a ConfigEntry. */ private static class MyDefinitionResolver implements DefinitionResolver { // The config entry. private final ConfigEntry entry; // Private constructor. private MyDefinitionResolver(ConfigEntry entry) { this.entry = entry; } /** * {@inheritDoc} */ public boolean matches(AbstractManagedObjectDefinition d) { String oc = LDAPProfile.getInstance().getObjectClass(d); return entry.hasObjectClass(oc); } }; /** * The root server managed object. */ private static final ServerManagedObject ROOT = new ServerManagedObject( ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections ., SortedSet> emptyMap(), null); /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); /** * Decodes a configuration entry into the required type of server * managed object. * * @param * The type of server configuration represented by the * decoded server managed object. * @param path * The location of the server managed object. * @param definition * The required managed object type. * @param configEntry * The configuration entry that should be decoded. * @return Returns the new server-side managed object from the * provided definition and configuration entry. * @throws DefinitionDecodingException * If the managed object's type could not be determined. * @throws ServerManagedObjectDecodingException * If one or more of the managed object's properties could * not be decoded. */ static ServerManagedObject decode( ManagedObjectPath path, AbstractManagedObjectDefinition definition, ConfigEntry configEntry) throws DefinitionDecodingException, ServerManagedObjectDecodingException { return decode(path, definition, configEntry, null); } /** * Decodes a configuration entry into the required type of server * managed object. * * @param * The type of server configuration represented by the * decoded server managed object. * @param path * The location of the server managed object. * @param definition * The required managed object type. * @param configEntry * The configuration entry that should be decoded. * @param newConfigEntry * Optional new configuration that does not exist yet in * the configuration back-end. This will be used for * resolving inherited default values. * @return Returns the new server-side managed object from the * provided definition and configuration entry. * @throws DefinitionDecodingException * If the managed object's type could not be determined. * @throws ServerManagedObjectDecodingException * If one or more of the managed object's properties could * not be decoded. */ static ServerManagedObject decode( ManagedObjectPath path, AbstractManagedObjectDefinition definition, ConfigEntry configEntry, ConfigEntry newConfigEntry) throws DefinitionDecodingException, ServerManagedObjectDecodingException { // First determine the correct definition to use for the entry. // This could either be the provided definition, or one of its // sub-definitions. DefinitionResolver resolver = new MyDefinitionResolver(configEntry); ManagedObjectDefinition mod = definition .resolveManagedObjectDefinition(resolver); // Build the managed object's properties. List exceptions = new LinkedList(); Map, SortedSet> properties = new HashMap, SortedSet>(); for (PropertyDefinition pd : mod.getAllPropertyDefinitions()) { List values = getAttribute(mod, pd, configEntry); try { decodeProperty(properties, path, pd, values, newConfigEntry); } catch (PropertyException e) { exceptions.add(e); } } // If there were no decoding problems then return the managed // object, otherwise throw an operations exception. ServerManagedObject mo = decodeAux(path, mod, properties, configEntry); if (exceptions.isEmpty()) { return mo; } else { throw new ServerManagedObjectDecodingException(mo, exceptions); } } /** * Gets the root server managed object. * * @return Returns the root server managed object. */ static ServerManagedObject getRootManagedObject() { return ROOT; } // Decode helper method required to avoid generics warning. private static ServerManagedObject decodeAux( ManagedObjectPath path, ManagedObjectDefinition d, Map, SortedSet> properties, ConfigEntry configEntry) { return new ServerManagedObject(path, d, properties, configEntry); } // Create a property using the provided string values. private static void decodeProperty( Map, SortedSet> properties, ManagedObjectPath path, PropertyDefinition pd, List stringValues, ConfigEntry newConfigEntry) throws PropertyException { PropertyException exception = null; SortedSet values = new TreeSet(pd); if (!stringValues.isEmpty()) { // The property has values defined for it. for (String value : stringValues) { try { values.add(pd.decodeValue(value)); } catch (IllegalPropertyValueStringException e) { exception = e; } } } else { // No values defined so get the defaults. try { values.addAll(DefaultValueFinder.getDefaultValues(path, pd, newConfigEntry)); } catch (DefaultBehaviorException e) { exception = e; } } if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) { // This exception takes precedence over previous exceptions. exception = new PropertyIsSingleValuedException(pd); T value = values.first(); values.clear(); values.add(value); } if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) { // The values maybe empty because of a previous exception. if (exception == null) { exception = new PropertyIsMandatoryException(pd); } } // TODO: If an exception occurs should we leave the property // empty? properties.put(pd, values); if (exception != null) { throw exception; } } // Gets the attribute associated with a property from a ConfigEntry. private static List getAttribute(ManagedObjectDefinition d, PropertyDefinition pd, ConfigEntry configEntry) { // TODO: we create a default attribute type if it is // undefined. We should log a warning here if this is the case // since the attribute should have been defined. String attrID = LDAPProfile.getInstance().getAttributeName(d, pd); AttributeType type = DirectoryServer.getAttributeType(attrID, true); AttributeValueDecoder decoder = new AttributeValueDecoder() { public String decode(AttributeValue value) throws DirectoryException { return value.getStringValue(); } }; List values = new LinkedList(); try { configEntry.getEntry().getAttributeValues(type, decoder, values); } catch (DirectoryException e) { // Should not happen. throw new RuntimeException(e); } return values; } // Gets a config entry required for a managed object and throws a // config exception on failure. private static ConfigEntry getManagedObjectConfigEntry(DN dn) throws ConfigException { ConfigEntry configEntry; try { configEntry = DirectoryServer.getConfigEntry(dn); } catch (ConfigException e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } int msgID = AdminMessages.MSGID_ADMIN_CANNOT_GET_MANAGED_OBJECT; String message = getMessage(msgID, String.valueOf(dn), stackTraceToSingleLineString(e)); throw new ConfigException(msgID, message, e); } // The configuration handler is free to return null indicating // that the entry does not exist. if (configEntry == null) { int msgID = AdminMessages.MSGID_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST; String message = getMessage(msgID, String.valueOf(dn)); throw new ConfigException(msgID, message); } return configEntry; } // The configuration entry associated with this server managed // object (null if root). private ConfigEntry configEntry; // The managed object's definition. private final ManagedObjectDefinition definition; // The managed object path identifying this managed object's // location. private final ManagedObjectPath path; // The managed object's properties. private final Map, SortedSet> properties; // Create an new server side managed object. private ServerManagedObject(ManagedObjectPath path, ManagedObjectDefinition d, Map, SortedSet> properties, ConfigEntry configEntry) { this.definition = d; this.path = path; this.properties = properties; this.configEntry = configEntry; } /** * Deregisters an existing configuration add listener. * * @param * The type of the child server configuration object. * @param d * The instantiable relation definition. * @param listener * The configuration add listener. * @throws IllegalArgumentException * If the instantiable relation definition is not * associated with this managed object's definition. */ public void deregisterAddListener( InstantiableRelationDefinition d, ConfigurationAddListener listener) throws IllegalArgumentException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d); deregisterAddListener(baseDN, listener); } /** * Deregisters an existing configuration add listener. * * @param * The type of the child server configuration object. * @param d * The optional relation definition. * @param listener * The configuration add listener. * @throws IllegalArgumentException * If the optional relation definition is not associated * with this managed object's definition. */ public void deregisterAddListener( OptionalRelationDefinition d, ConfigurationAddListener listener) throws IllegalArgumentException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d).getParent(); deregisterAddListener(baseDN, listener); } /** * Deregisters an existing configuration change listener. * * @param listener * The configuration change listener. */ public void deregisterChangeListener( ConfigurationChangeListener listener) { for (ConfigChangeListener l : configEntry.getChangeListeners()) { if (l instanceof ConfigChangeListenerAdaptor) { ConfigChangeListenerAdaptor adaptor = (ConfigChangeListenerAdaptor) l; if (adaptor.getConfigurationChangeListener() == listener) { configEntry.deregisterChangeListener(adaptor); } } } } /** * Deregisters an existing configuration delete listener. * * @param * The type of the child server configuration object. * @param d * The instantiable relation definition. * @param listener * The configuration delete listener. * @throws IllegalArgumentException * If the instantiable relation definition is not * associated with this managed object's definition. */ public void deregisterDeleteListener( InstantiableRelationDefinition d, ConfigurationDeleteListener listener) throws IllegalArgumentException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d); deregisterDeleteListener(baseDN, listener); } /** * Deregisters an existing configuration delete listener. * * @param * The type of the child server configuration object. * @param d * The optional relation definition. * @param listener * The configuration delete listener. * @throws IllegalArgumentException * If the optional relation definition is not associated * with this managed object's definition. */ public void deregisterDeleteListener( OptionalRelationDefinition d, ConfigurationDeleteListener listener) throws IllegalArgumentException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d).getParent(); deregisterDeleteListener(baseDN, listener); } /** * Retrieve an instantiable child managed object. * * @param * The requested type of the child server managed object * configuration. * @param d * The instantiable relation definition. * @param name * The name of the child managed object. * @return Returns the instantiable child managed object. * @throws IllegalArgumentException * If the relation definition is not associated with this * managed object's definition. * @throws ConfigException * If the child managed object could not be found or if it * could not be decoded. */ public ServerManagedObject getChild( InstantiableRelationDefinition d, String name) throws IllegalArgumentException, ConfigException { validateRelationDefinition(d); ManagedObjectPath childPath = path.child(d, name); return getChild(childPath, d); } /** * Retrieve an optional child managed object. * * @param * The requested type of the child server managed object * configuration. * @param d * The optional relation definition. * @return Returns the optional child managed object. * @throws IllegalArgumentException * If the optional relation definition is not associated * with this managed object's definition. * @throws ConfigException * If the child managed object could not be found or if it * could not be decoded. */ public ServerManagedObject getChild( OptionalRelationDefinition d) throws IllegalArgumentException, ConfigException { validateRelationDefinition(d); ManagedObjectPath childPath = path.child(d); return getChild(childPath, d); } /** * Retrieve a singleton child managed object. * * @param * The requested type of the child server managed object * configuration. * @param d * The singleton relation definition. * @return Returns the singleton child managed object. * @throws IllegalArgumentException * If the relation definition is not associated with this * managed object's definition. * @throws ConfigException * If the child managed object could not be found or if it * could not be decoded. */ public ServerManagedObject getChild( SingletonRelationDefinition d) throws IllegalArgumentException, ConfigException { validateRelationDefinition(d); ManagedObjectPath childPath = path.child(d); return getChild(childPath, d); } /** * Creates a server configuration view of this managed object. * * @return Returns the server configuration view of this managed * object. */ public S getConfiguration() { return definition.createServerConfiguration(this); } /** * Get the DN of the LDAP entry associated with this server managed * object. * * @return Returns the DN of the LDAP entry associated with this * server managed object, or an null DN if this is the root * managed object. */ public DN getDN() { if (configEntry != null) { return configEntry.getDN(); } else { return DN.nullDN(); } } /** * Get the definition associated with this server managed object. * * @return Returns the definition associated with this server * managed object. */ public ManagedObjectDefinition getManagedObjectDefinition() { return definition; } /** * Get the path of this server managed object. * * @return Returns the path of this server managed object. */ public ManagedObjectPath getManagedObjectPath() { return path; } /** * Get the effective value of the specified property. If the * property is multi-valued then just the first value is returned. * If the property does not have a value then its default value is * returned if it has one, or null indicating that * any default behavior is applicable. * * @param * The type of the property to be retrieved. * @param d * The property to be retrieved. * @return Returns the property's effective value, or * null indicating that any default behavior * is applicable. * @throws IllegalArgumentException * If the property definition is not associated with this * managed object's definition. */ public T getPropertyValue(PropertyDefinition d) throws IllegalArgumentException { Set values = getPropertyValues(d); if (values.isEmpty()) { return null; } else { return values.iterator().next(); } } /** * Get the effective values of the specified property. If the * property does not have any values then its default values are * returned if it has any, or an empty set indicating that any * default behavior is applicable. * * @param * The type of the property to be retrieved. * @param d * The property to be retrieved. * @return Returns a newly allocated set containing a copy of the * property's effective values. An empty set indicates that * the property has no default values defined and any * default behavior is applicable. * @throws IllegalArgumentException * If the property definition is not associated with this * managed object's definition. */ @SuppressWarnings("unchecked") public SortedSet getPropertyValues(PropertyDefinition d) throws IllegalArgumentException { if (!properties.containsKey(d)) { throw new IllegalArgumentException("Unknown property " + d.getName()); } return new TreeSet((SortedSet) properties.get(d)); } /** * Determines whether or not the optional managed object associated * with the specified optional relations exists. * * @param d * The optional relation definition. * @return Returns true if the optional managed * object exists, false otherwise. * @throws IllegalArgumentException * If the optional relation definition is not associated * with this managed object's definition. */ public boolean hasChild(OptionalRelationDefinition d) throws IllegalArgumentException { validateRelationDefinition(d); // Get the configuration entry. DN targetDN = DNBuilder.create(path, d); try { return (getManagedObjectConfigEntry(targetDN) != null); } catch (ConfigException e) { // Assume it doesn't exist. return false; } } /** * Lists the child managed objects associated with the specified * instantiable relation. * * @param d * The instantiable relation definition. * @return Returns the names of the child managed objects. * @throws IllegalArgumentException * If the relation definition is not associated with this * managed object's definition. */ public String[] listChildren(InstantiableRelationDefinition d) throws IllegalArgumentException { validateRelationDefinition(d); // Get the target entry. DN targetDN = DNBuilder.create(path, d); ConfigEntry configEntry; try { configEntry = DirectoryServer.getConfigEntry(targetDN); } catch (ConfigException e) { return new String[0]; } if (configEntry == null) { return new String[0]; } // Retrieve the children. Set children = configEntry.getChildren().keySet(); ArrayList names = new ArrayList(children.size()); for (DN child : children) { // Assume that RDNs are single-valued and can be trimmed. AttributeValue av = child.getRDN().getAttributeValue(0); names.add(av.getStringValue().trim()); } return names.toArray(new String[names.size()]); } /** * Register to be notified when new child configurations are added * beneath an instantiable relation. * * @param * The type of the child server configuration object. * @param d * The instantiable relation definition. * @param listener * The configuration add listener. * @throws IllegalArgumentException * If the instantiable relation definition is not * associated with this managed object's definition. * @throws ConfigException * If the configuration entry associated with the * instantiable relation could not be retrieved. */ public void registerAddListener( InstantiableRelationDefinition d, ConfigurationAddListener listener) throws IllegalArgumentException, ConfigException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d); ConfigAddListener adaptor = new ConfigAddListenerAdaptor(path, d, listener); registerAddListener(baseDN, adaptor); } /** * Register to be notified when a new child configurations is added * beneath an optional relation. * * @param * The type of the child server configuration object. * @param d * The optional relation definition. * @param listener * The configuration add listener. * @throws IllegalArgumentException * If the optional relation definition is not associated * with this managed object's definition. * @throws ConfigException * If the configuration entry associated with the * optional relation could not be retrieved. */ public void registerAddListener( OptionalRelationDefinition d, ConfigurationAddListener listener) throws IllegalArgumentException, ConfigException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d).getParent(); ConfigAddListener adaptor = new ConfigAddListenerAdaptor(path, d, listener); registerAddListener(baseDN, adaptor); } /** * Register to be notified when this server managed object is * changed. * * @param listener * The configuration change listener. */ public void registerChangeListener( ConfigurationChangeListener listener) { ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor(path, definition, listener); configEntry.registerChangeListener(adaptor); } /** * Register to be notified when existing child configurations are * deleted beneath an instantiable relation. * * @param * The type of the child server configuration object. * @param d * The instantiable relation definition. * @param listener * The configuration delete listener. * @throws IllegalArgumentException * If the instantiable relation definition is not * associated with this managed object's definition. * @throws ConfigException * If the configuration entry associated with the * instantiable relation could not be retrieved. */ public void registerDeleteListener( InstantiableRelationDefinition d, ConfigurationDeleteListener listener) throws IllegalArgumentException, ConfigException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d); ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor(path, d, listener); registerDeleteListener(baseDN, adaptor); } /** * Register to be notified when an existing child configuration is * deleted beneath an optional relation. * * @param * The type of the child server configuration object. * @param d * The optional relation definition. * @param listener * The configuration delete listener. * @throws IllegalArgumentException * If the optional relation definition is not associated * with this managed object's definition. * @throws ConfigException * If the configuration entry associated with the * optional relation could not be retrieved. */ public void registerDeleteListener( OptionalRelationDefinition d, ConfigurationDeleteListener listener) throws IllegalArgumentException, ConfigException { validateRelationDefinition(d); DN baseDN = DNBuilder.create(path, d).getParent(); ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor(path, d, listener); registerDeleteListener(baseDN, adaptor); } /** * Update the config entry associated with this server managed * object. This is only intended to be used by change listener call * backs in order to update the managed object with the correct * config entry. * * @param configEntry * The configuration entry. */ void setConfigEntry(ConfigEntry configEntry) { this.configEntry = configEntry; } // Deregister an add listener. private void deregisterAddListener(DN baseDN, ConfigurationAddListener listener) { try { ConfigEntry configEntry = getListenerConfigEntry(baseDN); if (configEntry != null) { for (ConfigAddListener l : configEntry.getAddListeners()) { if (l instanceof ConfigAddListenerAdaptor) { ConfigAddListenerAdaptor adaptor = (ConfigAddListenerAdaptor) l; if (adaptor.getConfigurationAddListener() == listener) { configEntry.deregisterAddListener(adaptor); } } } } } catch (ConfigException e) { // Ignore the exception since this implies deregistration. if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } } } // Deregister a delete listener. private void deregisterDeleteListener(DN baseDN, ConfigurationDeleteListener listener) { try { ConfigEntry configEntry = getListenerConfigEntry(baseDN); if (configEntry != null) { for (ConfigDeleteListener l : configEntry.getDeleteListeners()) { if (l instanceof ConfigDeleteListenerAdaptor) { ConfigDeleteListenerAdaptor adaptor = (ConfigDeleteListenerAdaptor) l; if (adaptor.getConfigurationDeleteListener() == listener) { configEntry.deregisterDeleteListener(adaptor); } } } } } catch (ConfigException e) { // Ignore the exception since this implies deregistration. if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } } } // Get a child managed object. private ServerManagedObject getChild( ManagedObjectPath childPath, RelationDefinition d) throws ConfigException { // Get the configuration entry. DN targetDN = DNBuilder.create(childPath); ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN); try { return decode(childPath, d.getChildDefinition(), configEntry); } catch (DefinitionDecodingException e) { throw ConfigExceptionFactory.getInstance() .createDecodingExceptionAdaptor(targetDN, e); } catch (ServerManagedObjectDecodingException e) { throw ConfigExceptionFactory.getInstance() .createDecodingExceptionAdaptor(e); } } // Gets a config entry required for a listener and throws a config // exception on failure or returns null if the entry does not exist. private ConfigEntry getListenerConfigEntry(DN dn) throws ConfigException { // Attempt to retrieve the listener base entry. ConfigEntry configEntry; try { configEntry = DirectoryServer.getConfigEntry(dn); } catch (ConfigException e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } int msgID = AdminMessages.MSGID_ADMIN_CANNOT_GET_LISTENER_BASE; String message = getMessage(msgID, String.valueOf(dn), stackTraceToSingleLineString(e)); throw new ConfigException(msgID, message, e); } return configEntry; } // Register an instantiable or optional relation add listener. private void registerAddListener(DN baseDN, ConfigAddListener adaptor) throws IllegalArgumentException, ConfigException { ConfigEntry relationEntry = getListenerConfigEntry(baseDN); if (relationEntry != null) { relationEntry.registerAddListener(adaptor); } else { // The relation entry does not exist yet so register a delayed // add listener. ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor); registerDelayedListener(baseDN, delayedListener); } } // Register a delayed listener with the nearest existing parent // entry to the provided base DN. private void registerDelayedListener(DN baseDN, ConfigAddListener delayedListener) throws ConfigException { DN parentDN = baseDN.getParent(); while (parentDN != null) { ConfigEntry relationEntry = getListenerConfigEntry(parentDN); if (relationEntry == null) { delayedListener = new DelayedConfigAddListener(parentDN, delayedListener); parentDN = parentDN.getParent(); } else { relationEntry.registerAddListener(delayedListener); return; } } // No parent entry could be found. int msgID = AdminMessages.MSGID_ADMIN_UNABLE_TO_REGISTER_LISTENER; String message = getMessage(msgID, String.valueOf(baseDN)); throw new ConfigException(msgID, message); } // Register an instantiable or optional relation delete listener. private void registerDeleteListener(DN baseDN, ConfigDeleteListener adaptor) throws ConfigException { ConfigEntry relationEntry = getListenerConfigEntry(baseDN); if (relationEntry != null) { relationEntry.registerDeleteListener(adaptor); } else { // The relation entry does not exist yet so register a delayed // add listener. ConfigAddListener delayedListener = new DelayedConfigAddListener(baseDN, adaptor); registerDelayedListener(baseDN, delayedListener); } } // Validate that a relation definition belongs to this managed // object. private void validateRelationDefinition(RelationDefinition rd) throws IllegalArgumentException { RelationDefinition tmp = definition.getRelationDefinition(rd.getName()); if (tmp != rd) { throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a " + definition.getName()); } } }