From 93240e26b8bb610e87ba12194f82cfe41fafcabc Mon Sep 17 00:00:00 2001
From: jcambon <jcambon@localhost>
Date: Wed, 18 Jun 2008 08:48:26 +0000
Subject: [PATCH] New top level object in RootConfiguration.xml : "extension", that allows to create a configuration object for an extension, that does not inherit from an existing configuration object.

---
 opends/resource/schema/02-config.ldif                                        |    8 
 opends/src/messages/messages/config.properties                               |    7 
 opends/src/server/org/opends/server/core/DirectoryServer.java                |   72 +++++
 opends/src/server/org/opends/server/api/Extension.java                       |  114 ++++++++
 opends/src/admin/defn/org/opends/server/admin/std/ExtensionConfiguration.xml |   90 ++++++
 opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml      |    6 
 opends/src/server/org/opends/server/core/ExtensionConfigManager.java         |  448 ++++++++++++++++++++++++++++++++++
 opends/resource/config/config.ldif                                           |    6 
 8 files changed, 751 insertions(+), 0 deletions(-)

diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 3b762ca..ecfb9f5 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -2220,3 +2220,9 @@
 ds-cfg-num-worker-threads: 24
 ds-cfg-max-work-queue-capacity: 0
 
+dn: cn=Extensions,cn=config
+objectClass: top
+objectClass: ds-cfg-branch
+objectClass: ds-cfg-plugin-root
+cn: Extensions
+
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 0934302..b4a5b9e 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -3737,3 +3737,11 @@
         ds-cfg-security-agent-file $
         ds-cfg-opendmk-jarfile )
   X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.182
+  NAME 'ds-cfg-extension'
+  SUP top
+  STRUCTURAL
+  MUST ( cn $
+         ds-cfg-java-class $
+         ds-cfg-enabled )
+  X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/ExtensionConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/ExtensionConfiguration.xml
new file mode 100644
index 0000000..b05c233
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/ExtensionConfiguration.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ! 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
+  !
+  !
+  !      Copyright 2007-2008 Sun Microsystems, Inc.
+  ! -->
+<adm:managed-object name="extension"
+  plural-name="extensions"
+  package="org.opends.server.admin.std"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap"
+  xmlns:cli="http://www.opends.org/admin-cli">
+  <adm:synopsis>
+    The
+    <adm:user-friendly-name />
+    allows to extend the configuration with new type of objects.
+  </adm:synopsis>
+  <adm:description>
+    It is an entry point for extensions that requires configuration objects
+    that does not inherit from an existing top-level object.
+  </adm:description>
+  <adm:tag name="core-server" />
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-extension</ldap:name>
+      <ldap:superior>top</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+  <adm:profile name="cli">
+    <cli:managed-object custom="true" />
+  </adm:profile>
+  <adm:property name="enabled" mandatory="true">
+    <adm:synopsis>
+      Indicates whether the
+      <adm:user-friendly-name />
+      is enabled.
+    </adm:synopsis>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-enabled</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="java-class" mandatory="true">
+    <adm:synopsis>
+      Specifies the fully-qualified name of the Java class that provides the
+      <adm:user-friendly-name />
+      implementation.
+    </adm:synopsis>
+	<adm:requires-admin-action>
+	  <adm:component-restart />
+	</adm:requires-admin-action>
+    <adm:syntax>
+      <adm:java-class>
+        <adm:instance-of>
+          org.opends.server.api.Extension
+        </adm:instance-of>
+      </adm:java-class>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-java-class</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+</adm:managed-object>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
index 3a5bbef..0f4f5aa 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -450,6 +450,12 @@
       </cli:relation>
     </adm:profile>
   </adm:relation>
+  <adm:relation name="extension">
+    <adm:one-to-many />
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence>cn=Extensions,cn=config</ldap:rdn-sequence>
+    </adm:profile>
+   </adm:relation>
   <adm:product-name>OpenDS Directory Server</adm:product-name>
   <adm:tag-definition name="logging">
     <adm:synopsis>Logging</adm:synopsis>
diff --git a/opends/src/messages/messages/config.properties b/opends/src/messages/messages/config.properties
index 960e3ef..38acbca 100644
--- a/opends/src/messages/messages/config.properties
+++ b/opends/src/messages/messages/config.properties
@@ -2138,3 +2138,10 @@
 SEVERE_ERR_CONFIG_LOGGING_INSANE_MODE_715=Invalid UNIX file permissions %s \
   does not allow write access to the log file by the log publisher
 SEVERE_ERR_CONFIG_LOGGING_MODE_INVALID_716=Invalid UNIX file permissions %s: %s
