From 7c30dbb5403772b323df3ad907d9ed15d23b5aee Mon Sep 17 00:00:00 2001
From: ludovicp <ludovicp@localhost>
Date: Thu, 29 Apr 2010 20:35:40 +0000
Subject: [PATCH] Last batch of changes for this week. This adds support for the IETF based Password Policy for LDAP as SubEntry. Also resolves the following issues : - 4544 : initializeBackend() should not set JE env config params directly. - 4478 : ECL in draft compat mode / search lastchangenumber can be very long - 4538 : Virtual attributes not retrieved when entry cache configured - 4547 : Search Filter Matching differ for cn=Directory Manager and plain user. - 4514 : Logs shows unexpected message with replication monitoring data missing (Partial fix) - 4534 : Replication using security does not work after server restart - 4516 : SEVERE_ERROR: servers (...) have the same ServerId In addition, they also improve reliability and performance in various areas including CollectiveAttributes, Virtual Attributes and Subentries management, Schema loading, Replication...
---
opends/src/server/org/opends/server/core/SubentryManager.java | 482 ++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 416 insertions(+), 66 deletions(-)
diff --git a/opends/src/server/org/opends/server/core/SubentryManager.java b/opends/src/server/org/opends/server/core/SubentryManager.java
index 008abcf..348d921 100644
--- a/opends/src/server/org/opends/server/core/SubentryManager.java
+++ b/opends/src/server/org/opends/server/core/SubentryManager.java
@@ -22,18 +22,24 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
-import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.SubentryChangeListener;
+import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
+import org.opends.server.api.plugin.PluginResult;
+import org.opends.server.api.plugin.PluginResult.PostOperation;
+import org.opends.server.api.plugin.PluginResult.PreOperation;
+import org.opends.server.api.plugin.PluginType;
import org.opends.server.controls.SubentriesControl;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.internal.InternalClientConnection;
@@ -48,10 +54,14 @@
import org.opends.server.types.SearchScope;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SubEntry;
-import org.opends.server.types.operation.PostResponseAddOperation;
-import org.opends.server.types.operation.PostResponseDeleteOperation;
-import org.opends.server.types.operation.PostResponseModifyOperation;
-import org.opends.server.types.operation.PostResponseModifyDNOperation;
+import org.opends.server.types.operation.PostOperationAddOperation;
+import org.opends.server.types.operation.PostOperationDeleteOperation;
+import org.opends.server.types.operation.PostOperationModifyDNOperation;
+import org.opends.server.types.operation.PostOperationModifyOperation;
+import org.opends.server.types.operation.PreOperationAddOperation;
+import org.opends.server.types.operation.PreOperationDeleteOperation;
+import org.opends.server.types.operation.PreOperationModifyDNOperation;
+import org.opends.server.types.operation.PreOperationModifyOperation;
import org.opends.server.workflowelement.localbackend.
LocalBackendSearchOperation;
@@ -73,8 +83,8 @@
* memory. If it is determined that this approach is not workable
* in all cases, then we will need an alternate strategy.
*/
-public class SubentryManager
- implements BackendInitializationListener, ChangeNotificationListener
+public class SubentryManager extends InternalDirectoryServerPlugin
+ implements BackendInitializationListener
{
/**
* The tracer object for the debug logger.
@@ -93,22 +103,87 @@
// Lock to protect internal data structures.
private final ReentrantReadWriteLock lock;
+ // The set of change notification listeners.
+ private CopyOnWriteArrayList<SubentryChangeListener>
+ changeListeners;
+
+ // Dummy configuration DN for Subentry Manager.
+ private static final String CONFIG_DN = "cn=Subentry Manager,cn=config";
+
/**
- * Creates a new instance of this group manager.
+ * Creates a new instance of this subentry manager.
+ *
+ * @throws DirectoryException If a problem occurs while
+ * creating an instance of
+ * the subentry manager.
*/
- public SubentryManager()
+ public SubentryManager() throws DirectoryException
{
+ super(DN.decode(CONFIG_DN), EnumSet.of(
+ PluginType.PRE_OPERATION_ADD,
+ PluginType.PRE_OPERATION_DELETE,
+ PluginType.PRE_OPERATION_MODIFY,
+ PluginType.PRE_OPERATION_MODIFY_DN,
+ PluginType.POST_OPERATION_ADD,
+ PluginType.POST_OPERATION_DELETE,
+ PluginType.POST_OPERATION_MODIFY,
+ PluginType.POST_OPERATION_MODIFY_DN),
+ true);
+
lock = new ReentrantReadWriteLock();
dn2SubEntry = new HashMap<DN,List<SubEntry>>();
dn2CollectiveSubEntry = new HashMap<DN,List<SubEntry>>();
- requestAttrs = new LinkedHashSet<String>();
- requestAttrs.add("subtreespecification");
- requestAttrs.add("*");
+ changeListeners =
+ new CopyOnWriteArrayList<SubentryChangeListener>();
+ requestAttrs = new LinkedHashSet<String>();
+ requestAttrs.add("*");
+ requestAttrs.add("+");
+
+ DirectoryServer.registerInternalPlugin(this);
DirectoryServer.registerBackendInitializationListener(this);
- DirectoryServer.registerChangeNotificationListener(this);
+ }
+
+ /**
+ * Perform any required finalization tasks for Subentry Manager.
+ * This should only be called at Directory Server shutdown.
+ */
+ public void finalizeSubentryManager()
+ {
+ // Deregister as internal plugin and
+ // backend initialization listener.
+ DirectoryServer.deregisterInternalPlugin(this);
+ DirectoryServer.deregisterBackendInitializationListener(this);
+ }
+
+ /**
+ * Registers the provided change notification listener with this manager
+ * so that it will be notified of any add, delete, modify, or modify DN
+ * operations that are performed.
+ *
+ * @param changeListener The change notification listener to register
+ * with this manager.
+ */
+ public void registerChangeListener(
+ SubentryChangeListener changeListener)
+ {
+ changeListeners.add(changeListener);
+ }
+
+ /**
+ * Deregisters the provided change notification listener with this manager
+ * so that it will no longer be notified of any add, delete, modify, or
+ * modify DN operations that are performed.
+ *
+ * @param changeListener The change notification listener to deregister
+ * with this manager.
+ */
+ public void deregisterChangeListener(
+ SubentryChangeListener changeListener)
+ {
+ changeListeners.remove(changeListener);
}
/**
@@ -163,45 +238,51 @@
try
{
boolean removed = false;
- Iterator<Map.Entry<DN, List<SubEntry>>> iterator =
+ Iterator<Map.Entry<DN, List<SubEntry>>> setIterator =
dn2SubEntry.entrySet().iterator();
- while (iterator.hasNext())
+ while (setIterator.hasNext())
{
- Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next();
List<SubEntry> subList = mapEntry.getValue();
- for (SubEntry subEntry : subList)
+ Iterator<SubEntry> listIterator = subList.iterator();
+ while (listIterator.hasNext())
{
+ SubEntry subEntry = listIterator.next();
if (subEntry.getDN().equals(entry.getDN()))
{
- removed = subList.remove(subEntry);
+ listIterator.remove();
+ removed = true;
break;
}
}
if (subList.isEmpty())
{
- iterator.remove();
+ setIterator.remove();
}
if (removed)
{
return;
}
}
- iterator = dn2CollectiveSubEntry.entrySet().iterator();
- while (iterator.hasNext())
+ setIterator = dn2CollectiveSubEntry.entrySet().iterator();
+ while (setIterator.hasNext())
{
- Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next();
List<SubEntry> subList = mapEntry.getValue();
- for (SubEntry subEntry : subList)
+ Iterator<SubEntry> listIterator = subList.iterator();
+ while (listIterator.hasNext())
{
+ SubEntry subEntry = listIterator.next();
if (subEntry.getDN().equals(entry.getDN()))
{
- removed = subList.remove(subEntry);
+ listIterator.remove();
+ removed = true;
break;
}
}
if (subList.isEmpty())
{
- iterator.remove();
+ setIterator.remove();
}
if (removed)
{
@@ -230,8 +311,10 @@
SearchFilter filter = null;
try
{
- filter = SearchFilter.createFilterFromString("(" +
- ATTR_OBJECTCLASS + "=" + OC_SUBENTRY + ")");
+ filter = SearchFilter.createFilterFromString("(|" +
+ "(" + ATTR_OBJECTCLASS + "=" + OC_SUBENTRY + ")" +
+ "(" + ATTR_OBJECTCLASS + "=" + OC_LDAP_SUBENTRY + ")" +
+ ")");
if (backend.getEntryCount() > 0 && ! backend.isIndexed(filter))
{
logError(WARN_SUBENTRY_FILTER_NOT_INDEXED.get(
@@ -292,7 +375,7 @@
for (SearchResultEntry entry : internalSearch.getSearchEntries())
{
- if (entry.isSubentry())
+ if (entry.isSubentry() || entry.isLDAPSubentry())
{
try
{
@@ -314,6 +397,37 @@
}
/**
+ * Return all subentries for this manager.
+ * Note that this getter will skip any collective subentries,
+ * returning only applicable regular subentries.
+ * @return all subentries for this manager.
+ */
+ public List<SubEntry> getSubentries()
+ {
+ if (dn2SubEntry.isEmpty())
+ {
+ return Collections.emptyList();
+ }
+
+ List<SubEntry> subentries = new ArrayList<SubEntry>();
+
+ lock.readLock().lock();
+ try
+ {
+ for (List<SubEntry> subList : dn2SubEntry.values())
+ {
+ subentries.addAll(subList);
+ }
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
+
+ return subentries;
+ }
+
+ /**
* Return subentries applicable to specific DN.
* Note that this getter will skip any collective subentries,
* returning only applicable regular subentries.
@@ -506,39 +620,43 @@
lock.writeLock().lock();
try
{
- Iterator<Map.Entry<DN, List<SubEntry>>> iterator =
+ Iterator<Map.Entry<DN, List<SubEntry>>> setIterator =
dn2SubEntry.entrySet().iterator();
- while (iterator.hasNext())
+ while (setIterator.hasNext())
{
- Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next();
List<SubEntry> subList = mapEntry.getValue();
- for (SubEntry subEntry : subList)
+ Iterator<SubEntry> listIterator = subList.iterator();
+ while (listIterator.hasNext())
{
+ SubEntry subEntry = listIterator.next();
if (backend.handlesEntry(subEntry.getDN()))
{
- subList.remove(subEntry);
+ listIterator.remove();
}
}
if (subList.isEmpty())
{
- iterator.remove();
+ setIterator.remove();
}
}
- iterator = dn2CollectiveSubEntry.entrySet().iterator();
- while (iterator.hasNext())
+ setIterator = dn2CollectiveSubEntry.entrySet().iterator();
+ while (setIterator.hasNext())
{
- Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ Map.Entry<DN, List<SubEntry>> mapEntry = setIterator.next();
List<SubEntry> subList = mapEntry.getValue();
- for (SubEntry subEntry : subList)
+ Iterator<SubEntry> listIterator = subList.iterator();
+ while (listIterator.hasNext())
{
+ SubEntry subEntry = listIterator.next();
if (backend.handlesEntry(subEntry.getDN()))
{
- subList.remove(subEntry);
+ listIterator.remove();
}
}
if (subList.isEmpty())
{
- iterator.remove();
+ setIterator.remove();
}
}
}
@@ -549,17 +667,177 @@
}
/**
- * {@inheritDoc} In this case, each entry is checked to see if it is
- * a subentry, and if so it will be registered with this manager.
+ * {@inheritDoc}
*/
- public void handleAddOperation(PostResponseAddOperation addOperation,
- Entry entry)
+ @Override
+ public PreOperation doPreOperation(
+ PreOperationAddOperation addOperation)
{
- if (entry.isSubentry())
+ Entry entry = addOperation.getEntryToAdd();
+
+ if (entry.isSubentry() || entry.isLDAPSubentry())
+ {
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.checkSubentryAddAcceptable(entry);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ return PluginResult.PreOperation.stopProcessing(
+ de.getResultCode(), de.getMessageObject());
+ }
+ }
+ }
+
+ return PluginResult.PreOperation.continueOperationProcessing();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PreOperation doPreOperation(
+ PreOperationDeleteOperation deleteOperation)
+ {
+ Entry entry = deleteOperation.getEntryToDelete();
+
+ if (entry.isSubentry() || entry.isLDAPSubentry())
+ {
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.checkSubentryDeleteAcceptable(entry);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ return PluginResult.PreOperation.stopProcessing(
+ de.getResultCode(), de.getMessageObject());
+ }
+ }
+ }
+
+ return PluginResult.PreOperation.continueOperationProcessing();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PreOperation doPreOperation(
+ PreOperationModifyOperation modifyOperation)
+ {
+ Entry oldEntry = modifyOperation.getCurrentEntry();
+ Entry newEntry = modifyOperation.getModifiedEntry();
+
+ if ((newEntry.isSubentry() || newEntry.isLDAPSubentry()) ||
+ (oldEntry.isSubentry() || oldEntry.isLDAPSubentry()))
+ {
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.checkSubentryModifyAcceptable(
+ oldEntry, newEntry);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ return PluginResult.PreOperation.stopProcessing(
+ de.getResultCode(), de.getMessageObject());
+ }
+ }
+ }
+
+ return PluginResult.PreOperation.continueOperationProcessing();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PreOperation doPreOperation(
+ PreOperationModifyDNOperation modifyDNOperation)
+ {
+ Entry oldEntry = modifyDNOperation.getOriginalEntry();
+ Entry newEntry = modifyDNOperation.getUpdatedEntry();
+
+ if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
+ {
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.checkSubentryModifyAcceptable(
+ oldEntry, newEntry);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ return PluginResult.PreOperation.stopProcessing(
+ de.getResultCode(), de.getMessageObject());
+ }
+ }
+ }
+
+ return PluginResult.PreOperation.continueOperationProcessing();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PostOperation doPostOperation(
+ PostOperationAddOperation addOperation)
+ {
+ Entry entry = addOperation.getEntryToAdd();
+
+ if (entry.isSubentry() || entry.isLDAPSubentry())
{
try
{
addSubEntry(entry);
+
+ // Notify change listeners.
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.handleSubentryAdd(entry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
}
catch (Exception e)
{
@@ -571,38 +849,67 @@
// FIXME -- Handle this.
}
}
+
+ return PluginResult.PostOperation.continueOperationProcessing();
}
/**
- * {@inheritDoc} In this case, each entry is checked to see if it is
- * a subentry, and if so it will be deregistered with this manager.
+ * {@inheritDoc}
*/
- public void handleDeleteOperation(PostResponseDeleteOperation deleteOperation,
- Entry entry)
+ @Override
+ public PostOperation doPostOperation(
+ PostOperationDeleteOperation deleteOperation)
{
- if (entry.isSubentry())
+ Entry entry = deleteOperation.getEntryToDelete();
+
+ if (entry.isSubentry() || entry.isLDAPSubentry())
{
removeSubEntry(entry);
+
+ // Notify change listeners.
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.handleSubentryDelete(entry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
}
+
+ return PluginResult.PostOperation.continueOperationProcessing();
}
/**
- * {@inheritDoc} In this case, if the entry is a registered subentry
- * then it will be recreated from the contents of the provided entry
- * and re-registered with this manager.
+ * {@inheritDoc}
*/
- public void handleModifyOperation(PostResponseModifyOperation modifyOperation,
- Entry oldEntry, Entry newEntry)
+ @Override
+ public PostOperation doPostOperation(
+ PostOperationModifyOperation modifyOperation)
{
- if (oldEntry.isSubentry())
+ Entry oldEntry = modifyOperation.getCurrentEntry();
+ Entry newEntry = modifyOperation.getModifiedEntry();
+
+ boolean notify = false;
+
+ if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
{
removeSubEntry(oldEntry);
+ notify = true;
}
- if (newEntry.isSubentry())
+ if (newEntry.isSubentry() || newEntry.isLDAPSubentry())
{
try
{
addSubEntry(newEntry);
+ notify = true;
}
catch (Exception e)
{
@@ -614,19 +921,42 @@
// FIXME -- Handle this.
}
}
+
+ if (notify)
+ {
+ // Notify change listeners.
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.handleSubentryModify(
+ oldEntry, newEntry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+
+ return PluginResult.PostOperation.continueOperationProcessing();
}
/**
- * {@inheritDoc} In this case, if the subentry is registered then it
- * will be recreated from the contents of the provided entry and re-
- * registered with this manager under the new DN and the old instance
- * will be deregistered.
+ * {@inheritDoc}
*/
- public void handleModifyDNOperation(
- PostResponseModifyDNOperation modifyDNOperation,
- Entry oldEntry, Entry newEntry)
+ @Override
+ public PostOperation doPostOperation(
+ PostOperationModifyDNOperation modifyDNOperation)
{
- if (oldEntry.isSubentry())
+ Entry oldEntry = modifyDNOperation.getOriginalEntry();
+ Entry newEntry = modifyDNOperation.getUpdatedEntry();
+
+ if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
{
removeSubEntry(oldEntry);
try
@@ -642,6 +972,26 @@
// FIXME -- Handle this.
}
+
+ // Notify change listeners.
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.handleSubentryModify(
+ oldEntry, newEntry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
}
+
+ return PluginResult.PostOperation.continueOperationProcessing();
}
}
--
Gitblit v1.10.0