mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noel Rouvignac
12.51.2013 4c82e8ee11f1a023135243fa75ee77dfe70f8289
Fixing a test failure in Continuous Integration.
See the test failure stack trace at the bottom of this commit message.
ECLReplicationServerPreTest(), the very first test of ExternalChangeLogTest, expects external changelog to be disabled.
However, when GroupIdHandshakeTest was run before ExternalChangeLogTest but never removed ECL virtual attributes.
This led to the "lastChangeNumber" virtual attribute having an actual value of "0" while it was expected it would not exist on the returned entry.

Moved the total responsibility of maintaining the virtual attributes to VirtualAttributeConfigManager.
Ensured the ECL virtual attributes are removed on ECL shutdown.




VirtualAttributeConfigManager.java:
Do not call anymore the DirectoryServer.(de)registerVirtualAttribute() methods.
Added getVirtualAttributes(), register(), deregister(), getDummyDN().
In loadProvider(), removed the use of reflection.

DirectoryServer.java:
Removed virtualAttributes field.
Delegated getVirtualAttributes(), registerVirtualAttribute(), deregisterVirtualAttribute() calls to VirtualAttributeConfigManager.
Removed replaceVirtualAttribute().

ReplicationServer.java:
Renamed externalChangeLogWorkflowID field to eclWorkflowID and externalChangeLogWorkflowImpl field to eclWorkflowImpl.
Changed eclWorkflowImpl to use an AtomicReference.
In enableECL(), used AotmicReference.compareAndSet() to avoid multiple threads initializing at the same time.
Renamed enableECLVirtualAttr() to buildVirtualAttributeRule() + extracted the call to DirectoryServer.registerVirtualAttribute() outside.
In buildVirtualAttributeRule(), used Collections.* methods.
In shutdownECL(), deregister the virtual attributes added by the enableECL().





[testng] T E S T F A I L U R E ! ! !
[testng]
[testng] Failed Test: org.opends.server.replication.server.ExternalChangeLogTest#ECLReplicationServerPreTest
[testng] Failure Cause: java.lang.AssertionError: expected:<null> but was:<0>
[testng] org.testng.Assert.fail(Assert.java:84)
[testng] org.testng.Assert.failNotEquals(Assert.java:438)
[testng] org.testng.Assert.assertEquals(Assert.java:108)
[testng] org.testng.Assert.assertEquals(Assert.java:129)
[testng] org.testng.Assert.assertEquals(Assert.java:139)
[testng] org.opends.server.replication.server.ExternalChangeLogTest.ECLCompatTestLimits(ExternalChangeLogTest.java:2560)
[testng] org.opends.server.replication.server.ExternalChangeLogTest.ECLIsNotASupportedSuffix(ExternalChangeLogTest.java:407)
[testng] org.opends.server.replication.server.ExternalChangeLogTest.ECLReplicationServerPreTest(ExternalChangeLogTest.java:167)
3 files modified
313 ■■■■■ changed files
opends/src/server/org/opends/server/core/DirectoryServer.java 63 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/VirtualAttributeConfigManager.java 123 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/server/ReplicationServer.java 127 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -446,9 +446,6 @@
  private List<SynchronizationProvider<SynchronizationProviderCfg>>
               synchronizationProviders;
  /** The set of virtual attributes defined in the server. */
  private List<VirtualAttributeRule> virtualAttributes;
  /**
   * The set of backend initialization listeners registered with the Directory
   * Server.
@@ -918,8 +915,6 @@
      directoryServer.supportedFeatures = new TreeSet<String>();
      directoryServer.supportedLDAPVersions =
           new ConcurrentHashMap<Integer,List<ConnectionHandler>>();
      directoryServer.virtualAttributes =
           new CopyOnWriteArrayList<VirtualAttributeRule>();
      directoryServer.connectionHandlers =
           new CopyOnWriteArrayList<ConnectionHandler>();
      directoryServer.identityMappers =
@@ -4394,9 +4389,9 @@
   * @return  The set of virtual attribute rules registered with the Directory
   *          Server.
   */
  public static List<VirtualAttributeRule> getVirtualAttributes()
  public static Collection<VirtualAttributeRule> getVirtualAttributes()
  {
    return directoryServer.virtualAttributes;
    return directoryServer.virtualAttributeConfigManager.getVirtualAttributes();
  }