+MILD_ERR_CONFIG_EXTENSION_CONFIG_NOT_ACCEPTABLE_717=The configuration for \
+ the extension defined in configuration entry %s was not \
+ acceptable:  %s
+MILD_ERR_CONFIG_EXTENSION_INITIALIZATION_FAILED_718=An error occurred while \
+ trying to initialize an instance of class %s as an extension as \
+ defined in configuration entry %s:  %s
+
diff --git a/opends/src/server/org/opends/server/api/Extension.java b/opends/src/server/org/opends/server/api/Extension.java
new file mode 100644
index 0000000..72a46f8
--- /dev/null
+++ b/opends/src/server/org/opends/server/api/Extension.java
@@ -0,0 +1,114 @@
+/*
+ * 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
+ *
+ *
+ *      Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.api;
+
+import org.opends.messages.Message;
+
+import java.util.List;
+import org.opends.server.admin.std.server.ExtensionCfg;
+import org.opends.server.config.ConfigException;
+import org.opends.server.types.InitializationException;
+
+
+
+/**
+ * This class defines the set of methods and structures that must be
+ * implemented by a Directory Server extension.
+ *
+ * @param <T>
+ *          The type of extension configuration handled by
+ *          this extension implementation.
+ */
+@org.opends.server.types.PublicAPI(
+     stability=org.opends.server.types.StabilityLevel.VOLATILE,
+     mayInstantiate=false,
+     mayExtend=true,
+     mayInvoke=true)
+public abstract class Extension
+       <T extends ExtensionCfg>
+{
+  /**
+   * Initializes this extension based on the
+   * information in the provided extension configuration.
+   *
+   * @param configuration
+   *          The extension configuration that contains the
+   *          information to use to initialize this connection
+   *          handler.
+   * @throws ConfigException
+   *           If an unrecoverable problem arises in the process of
+   *           performing the initialization as a result of the server
+   *           configuration.
+   * @throws InitializationException
+   *           If a problem occurs during initialization that is not
+   *           related to the server configuration.
+   */
+  public abstract void initializeExtension(T configuration)
+      throws ConfigException, InitializationException;
+
+
+
+  /**
+   * Indicates whether the provided configuration is acceptable for
+   * this extension.  It should be possible to call this
+   * method on an uninitialized extension instance in order
+   * to determine whether the extension would be able to use
+   * the provided configuration.
+   * <BR><BR>
+   * Note that implementations which use a subclass of the provided
+   * configuration class will likely need to cast the configuration
+   * to the appropriate subclass type.
+   *
+   * @param  configuration        The extension configuration
+   *                              for which to make the determination.
+   * @param  unacceptableReasons  A list that may be used to hold the
+   *                              reasons that the provided
+   *                              configuration is not acceptable.
+   *
+   * @return  {@code true} if the provided configuration is acceptable
+   *          for this extension, or {@code false} if not.
+   */
+  public boolean isConfigurationAcceptable(
+                      ExtensionCfg configuration,
+                      List<Message> unacceptableReasons)
+  {
+    // This default implementation does not perform any special
+    // validation.  It should be overridden by extension
+    // implementations that wish to perform more detailed validation.
+    return true;
+  }
+
+
+
+  /**
+   * Performs any finalization that may be necessary for
+   * this extension.
+   *
+   */
+  public abstract void finalizeExtension();
+
+ }
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index efbf7f0..4d108ef 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -56,6 +56,7 @@
 import org.opends.server.api.ImportTaskListener;
 import org.opends.server.api.InvokableComponent;
 import org.opends.server.api.KeyManagerProvider;
+import org.opends.server.api.Extension;
 import org.opends.server.api.MatchingRule;
 import org.opends.server.api.MonitorProvider;
 import org.opends.server.api.OrderingMatchingRule;
@@ -400,6 +401,9 @@
   // The set of key manager providers registered with the server.
   private ConcurrentHashMap<DN,KeyManagerProvider> keyManagerProviders;
 
+  // The set of extensions registered with the server.
+  private ConcurrentHashMap<DN,Extension> extensions;
+
   // The set of password generators registered with the Directory Server, as a
   // mapping between the DN of the associated configuration entry and the
   // generator implementation.
@@ -577,6 +581,9 @@
   // The key manager provider configuration manager for the Directory Server.
   private KeyManagerProviderConfigManager keyManagerProviderConfigManager;
 
+  // The extension configuration manager for the Directory Server.
+  private ExtensionConfigManager extensionConfigManager;
+
   // The set of connections that are currently established.
   private LinkedHashSet<ClientConnection> establishedConnections;
 
@@ -896,6 +903,8 @@
       directoryServer.alternateRootBindDNs = new ConcurrentHashMap<DN,DN>();
       directoryServer.keyManagerProviders =
            new ConcurrentHashMap<DN,KeyManagerProvider>();
+      directoryServer.extensions =
+           new ConcurrentHashMap<DN,Extension>();
       directoryServer.trustManagerProviders =
            new ConcurrentHashMap<DN,TrustManagerProvider>();
       directoryServer.rotationPolicies =
@@ -1326,6 +1335,11 @@
       keyManagerProviderConfigManager.initializeKeyManagerProviders();
 
 
+      // Initialize the extension.
+      extensionConfigManager = new ExtensionConfigManager();
+      extensionConfigManager.initializeExtensions();
+
+
       // Initialize the trust manager provider.
       trustManagerProviderConfigManager =
            new TrustManagerProviderConfigManager();
@@ -7631,6 +7645,64 @@
 
 
   /**
+   * Retrieves the set of extensions registered with the Directory
+   * Server.
+   *
+   * @return  The set of extensions registered with the Directory
+   *          Server.
+   */
+  public static Map<DN,Extension> getExtensions()
+  {
+    return directoryServer.extensions;
+  }
+
+
+
+  /**
+   * Retrieves the extension registered with the provided entry DN.
+   *
+   * @param  providerDN  The DN with which the extension is
+   *                     registered.
+   *
+   * @return  The extension registered with the provided entry DN, or
+   *          {@code null} if there is no such extension registered
+   *          with the server.
+   */
+  public static Extension getExtension(DN providerDN)
+  {
+    return directoryServer.extensions.get(providerDN);
+  }
+
+
+
+  /**
+   * Registers the provided extension with the Directory Server.
+   *
+   * @param  providerDN  The DN with which to register the extension.
+   * @param  provider    The extension to register with the server.
+   */
+  public static void registerExtension(DN providerDN,
+                                                Extension provider)
+  {
+    directoryServer.extensions.put(providerDN, provider);
+  }
+
+
+
+  /**
+   * Deregisters the specified extension with the Directory Server.
+   *
+   * @param  providerDN  The DN with which the extension is
+   *                     registered.
+   */
+  public static void deregisterExtension(DN providerDN)
+  {
+    directoryServer.extensions.remove(providerDN);
+  }
+
+
+
+  /**
    * Retrieves a set containing the names of the allowed tasks that may be
    * invoked in the server.
    *
diff --git a/opends/src/server/org/opends/server/core/ExtensionConfigManager.java b/opends/src/server/org/opends/server/core/ExtensionConfigManager.java
new file mode 100644
index 0000000..745270c
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/ExtensionConfigManager.java
@@ -0,0 +1,448 @@
+/*
+ * 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
+ *
+ *
+ *      Copyright 2006-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+import org.opends.messages.Message;
+
+
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opends.server.admin.ClassPropertyDefinition;
+import org.opends.server.admin.server.ConfigurationAddListener;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ConfigurationDeleteListener;
+import org.opends.server.admin.std.meta.ExtensionCfgDefn;
+import org.opends.server.admin.std.server.ExtensionCfg;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.admin.server.ServerManagementContext;
+import org.opends.server.api.Extension;
+import org.opends.server.config.ConfigException;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+
+import static org.opends.messages.ConfigMessages.*;
+
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class defines a utility that will be used to manage the set of
+ * extensions defined in the Directory Server.  It will initialize the
+ * extensions when the server starts, and then will manage any
+ * additions, removals, or modifications to any extensions while
+ * the server is running.
+ */
+public class  ExtensionConfigManager
+       implements ConfigurationChangeListener<ExtensionCfg>,
+                  ConfigurationAddListener<ExtensionCfg>,
+                  ConfigurationDeleteListener<ExtensionCfg>
+
+{
+  // A mapping between the DNs of the config entries and the associated
+  // extensions.
+  private ConcurrentHashMap<DN,Extension> extensions;
+
+
+
+  /**
+   * Creates a new instance of this extension config manager.
+   */
+  public ExtensionConfigManager()
+  {
+    extensions = new ConcurrentHashMap<DN,Extension>();
+  }
+
+
+
+  /**
+   * Initializes all extensions currently defined in the Directory
+   * Server configuration.  This should only be called at Directory Server
+   * startup.
+   *
+   * @throws  ConfigException  If a configuration problem causes the
+   *                           extension initialization process to fail.
+   *
+   * @throws  InitializationException  If a problem occurs while initializing
+   *                                   the extensions that is not
+   *                                   related to the server configuration.
+   */
+  public void initializeExtensions()
+         throws ConfigException, InitializationException
+  {
+    // Get the root configuration object.
+    ServerManagementContext managementContext =
+         ServerManagementContext.getInstance();
+    RootCfg rootConfiguration =
+         managementContext.getRootConfiguration();
+
+
+    // Register as an add and delete listener with the root configuration so we
+    // can be notified if any extension entries are added or removed.
+    rootConfiguration.addExtensionAddListener(this);
+    rootConfiguration.addExtensionDeleteListener(this);
+
+
+    //Initialize the existing extensions.
+    for (String name : rootConfiguration.listExtensions())
+    {
+      ExtensionCfg extensionConfig =
+              rootConfiguration.getExtension(name);
+      extensionConfig.addChangeListener(this);
+
+      if (extensionConfig.isEnabled())
+      {
+        String className = extensionConfig.getJavaClass();
+        try
+        {
+          Extension extension =
+               loadExtension(className, extensionConfig, true);
+          extensions.put(extensionConfig.dn(), extension);
+          DirectoryServer.registerExtension(extensionConfig.dn(),
+                                                     extension);
+        }
+        catch (InitializationException ie)
+        {
+          logError(ie.getMessageObject());
+          continue;
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationAddAcceptable(
+          ExtensionCfg configuration,
+          List<Message> unacceptableReasons)
+  {
+    if (configuration.isEnabled())
+    {
+      // Get the name of the class and make sure we can instantiate it as a
+      // extension.
+      String className = configuration.getJavaClass();
+      try
+      {
+        loadExtension(className, configuration, false);
+      }
+      catch (InitializationException ie)
+      {
+        unacceptableReasons.add(ie.getMessageObject());
+        return false;
+      }
+    }
+
+    // If we've gotten here, then it's fine.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationAdd(
+          ExtensionCfg configuration)
+  {
+    ResultCode        resultCode          = ResultCode.SUCCESS;
+    boolean           adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+    configuration.addChangeListener(this);
+
+    if (! configuration.isEnabled())
+    {
+      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+    }
+
+    Extension extension = null;
+
+    // Get the name of the class and make sure we can instantiate it as an
+    // extension.
+    String className = configuration.getJavaClass();
+    try
+    {
+      extension = loadExtension(className, configuration, true);
+    }
+    catch (InitializationException ie)
+    {
+      if (resultCode == ResultCode.SUCCESS)
+      {
+        resultCode = DirectoryServer.getServerErrorResultCode();
+      }
+
+      messages.add(ie.getMessageObject());
+    }
+
+    if (resultCode == ResultCode.SUCCESS)
+    {
+      extensions.put(configuration.dn(), extension);
+      DirectoryServer.registerExtension(configuration.dn(), extension);
+    }
+
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationDeleteAcceptable(
+                      ExtensionCfg configuration,
+                      List<Message> unacceptableReasons)
+  {
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationDelete(
+                                 ExtensionCfg configuration)
+  {
+    ResultCode        resultCode          = ResultCode.SUCCESS;
+    boolean           adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+    DirectoryServer.deregisterExtension(configuration.dn());
+
+    Extension extension = extensions.remove(configuration.dn());
+    if (extension != null)
+    {
+      extension.finalizeExtension();
+    }
+
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+                      ExtensionCfg configuration,
+                      List<Message> unacceptableReasons)
+  {
+    if (configuration.isEnabled())
+    {
+      // Get the name of the class and make sure we can instantiate it as an
+      // extension.
+      String className = configuration.getJavaClass();
+      try
+      {
+        loadExtension(className, configuration, false);
+      }
+      catch (InitializationException ie)
+      {
+        unacceptableReasons.add(ie.getMessageObject());
+        return false;
+      }
+    }
+
+    // If we've gotten here, then it's fine.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+                                 ExtensionCfg configuration)
+  {
+    ResultCode        resultCode          = ResultCode.SUCCESS;
+    boolean           adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+
+    // Get the existing extension if it's already enabled.
+    Extension existingExtension = extensions.get(configuration.dn());
+
+
+    // If the new configuration has the extension disabled, then disable it if
+    // it is enabled, or do nothing if it's already disabled.
+    if (! configuration.isEnabled())
+    {
+      if (existingExtension != null)
+      {
+        DirectoryServer.deregisterExtension(configuration.dn());
+
+        Extension extension = extensions.remove(configuration.dn());
+        if (extension != null)
+        {
+          extension.finalizeExtension();
+        }
+      }
+
+      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+    }
+
+
+    // Get the class for the extension.  If the extension is already
+    // enabled, then we shouldn't do anything with it although if the class has
+    // changed then we'll at least need to indicate that administrative action
+    // is required.  If the extension is disabled, then instantiate the class
+    // and initialize and register it as a extension.
+    String className = configuration.getJavaClass();
+    if (existingExtension != null)
+    {
+      if (! className.equals(existingExtension.getClass().getName()))
+      {
+        adminActionRequired = true;
+      }
+
+      return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+    }
+
+    Extension extension = null;
+    try
+    {
+      extension = loadExtension(className, configuration, true);
+    }
+    catch (InitializationException ie)
+    {
+      if (resultCode == ResultCode.SUCCESS)
+      {
+        resultCode = DirectoryServer.getServerErrorResultCode();
+      }
+
+      messages.add(ie.getMessageObject());
+    }
+
+    if (resultCode == ResultCode.SUCCESS)
+    {
+      extensions.put(configuration.dn(), extension);
+      DirectoryServer.registerExtension(configuration.dn(), extension);
+    }
+
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
+
+
+
+  /**
+   * Loads the specified class, instantiates it as a extension, and
+   * optionally initializes that instance.
+   *
+   * @param  className      The fully-qualified name of the extension
+   *                        class to load, instantiate, and initialize.
+   * @param  configuration  The configuration to use to initialize the
+   *                        extension.  It must not be {@code null}.
+   * @param  initialize     Indicates whether the extension instance
+   *                        should be initialized.
+   *
+   * @return  The possibly initialized extension.
+   *
+   * @throws  InitializationException  If the provided configuration is not
+   *                                   acceptable, or if a problem occurred
+   *                                   while attempting to initialize the
+   *                                   extension using that
+   *                                   configuration.
+   */
+  private Extension loadExtension(String className,
+                                          ExtensionCfg configuration,
+                                          boolean initialize)
+          throws InitializationException
+  {
+    try
+    {
+      ExtensionCfgDefn definition =
+              ExtensionCfgDefn.getInstance();
+      ClassPropertyDefinition propertyDefinition =
+           definition.getJavaClassPropertyDefinition();
+      Class<? extends Extension> extensionClass =
+           propertyDefinition.loadClass(className, Extension.class);
+      Extension extension = extensionClass.newInstance();
+
+
+      if (initialize)
+      {
+        Method method = extension.getClass().getMethod(
+            "initializeExtension", configuration.configurationClass());
+        method.invoke(extension, configuration);
+      }
+      else
+      {
+        Method method =
+             extension.getClass().getMethod("isConfigurationAcceptable",
+                                           ExtensionCfg.class,
+                                           List.class);
+
+        List<Message> unacceptableReasons = new ArrayList<Message>();
+        Boolean acceptable = (Boolean) method.invoke(extension, configuration,
+                                                     unacceptableReasons);
+        if (! acceptable)
+        {
+          StringBuilder buffer = new StringBuilder();
+          if (! unacceptableReasons.isEmpty())
+          {
+            Iterator<Message> iterator = unacceptableReasons.iterator();
+            buffer.append(iterator.next());
+            while (iterator.hasNext())
+            {
+              buffer.append(".  ");
+              buffer.append(iterator.next());
+            }
+          }
+
+          Message message = ERR_CONFIG_EXTENSION_CONFIG_NOT_ACCEPTABLE.get(
+              String.valueOf(configuration.dn()), buffer.toString());
+          throw new InitializationException(message);
+        }
+      }
+
+      return extension;
+    }
+    catch (InitializationException ie)
+    {
+      throw ie;
+    }
+    catch (Exception e)
+    {
+      Message message = ERR_CONFIG_EXTENSION_INITIALIZATION_FAILED.
+          get(className, String.valueOf(configuration.dn()),
+              stackTraceToSingleLineString(e));
+      throw new InitializationException(message, e);
+    }
+  }
+}
+

--
Gitblit v1.10.0