@@ -4417,7 +4412,7 @@
    List<VirtualAttributeRule> ruleList =
        new LinkedList<VirtualAttributeRule>();
    for (VirtualAttributeRule rule : directoryServer.virtualAttributes)
    for (VirtualAttributeRule rule : getVirtualAttributes())
    {
      if (rule.appliesToEntry(entry))
      {
@@ -4428,68 +4423,24 @@
    return ruleList;
  }
  /**
   * Registers the provided virtual attribute rule with the Directory Server.
   *
   * @param  rule  The virtual attribute rule to be registered.
   */
  public static void registerVirtualAttribute(VirtualAttributeRule rule)
  public static void registerVirtualAttribute(final VirtualAttributeRule rule)
  {
    synchronized (directoryServer.virtualAttributes)
    {
      directoryServer.virtualAttributes.add(rule);
    }
    getInstance().virtualAttributeConfigManager.register(rule);
  }
  /**
   * Deregisters the provided virtual attribute rule with the Directory Server.
   *
   * @param  rule  The virutal attribute rule to be deregistered.
   * @param  rule  The virtual attribute rule to be deregistered.
   */
  public static void deregisterVirtualAttribute(VirtualAttributeRule rule)
  {
    synchronized (directoryServer.virtualAttributes)
    {
      directoryServer.virtualAttributes.remove(rule);
    }
  }
  /**
   * Replaces the specified virtual attribute rule in the set of virtual
   * attributes registered with the Directory Server.  If the old rule cannot
   * be found in the list, then the set of registered virtual attributes is not
   * updated.
   *
   * @param  oldRule  The existing rule that should be replaced with the new
   *                  rule.
   * @param  newRule  The new rule that should be used in place of the existing
   *                  rule.
   *
   * @return  {@code true} if the old rule was found and replaced with the new
   *          version, or {@code false} if it was not.
   */
  public static boolean replaceVirtualAttribute(VirtualAttributeRule oldRule,
                                                VirtualAttributeRule newRule)
  {
    synchronized (directoryServer.virtualAttributes)
    {
      int pos = directoryServer.virtualAttributes.indexOf(oldRule);
      if (pos >= 0)
      {
        directoryServer.virtualAttributes.set(pos, newRule);
        return true;
      }
      else
      {
        return false;
      }
    }
    getInstance().virtualAttributeConfigManager.deregister(rule);
  }
opends/src/server/org/opends/server/core/VirtualAttributeConfigManager.java
@@ -27,7 +27,6 @@
 */
package org.opends.server.core;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
@@ -104,8 +103,7 @@
    // Get the root configuration object.
    ServerManagementContext managementContext =
         ServerManagementContext.getInstance();
    RootCfg rootConfiguration =
         managementContext.getRootConfiguration();
    RootCfg rootConfiguration = managementContext.getRootConfiguration();
    // Register as an add and delete listener with the root configuration so we
@@ -162,7 +160,6 @@
          VirtualAttributeRule rule = createRule(cfg, provider, filters);
          rules.put(cfg.dn(), rule);
          DirectoryServer.registerVirtualAttribute(rule);
        }
        catch (InitializationException ie)
        {
@@ -185,10 +182,7 @@
           cfg.getConflictBehavior());
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationAddAcceptable(
                      VirtualAttributeCfg configuration,
@@ -242,11 +236,7 @@
    return filters;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationAdd(
                                 VirtualAttributeCfg configuration)
@@ -296,17 +286,12 @@
    {
      VirtualAttributeRule rule = createRule(configuration, provider, filters);
      rules.put(configuration.dn(), rule);
      DirectoryServer.registerVirtualAttribute(rule);
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationDeleteAcceptable(
                      VirtualAttributeCfg configuration,
@@ -316,11 +301,7 @@
    return true;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationDelete(
                                 VirtualAttributeCfg configuration)
@@ -332,18 +313,13 @@
    VirtualAttributeRule rule = rules.remove(configuration.dn());
    if (rule != null)
    {
      DirectoryServer.deregisterVirtualAttribute(rule);
      rule.getProvider().finalizeVirtualAttributeProvider();
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public boolean isConfigurationChangeAcceptable(
                      VirtualAttributeCfg configuration,
@@ -384,11 +360,7 @@
    return true;
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public ConfigChangeResult applyConfigurationChange(
                                 VirtualAttributeCfg configuration)
@@ -409,7 +381,6 @@
      if (existingRule != null)
      {
        rules.remove(configuration.dn());
        DirectoryServer.deregisterVirtualAttribute(existingRule);
        existingRule.getProvider().finalizeVirtualAttributeProvider();
      }
@@ -450,15 +421,9 @@
    if (resultCode == ResultCode.SUCCESS)
    {
      VirtualAttributeRule rule = createRule(configuration, provider, filters);
      rules.put(configuration.dn(), rule);
      if (existingRule == null)
      if (existingRule != null)
      {
        DirectoryServer.registerVirtualAttribute(rule);
      }
      else
      {
        DirectoryServer.replaceVirtualAttribute(existingRule, rule);
        existingRule.getProvider().finalizeVirtualAttributeProvider();
      }
    }
@@ -474,7 +439,7 @@
   *
   * @param  className      The fully-qualified name of the certificate mapper
   *                        class to load, instantiate, and initialize.
   * @param  configuration  The configuration to use to initialize the
   * @param  cfg            The configuration to use to initialize the
   *                        virtual attribute provider.  It must not be
   *                        {@code null}.
   * @param  initialize     Indicates whether the virtual attribute provider
@@ -485,8 +450,9 @@
   * @throws  InitializationException  If a problem occurred while attempting to
   *                                   initialize the certificate mapper.
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  private VirtualAttributeProvider<? extends VirtualAttributeCfg>
               loadProvider(String className, VirtualAttributeCfg configuration,
               loadProvider(String className, VirtualAttributeCfg cfg,
                            boolean initialize)
          throws InitializationException
  {
@@ -499,31 +465,20 @@
      Class<? extends VirtualAttributeProvider> providerClass =
           propertyDefinition.loadClass(className,
                                        VirtualAttributeProvider.class);
      VirtualAttributeProvider<? extends VirtualAttributeCfg> provider =
           providerClass.newInstance();
      VirtualAttributeProvider provider = providerClass.newInstance();
      if (initialize)
      {
        Method method = provider.getClass().getMethod(
            "initializeVirtualAttributeProvider",
            configuration.configurationClass());
        method.invoke(provider, configuration);
        provider.initializeVirtualAttributeProvider(cfg);
      }
      else
      {
        Method method =
             provider.getClass().getMethod("isConfigurationAcceptable",
                                           VirtualAttributeCfg.class,
                                           List.class);
        List<Message> unacceptableReasons = new ArrayList<Message>();
        Boolean acceptable = (Boolean) method.invoke(provider, configuration,
                                                     unacceptableReasons);
        if (! acceptable)
        if (!provider.isConfigurationAcceptable(cfg, unacceptableReasons))
        {
          String reasons = collectionToString(unacceptableReasons, ".  ");
          Message message = ERR_CONFIG_VATTR_CONFIG_NOT_ACCEPTABLE.get(
              String.valueOf(configuration.dn()), reasons);
              String.valueOf(cfg.dn()), reasons);
          throw new InitializationException(message);
        }
      }
@@ -533,9 +488,55 @@
    catch (Exception e)
    {
      Message message = ERR_CONFIG_VATTR_INITIALIZATION_FAILED.
          get(className, String.valueOf(configuration.dn()),
          get(className, String.valueOf(cfg.dn()),
              stackTraceToSingleLineString(e));
      throw new InitializationException(message, e);
    }
  }
  /**
   * Retrieves the collection of registered virtual attribute rules.
   *
   * @return The collection of registered virtual attribute rules.
   */
  public Collection<VirtualAttributeRule> getVirtualAttributes()
  {
    return this.rules.values();
  }
  /**
   * Registers the provided virtual attribute rule.
   *
   * @param rule
   *          The virtual attribute rule to be registered.
   */
  public void register(VirtualAttributeRule rule)
  {
    rules.put(getDummyDN(rule), rule);
  }
  /**
   * Deregisters the provided virtual attribute rule.
   *
   * @param rule
   *          The virtual attribute rule to be deregistered.
   */
  public void deregister(VirtualAttributeRule rule)
  {
    rules.remove(getDummyDN(rule));
  }
  private DN getDummyDN(VirtualAttributeRule rule)
  {
    try
    {
      String name = rule.getAttributeType().getNameOrOID();
      return DN.decode("cn=" + name + ",cn=Virtual Attributes,cn=config");
    }
    catch (DirectoryException e)
    {
      // should never happen
      throw new RuntimeException(e);
    }
  }
}
opends/src/server/org/opends/server/replication/server/ReplicationServer.java
@@ -32,13 +32,13 @@
import java.net.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicReference;
import org.opends.messages.Category;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.messages.Severity;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn.*;
import org.opends.server.admin.std.server.ReplicationServerCfg;
import org.opends.server.admin.std.server.UserDefinedVirtualAttributeCfg;
@@ -111,10 +111,11 @@
   */
  private static final DebugTracer TRACER = getTracer();
  private static String externalChangeLogWorkflowID =
  private static String eclWorkflowID =
    "External Changelog Workflow ID";
  private ECLWorkflowElement eclwe;
  private WorkflowImpl externalChangeLogWorkflowImpl = null;
  private AtomicReference<WorkflowImpl> eclWorkflowImpl =
      new AtomicReference<WorkflowImpl>();
  /**
   * This is required for unit testing, so that we can keep track of all the
@@ -428,7 +429,7 @@
      // Creates the ECL workflow elem so that DS (LDAPReplicationDomain)
      // can know me and really enableECL.
      if (WorkflowImpl.getWorkflow(externalChangeLogWorkflowID) != null)
      if (WorkflowImpl.getWorkflow(eclWorkflowID) != null)
      {
        // Already done. Nothing to do
        return;
@@ -461,54 +462,54 @@
   */
  public void enableECL() throws DirectoryException
  {
    if (externalChangeLogWorkflowImpl!=null)
    if (eclWorkflowImpl.get() != null)
    {
      // do nothing if ECL is already enabled
      // ECL is already enabled, do nothing
      return;
    }
    // Create the workflow for the base DN and register the workflow with
    // the server.
    externalChangeLogWorkflowImpl = new WorkflowImpl(
        externalChangeLogWorkflowID,
        DN.decode(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT),
        eclwe.getWorkflowElementID(),
        eclwe);
    externalChangeLogWorkflowImpl.register();
    // Create the workflow for the base DN
    // and register the workflow with the server.
    final DN dn = DN.decode(ServerConstants.DN_EXTERNAL_CHANGELOG_ROOT);
    final WorkflowImpl workflowImpl = new WorkflowImpl(eclWorkflowID, dn,
        eclwe.getWorkflowElementID(), eclwe);
    if (!eclWorkflowImpl.compareAndSet(null, workflowImpl))
    {
      // ECL is being enabled, do nothing
      return;
    }
    NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup();
    defaultNetworkGroup.registerWorkflow(externalChangeLogWorkflowImpl);
    workflowImpl.register();
    // FIXME:ECL should the ECL Workflow be registered in adminNetworkGroup?
    NetworkGroup adminNetworkGroup = NetworkGroup.getAdminNetworkGroup();
    adminNetworkGroup.registerWorkflow(externalChangeLogWorkflowImpl);
    NetworkGroup.getDefaultNetworkGroup().registerWorkflow(workflowImpl);
    // FIXME:ECL should the ECL Workflow be registered in internalNetworkGroup?
    NetworkGroup internalNetworkGroup = NetworkGroup.getInternalNetworkGroup();
    internalNetworkGroup.registerWorkflow(externalChangeLogWorkflowImpl);
    // FIXME:ECL should the ECL Workflow be registered in admin and internal
    // network groups?
    NetworkGroup.getAdminNetworkGroup().registerWorkflow(workflowImpl);
    NetworkGroup.getInternalNetworkGroup().registerWorkflow(workflowImpl);
    enableECLVirtualAttr("lastexternalchangelogcookie",
        new LastCookieVirtualProvider());
    enableECLVirtualAttr("firstchangenumber",
        new FirstChangeNumberVirtualAttributeProvider());
    enableECLVirtualAttr("lastchangenumber",
        new LastChangeNumberVirtualAttributeProvider());
    enableECLVirtualAttr("changelog",
        new ChangelogBaseDNVirtualAttributeProvider());
    DirectoryServer.registerVirtualAttribute(buildVirtualAttributeRule(
        "lastexternalchangelogcookie", new LastCookieVirtualProvider()));
    DirectoryServer.registerVirtualAttribute(buildVirtualAttributeRule(
        "firstchangenumber", new FirstChangeNumberVirtualAttributeProvider()));
    DirectoryServer.registerVirtualAttribute(buildVirtualAttributeRule(
        "lastchangenumber", new LastChangeNumberVirtualAttributeProvider()));
    DirectoryServer.registerVirtualAttribute(buildVirtualAttributeRule(
        "changelog", new ChangelogBaseDNVirtualAttributeProvider()));
  }
  private static void enableECLVirtualAttr(String attrName,
  private static VirtualAttributeRule buildVirtualAttributeRule(String attrName,
      VirtualAttributeProvider<UserDefinedVirtualAttributeCfg> provider)
  throws DirectoryException
      throws DirectoryException
  {
    Set<DN> baseDNs = new HashSet<DN>(0);
    Set<DN> groupDNs = new HashSet<DN>(0);
    Set<SearchFilter> filters = new HashSet<SearchFilter>(0);
    VirtualAttributeCfgDefn.ConflictBehavior conflictBehavior =
      ConflictBehavior.VIRTUAL_OVERRIDES_REAL;
    ConflictBehavior conflictBehavior = ConflictBehavior.VIRTUAL_OVERRIDES_REAL;
    try
    {
      Set<DN> baseDNs = Collections.singleton(DN.decode(""));
      Set<DN> groupDNs = Collections.emptySet();
      Set<SearchFilter> filters = Collections.singleton(
          SearchFilter.createFilterFromString("(objectclass=*)"));
      // To avoid the configuration in cn=config just
      // create a rule and register it into the DirectoryServer
@@ -517,17 +518,9 @@
      AttributeType attributeType = DirectoryServer.getAttributeType(
          attrName, false);
      SearchFilter filter =
        SearchFilter.createFilterFromString("objectclass=*");
      filters.add(filter);
      baseDNs.add(DN.decode(""));
      VirtualAttributeRule rule =
        new VirtualAttributeRule(attributeType, provider,
              baseDNs, SearchScope.BASE_OBJECT,
              groupDNs, filters, conflictBehavior);
      DirectoryServer.registerVirtualAttribute(rule);
      return new VirtualAttributeRule(attributeType, provider,
            baseDNs, SearchScope.BASE_OBJECT,
            groupDNs, filters, conflictBehavior);
    }
    catch (Exception e)
    {
@@ -539,25 +532,35 @@
  private void shutdownECL()
  {
    WorkflowImpl eclwf = (WorkflowImpl) WorkflowImpl
        .getWorkflow(externalChangeLogWorkflowID);
    WorkflowImpl eclwf = (WorkflowImpl) WorkflowImpl.getWorkflow(eclWorkflowID);
    // do it only if not already done by another RS (unit test case)
    // if (DirectoryServer.getWorkflowElement(externalChangeLogWorkflowID)
    if (eclwf != null)
    {
      // FIXME:ECL should the ECL Workflow be registered in
      // internalNetworkGroup?
      NetworkGroup internalNetworkGroup = NetworkGroup
          .getInternalNetworkGroup();
      internalNetworkGroup.deregisterWorkflow(externalChangeLogWorkflowID);
      // FIXME:ECL should the ECL Workflow be registered in admin and internal
      // network groups?
      NetworkGroup.getInternalNetworkGroup().deregisterWorkflow(eclWorkflowID);
      NetworkGroup.getAdminNetworkGroup().deregisterWorkflow(eclWorkflowID);
      // FIXME:ECL should the ECL Workflow be registered in adminNetworkGroup?
      NetworkGroup adminNetworkGroup = NetworkGroup.getAdminNetworkGroup();
      adminNetworkGroup.deregisterWorkflow(externalChangeLogWorkflowID);
      NetworkGroup.getDefaultNetworkGroup().deregisterWorkflow(eclWorkflowID);
      NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup();
      defaultNetworkGroup.deregisterWorkflow(externalChangeLogWorkflowID);
      try
      {
        DirectoryServer.deregisterVirtualAttribute(buildVirtualAttributeRule(
            "lastexternalchangelogcookie", new LastCookieVirtualProvider()));
        DirectoryServer.deregisterVirtualAttribute(buildVirtualAttributeRule(
            "firstchangenumber",
            new FirstChangeNumberVirtualAttributeProvider()));
        DirectoryServer.deregisterVirtualAttribute(buildVirtualAttributeRule(
            "lastchangenumber",
            new LastChangeNumberVirtualAttributeProvider()));
        DirectoryServer.deregisterVirtualAttribute(buildVirtualAttributeRule(
            "changelog", new ChangelogBaseDNVirtualAttributeProvider()));
      }
      catch (DirectoryException e)
      {
        // Should never happen
        throw new RuntimeException(e);
      }
      eclwf.deregister();
      eclwf.finalizeWorkflow();