From a09e50d8d41c0f50c486742f4cc2343083c635e3 Mon Sep 17 00:00:00 2001
From: ludovicp <ludovicp@localhost>
Date: Fri, 25 Jun 2010 09:25:49 +0000
Subject: [PATCH] Fixes issues #4552 #4557, making sure plugins and internal services are properly handling subtree move or delete. The changes particularly resolve problems raised by the community with the referential integrity and the isMemberOf plug-ins. Unit-tests have been updated to cover those cases
---
opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java | 11
opends/src/server/org/opends/server/api/plugin/PluginResult.java | 194 +++
opends/src/server/org/opends/server/api/plugin/PluginType.java | 11
opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java | 13
opends/src/admin/messages/PasswordPolicyImportPluginCfgDefn.properties | 1
opends/src/server/org/opends/server/core/SubentryManager.java | 302 +++-
opends/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java | 113 +
opends/src/admin/messages/ReferentialIntegrityPluginCfgDefn.properties | 1
opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java | 222 +++
opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml | 32
opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java | 16
opends/src/admin/messages/PluginRootCfgDefn.properties | 3
opends/src/admin/messages/UniqueAttributePluginCfgDefn.properties | 1
opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java | 172 ++
opends/src/messages/messages/jeb.properties | 2
opends/src/server/org/opends/server/extensions/DynamicGroup.java | 13
opends/src/server/org/opends/server/extensions/StaticGroup.java | 13
opends/src/admin/messages/PluginCfgDefn.properties | 1
opends/src/server/org/opends/server/core/AuthenticatedUsers.java | 245 +++
opends/src/server/org/opends/server/core/GroupManager.java | 220 ++
opends/src/admin/messages/ProfilerPluginCfgDefn.properties | 1
opends/resource/schema/02-config.ldif | 6
opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml | 8
opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java | 25
opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java | 265 ++-
opends/src/admin/messages/LastModPluginCfgDefn.properties | 1
opends/src/admin/messages/ChangeNumberControlPluginCfgDefn.properties | 1
opends/src/server/org/opends/server/core/PluginConfigManager.java | 92 +
opends/resource/config/config.ldif | 1
opends/src/admin/messages/EntryUUIDPluginCfgDefn.properties | 1
opends/src/admin/messages/LDAPAttributeDescriptionListPluginCfgDefn.properties | 1
opends/src/admin/messages/FractionalLDIFImportPluginCfgDefn.properties | 1
opends/src/admin/messages/NetworkGroupPluginCfgDefn.properties | 1
opends/src/admin/messages/SevenBitCleanPluginCfgDefn.properties | 1
opends/src/server/org/opends/server/authorization/dseecompat/AciList.java | 348 +++--
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java | 22
opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java | 13
opends/tests/unit-tests-testng/src/server/org/opends/server/api/DITCacheMapTestCase.java | 496 +++++++
opends/src/server/org/opends/server/crypto/CryptoManagerSync.java | 6
opends/src/server/org/opends/server/types/AuthenticationInfo.java | 44
opends/src/server/org/opends/server/api/DITCacheMap.java | 791 ++++++++++++
opends/src/server/org/opends/server/api/Group.java | 13
opends/src/admin/defn/org/opends/server/admin/std/ReferentialIntegrityPluginConfiguration.xml | 3
opends/src/messages/messages/plugin.properties | 12
44 files changed, 3,232 insertions(+), 507 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index c370f27..ada1f35 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -1786,6 +1786,7 @@
ds-cfg-plugin-type: postOperationDelete
ds-cfg-plugin-type: postOperationModifyDN
ds-cfg-plugin-type: subordinateModifyDN
+ds-cfg-plugin-type: subordinateDelete
ds-cfg-attribute-type: member
ds-cfg-attribute-type: uniqueMember
ds-cfg-invoke-for-internal-operations: true
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 2bd9523..e95dd17 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -2465,6 +2465,11 @@
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
SINGLE-VALUE
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.609
+ NAME 'ds-cfg-plugin-order-subordinate-delete'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler'
SUP top
@@ -3573,6 +3578,7 @@
ds-cfg-plugin-order-search-result-entry $
ds-cfg-plugin-order-search-result-reference $
ds-cfg-plugin-order-subordinate-modify-dn $
+ ds-cfg-plugin-order-subordinate-delete $
ds-cfg-plugin-order-intermediate-response )
X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.112
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml
index de91736..d26a54e 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/PluginConfiguration.xml
@@ -23,7 +23,7 @@
! CDDL HEADER END
!
!
- ! Copyright 2007-2009 Sun Microsystems, Inc.
+ ! Copyright 2007-2010 Sun Microsystems, Inc.
! -->
<adm:managed-object name="plugin" plural-name="plugins"
package="org.opends.server.admin.std"
@@ -362,6 +362,12 @@
subordinate to the target of a modify DN operation.
</adm:synopsis>
</adm:value>
+ <adm:value name="subordinatedelete">
+ <adm:synopsis>
+ Invoked in the course of deleting a subordinate
+ entry of a delete operation.
+ </adm:synopsis>
+ </adm:value>
<adm:value name="intermediateresponse">
<adm:synopsis>
Invoked before sending an intermediate repsonse message to
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml
index 5eb8892..dea9a5c 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/PluginRootConfiguration.xml
@@ -23,7 +23,7 @@
! CDDL HEADER END
!
!
- ! Copyright 2007-2009 Sun Microsystems, Inc.
+ ! Copyright 2007-2010 Sun Microsystems, Inc.
! -->
<adm:managed-object name="plugin-root" plural-name="plugin-roots"
package="org.opends.server.admin.std"
@@ -1607,6 +1607,36 @@
</ldap:attribute>
</adm:profile>
</adm:property>
+ <adm:property name="plugin-order-subordinate-delete">
+ <adm:synopsis>
+ Specifies the order in which subordinate delete plug-ins are to
+ be loaded and invoked.
+ </adm:synopsis>
+ <adm:description>
+ The value is a comma-delimited list
+ of plug-in names (where the plug-in name is the RDN value from the
+ plug-in configuration entry DN). The list can include at most one
+ asterisk to indicate the position of any unspecified plug-in (and
+ the relative order of those unspecified plug-ins is
+ undefined).
+ </adm:description>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>
+ The order in which subordinate delete plug-ins are loaded
+ and invoked is undefined.
+ </adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-plugin-order-subordinate-delete</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
<adm:property name="plugin-order-intermediate-response">
<adm:synopsis>
Specifies the order in which intermediate response plug-ins are to
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/ReferentialIntegrityPluginConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/ReferentialIntegrityPluginConfiguration.xml
index 9f977ea..1cf8344 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/ReferentialIntegrityPluginConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/ReferentialIntegrityPluginConfiguration.xml
@@ -23,7 +23,7 @@
! CDDL HEADER END
!
!
- ! Copyright 2007-2008 Sun Microsystems, Inc.
+ ! Copyright 2007-2010 Sun Microsystems, Inc.
! -->
<adm:managed-object name="referential-integrity-plugin"
plural-name="referential-integrity-plugins"
@@ -67,6 +67,7 @@
<adm:value>postoperationdelete</adm:value>
<adm:value>postoperationmodifydn</adm:value>
<adm:value>subordinatemodifydn</adm:value>
+ <adm:value>subordinatedelete</adm:value>
</adm:defined>
</adm:default-behavior>
</adm:property-override>
diff --git a/opends/src/admin/messages/ChangeNumberControlPluginCfgDefn.properties b/opends/src/admin/messages/ChangeNumberControlPluginCfgDefn.properties
index 0bee311..7fcf2f3 100644
--- a/opends/src/admin/messages/ChangeNumberControlPluginCfgDefn.properties
+++ b/opends/src/admin/messages/ChangeNumberControlPluginCfgDefn.properties
@@ -58,4 +58,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/EntryUUIDPluginCfgDefn.properties b/opends/src/admin/messages/EntryUUIDPluginCfgDefn.properties
index 7dc5d3a..a9103ce 100644
--- a/opends/src/admin/messages/EntryUUIDPluginCfgDefn.properties
+++ b/opends/src/admin/messages/EntryUUIDPluginCfgDefn.properties
@@ -58,4 +58,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/FractionalLDIFImportPluginCfgDefn.properties b/opends/src/admin/messages/FractionalLDIFImportPluginCfgDefn.properties
index dec2887..0ae518b 100644
--- a/opends/src/admin/messages/FractionalLDIFImportPluginCfgDefn.properties
+++ b/opends/src/admin/messages/FractionalLDIFImportPluginCfgDefn.properties
@@ -58,4 +58,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/LDAPAttributeDescriptionListPluginCfgDefn.properties b/opends/src/admin/messages/LDAPAttributeDescriptionListPluginCfgDefn.properties
index 44de0e4..99a39ad 100644
--- a/opends/src/admin/messages/LDAPAttributeDescriptionListPluginCfgDefn.properties
+++ b/opends/src/admin/messages/LDAPAttributeDescriptionListPluginCfgDefn.properties
@@ -58,4 +58,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/LastModPluginCfgDefn.properties b/opends/src/admin/messages/LastModPluginCfgDefn.properties
index 3080f98..d17d72a 100644
--- a/opends/src/admin/messages/LastModPluginCfgDefn.properties
+++ b/opends/src/admin/messages/LastModPluginCfgDefn.properties
@@ -58,4 +58,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/NetworkGroupPluginCfgDefn.properties b/opends/src/admin/messages/NetworkGroupPluginCfgDefn.properties
index a9a8b5e..2a1c4e6 100644
--- a/opends/src/admin/messages/NetworkGroupPluginCfgDefn.properties
+++ b/opends/src/admin/messages/NetworkGroupPluginCfgDefn.properties
@@ -58,4 +58,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/PasswordPolicyImportPluginCfgDefn.properties b/opends/src/admin/messages/PasswordPolicyImportPluginCfgDefn.properties
index d57f754..fe8fdc7 100644
--- a/opends/src/admin/messages/PasswordPolicyImportPluginCfgDefn.properties
+++ b/opends/src/admin/messages/PasswordPolicyImportPluginCfgDefn.properties
@@ -63,4 +63,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/PluginCfgDefn.properties b/opends/src/admin/messages/PluginCfgDefn.properties
index 12ef5b4..86d6384 100644
--- a/opends/src/admin/messages/PluginCfgDefn.properties
+++ b/opends/src/admin/messages/PluginCfgDefn.properties
@@ -57,4 +57,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/PluginRootCfgDefn.properties b/opends/src/admin/messages/PluginRootCfgDefn.properties
index 95b0e64..a22fc82 100644
--- a/opends/src/admin/messages/PluginRootCfgDefn.properties
+++ b/opends/src/admin/messages/PluginRootCfgDefn.properties
@@ -155,6 +155,9 @@
property.plugin-order-startup.synopsis=Specifies the order in which startup plug-ins are to be loaded and invoked.
property.plugin-order-startup.description=The value is a comma-delimited list of plug-in names (where the plug-in name is the RDN value from the plug-in configuration entry DN). The list can include at most one asterisk to indicate the position of any unspecified plug-in (and the relative order of those unspecified plug-ins is undefined).
property.plugin-order-startup.default-behavior.alias.synopsis=The order in which startup plug-ins are loaded and invoked is undefined.
+property.plugin-order-subordinate-delete.synopsis=Specifies the order in which subordinate delete plug-ins are to be loaded and invoked.
+property.plugin-order-subordinate-delete.description=The value is a comma-delimited list of plug-in names (where the plug-in name is the RDN value from the plug-in configuration entry DN). The list can include at most one asterisk to indicate the position of any unspecified plug-in (and the relative order of those unspecified plug-ins is undefined).
+property.plugin-order-subordinate-delete.default-behavior.alias.synopsis=The order in which subordinate delete plug-ins are loaded and invoked is undefined.
property.plugin-order-subordinate-modify-dn.synopsis=Specifies the order in which subordinate modify DN plug-ins are to be loaded and invoked.
property.plugin-order-subordinate-modify-dn.description=The value is a comma-delimited list of plug-in names (where the plug-in name is the RDN value from the plug-in configuration entry DN). The list can include at most one asterisk to indicate the position of any unspecified plug-in (and the relative order of those unspecified plug-ins is undefined).
property.plugin-order-subordinate-modify-dn.default-behavior.alias.synopsis=The order in which subordinate modify DN plug-ins are loaded and invoked is undefined.
diff --git a/opends/src/admin/messages/ProfilerPluginCfgDefn.properties b/opends/src/admin/messages/ProfilerPluginCfgDefn.properties
index 7c55b1e..b091793 100644
--- a/opends/src/admin/messages/ProfilerPluginCfgDefn.properties
+++ b/opends/src/admin/messages/ProfilerPluginCfgDefn.properties
@@ -59,6 +59,7 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
property.profile-action.synopsis=Specifies the action that should be taken by the profiler.
property.profile-action.description=A value of "start" causes the profiler thread to start collecting data if it is not already active. A value of "stop" causes the profiler thread to stop collecting data and write it to disk, and a value of "cancel" causes the profiler thread to stop collecting data and discard anything that has been captured. These operations occur immediately.
diff --git a/opends/src/admin/messages/ReferentialIntegrityPluginCfgDefn.properties b/opends/src/admin/messages/ReferentialIntegrityPluginCfgDefn.properties
index 1c4a9fe..b263a86 100644
--- a/opends/src/admin/messages/ReferentialIntegrityPluginCfgDefn.properties
+++ b/opends/src/admin/messages/ReferentialIntegrityPluginCfgDefn.properties
@@ -65,6 +65,7 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
property.update-interval.synopsis=Specifies the interval in seconds when referential integrity updates are made.
property.update-interval.description=If this value is 0, then the updates are made synchronously in the foreground.
diff --git a/opends/src/admin/messages/SevenBitCleanPluginCfgDefn.properties b/opends/src/admin/messages/SevenBitCleanPluginCfgDefn.properties
index 469cc2f..69e1a1d 100644
--- a/opends/src/admin/messages/SevenBitCleanPluginCfgDefn.properties
+++ b/opends/src/admin/messages/SevenBitCleanPluginCfgDefn.properties
@@ -62,4 +62,5 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
diff --git a/opends/src/admin/messages/UniqueAttributePluginCfgDefn.properties b/opends/src/admin/messages/UniqueAttributePluginCfgDefn.properties
index 97052f4..d4d27f5 100644
--- a/opends/src/admin/messages/UniqueAttributePluginCfgDefn.properties
+++ b/opends/src/admin/messages/UniqueAttributePluginCfgDefn.properties
@@ -60,5 +60,6 @@
property.plugin-type.syntax.enumeration.value.searchresultreference.synopsis=Invoked before sending a search result reference to the client.
property.plugin-type.syntax.enumeration.value.shutdown.synopsis=Invoked during a graceful Directory Server shutdown.
property.plugin-type.syntax.enumeration.value.startup.synopsis=Invoked during the Directory Server startup process.
+property.plugin-type.syntax.enumeration.value.subordinatedelete.synopsis=Invoked in the course of deleting a subordinate entry of a delete operation.
property.plugin-type.syntax.enumeration.value.subordinatemodifydn.synopsis=Invoked in the course of moving or renaming an entry subordinate to the target of a modify DN operation.
property.type.synopsis=Specifies the type of attributes to check for value uniqueness.
diff --git a/opends/src/messages/messages/jeb.properties b/opends/src/messages/messages/jeb.properties
index f1f8cdf..2ba7d9d 100644
--- a/opends/src/messages/messages/jeb.properties
+++ b/opends/src/messages/messages/jeb.properties
@@ -365,6 +365,8 @@
SEVERE_ERR_CONFIG_JEB_CACHE_SIZE_TOO_SMALL_194=Configuration \
attribute ds-cfg-db-cache-size has a value of %d which is less than \
the minimum: %d
+MILD_ERR_JEB_DELETE_ABORTED_BY_SUBORDINATE_PLUGIN_195=A plugin caused the \
+ delete operation to be aborted while deleting a subordinate entry %s
NOTICE_JEB_IMPORT_LDIF_PHASE_TWO_MEM_REPORT_196=The available memory for phase \
two processing is %d bytes. The read ahead cache size is %d bytes calculated \
using %d buffers
diff --git a/opends/src/messages/messages/plugin.properties b/opends/src/messages/messages/plugin.properties
index aa03fb8..0ab9988 100644
--- a/opends/src/messages/messages/plugin.properties
+++ b/opends/src/messages/messages/plugin.properties
@@ -20,7 +20,7 @@
#
# CDDL HEADER END
#
-# Copyright 2006-2009 Sun Microsystems, Inc.
+# Copyright 2006-2010 Sun Microsystems, Inc.
@@ -412,4 +412,12 @@
SEVERE_ERR_PLUGIN_CHANGE_NUMBER_INVALID_PLUGIN_TYPE_LIST_114=An attempt was \
made to register the Change Number Control plugin with the following plugin \
types : %s. However this plugin must be configured with all of the following \
- plugin types : %s
\ No newline at end of file
+ plugin types : %s
+SEVERE_ERR_PLUGIN_SUBORDINATE_DELETE_PLUGIN_EXCEPTION_115=The subordinate \
+ delete plugin defined in configuration entry %s threw an exception when it \
+ was invoked for connection %d operation %d: %s. Processing on this \
+ operation will be terminated
+SEVERE_ERR_PLUGIN_SUBORDINATE_DELETE_PLUGIN_RETURNED_NULL_116=The \
+ subordinate delete plugin defined in configuration entry %s returned null \
+ when invoked for connection %d operation %s. This is an illegal response, \
+ and processing on this operation will be terminated
\ No newline at end of file
diff --git a/opends/src/server/org/opends/server/api/DITCacheMap.java b/opends/src/server/org/opends/server/api/DITCacheMap.java
new file mode 100644
index 0000000..6f2d335
--- /dev/null
+++ b/opends/src/server/org/opends/server/api/DITCacheMap.java
@@ -0,0 +1,791 @@
+/*
+ * 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 2010 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.api;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import org.opends.server.types.DN;
+
+
+/**
+ * The DITCacheMap class implements custom Map for structural
+ * storage of arbitrary objects in Directory Information Tree
+ * (DIT) like structure.
+ *
+ * This Map intended usage is for caching various server
+ * objects which can be subject to subtree operations
+ * like retrieval or removal of all objects under a
+ * specific DN. While using a regular Map it would
+ * require the entire Map iteration to achieve, this Map
+ * implementation maintains such internal structure that
+ * subtree operations are more efficient and do not
+ * require iterations over the entire map, instead
+ * additional subtree operations methods are provided by
+ * this Map to do just that.
+ *
+ * API wise it behaves exactly like a regular Map
+ * implementation except for providing additional
+ * subtree methods. All required linkage and
+ * structuring is performed within this Map
+ * implementation itself and not exposed via the
+ * API in any way. For example, putting these
+ * key/value pairs
+ *
+ * cn=Object1,ou=Objects,dc=example,dc=com : object1
+ * cn=Object2,ou=Objects,dc=example,dc=com : object2
+ * cn=Object3,ou=Objects,dc=example,dc=com : object3
+ *
+ * then invoking a subtree method on this Map with
+ * any of these keys
+ *
+ * ou=Objects,dc=example,dc=com
+ * dc=example,dc=com
+ * dc=com
+ *
+ * would bring all three objects previously stored in
+ * this map into subtree operation scope. Standard
+ * Map API methods can only work with the objects
+ * previously stored in this map explicitly.
+ *
+ * Note that this Map implementation is not
+ * synchronized.
+ *
+ * @param <T> arbitrary object type.
+ */
+public class DITCacheMap<T> extends AbstractMap<DN,T>
+{
+ /**
+ * Node class for object storage and
+ * linking to any subordinate nodes.
+ * @param <T> arbitrary storage object.
+ */
+ private static final class Node<T>
+ {
+ // Node DN.
+ DN dn;
+ // Storage object or null if this node exist
+ // only to support the DIT like structuring.
+ T element;
+ // Parent.
+ Node<T> parent;
+ // First child.
+ Node<T> child;
+ // Next sibling.
+ Node<T> next;
+ // Previous sibling.
+ Node<T> previous;
+ }
+
+ // Map size reflecting only nodes
+ // containing non empty elements.
+ private int size = 0;
+
+ // Backing Map implementation.
+ private Map<DN,Node<T>> ditCacheMap;
+
+ /**
+ * Default contructor.
+ */
+ public DITCacheMap()
+ {
+ ditCacheMap = new HashMap<DN,Node<T>>();
+ }
+
+ /**
+ * Contructs a new DITCacheMap from a given Map.
+ * @param m existing Map to construct new
+ * DITCacheMap from.
+ */
+ public DITCacheMap(Map<? extends DN, ? extends T> m)
+ {
+ ditCacheMap = new HashMap<DN,Node<T>>();
+ this.putAll(m);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int size()
+ {
+ return size;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isEmpty()
+ {
+ return ditCacheMap.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsKey(Object key)
+ {
+ if (get((DN) key) != null)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean containsValue(Object value)
+ {
+ for (Node<T> node : ditCacheMap.values())
+ {
+ if ((node.element != null) &&
+ node.element.equals(value))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public T get(Object key)
+ {
+ Node<T> node = ditCacheMap.get((DN)key);
+ if (node != null)
+ {
+ return node.element;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a set of stored objects
+ * subordinate to subtree DN.
+ * @param key subtree DN.
+ * @return collection of stored objects
+ * subordinate to subtree DN.
+ */
+ public Collection<T> getSubtree(DN key)
+ {
+ return new DITSubtreeSet(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public T put(DN key, T value)
+ {
+ T returnValue = null;
+
+ Node<T> existingNode = ditCacheMap.get(key);
+ if (existingNode != null)
+ {
+ returnValue = existingNode.element;
+ existingNode.element = value;
+ }
+ else
+ {
+ Node<T> node = new Node<T>();
+ node.dn = key;
+ node.element = value;
+ node.parent = null;
+ node.child = null;
+ node.next = null;
+ node.previous = null;
+
+ ditCacheMap.put(key, node);
+ size++;
+
+ for (DN parentDN = key.getParent();
+ parentDN != null;
+ parentDN = parentDN.getParent())
+ {
+ Node<T> parentNode = ditCacheMap.get(parentDN);
+ if (parentNode != null)
+ {
+ if (parentNode.child != null)
+ {
+ Node<T> lastNode = parentNode.child;
+ while (lastNode.next != null)
+ {
+ lastNode = lastNode.next;
+ }
+ node.previous = lastNode;
+ lastNode.next = node;
+ }
+ else
+ {
+ parentNode.child = node;
+ }
+ node.parent = parentNode;
+ break;
+ }
+ else
+ {
+ parentNode = new Node<T>();
+ parentNode.dn = parentDN;
+ parentNode.element = null;
+ parentNode.parent = null;
+ parentNode.child = node;
+ parentNode.next = null;
+ parentNode.previous = null;
+ ditCacheMap.put(parentDN, parentNode);
+ node.parent = parentNode;
+ node = parentNode;
+ }
+ }
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public T remove(Object key)
+ {
+ T returnValue = null;
+
+ Node<T> existingNode = ditCacheMap.get((DN)key);
+ if ((existingNode != null) &&
+ (existingNode.element != null))
+ {
+ returnValue = existingNode.element;
+
+ try
+ {
+ if (existingNode.child == null)
+ {
+ ditCacheMap.remove((DN)key);
+ }
+ else
+ {
+ existingNode.element = null;
+ return returnValue;
+ }
+ }
+ finally
+ {
+ size--;
+ }
+
+ for (DN parentDN = existingNode.dn.getParent();
+ parentDN != null;
+ parentDN = parentDN.getParent())
+ {
+ Node<T> parentNode = ditCacheMap.get(parentDN);
+ if (parentNode.child == existingNode)
+ {
+ parentNode.child = existingNode.next;
+ }
+ else
+ {
+ if (existingNode.next != null)
+ {
+ existingNode.next.previous = existingNode.previous;
+ }
+ if (existingNode.previous != null)
+ {
+ existingNode.previous.next = existingNode.next;
+ }
+ }
+ if ((parentNode.child == null) &&
+ (parentNode.element == null))
+ {
+ existingNode = ditCacheMap.remove(parentDN);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Removes a set of stored objects subordinate to subtree DN.
+ * @param key subtree DN.
+ * @param values collection for removed objects subordinate
+ * to subtree DN or <code>null</code>.
+ * @return <code>true</code> on success or
+ * <code>false</code> otherwise.
+ */
+ public boolean removeSubtree(DN key, Collection<? super T> values)
+ {
+ Node<T> rootNode = ditCacheMap.get(key);
+
+ if (rootNode != null)
+ {
+ if (rootNode.element != null)
+ {
+ remove(key);
+ if (values != null)
+ {
+ values.add(rootNode.element);
+ }
+ }
+
+ Node<T> node = rootNode.child;
+
+ while (node != null)
+ {
+ if (node.element != null)
+ {
+ remove(node.dn);
+ if (values != null)
+ {
+ values.add(node.element);
+ }
+ }
+ if (node.child != null)
+ {
+ node = node.child;
+ }
+ else
+ {
+ while ((node.next == null) &&
+ (node.parent != rootNode))
+ {
+ node = node.parent;
+ }
+ node = node.next;
+ }
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void putAll(Map<? extends DN, ? extends T> m)
+ {
+ for (Entry<? extends DN, ? extends T> entry : m.entrySet())
+ {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear()
+ {
+ ditCacheMap.clear();
+ size = 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<Entry<DN, T>> entrySet()
+ {
+ return new DITCacheEntrySet();
+ }
+
+ /**
+ * EntrySet class implementation for the DITCacheMap.
+ */
+ private class DITCacheEntrySet extends AbstractSet<Entry<DN, T>>
+ {
+ /**
+ * Iterator class implementation for the DITCacheEntrySet.
+ */
+ private class EntryIterator implements Iterator<Entry<DN, T>>
+ {
+ private Iterator<Entry<DN, Node<T>>> ditCacheMapIterator;
+ private Entry<DN, Node<T>> currentEntry;
+ private Entry<DN, Node<T>> nextEntry;
+ private boolean hasNext;
+
+ /**
+ * Default constructor.
+ */
+ public EntryIterator()
+ {
+ ditCacheMapIterator = ditCacheMap.entrySet().iterator();
+ currentEntry = null;
+ nextEntry = null;
+ hasNext = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasNext()
+ {
+ if (hasNext)
+ {
+ return true;
+ }
+ while (ditCacheMapIterator.hasNext())
+ {
+ Entry<DN, Node<T>> entry = ditCacheMapIterator.next();
+ Node<T> node = entry.getValue();
+ if ((node != null) && (node.element != null))
+ {
+ nextEntry = entry;
+ hasNext = true;
+ return true;
+ }
+ }
+ nextEntry = null;
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Entry<DN, T> next()
+ {
+ if (nextEntry != null)
+ {
+ Node<T> node = nextEntry.getValue();
+ currentEntry = nextEntry;
+ nextEntry = null;
+ hasNext = false;
+ return new DITCacheMapEntry(node.dn, node.element);
+ }
+ while (ditCacheMapIterator.hasNext())
+ {
+ Entry<DN, Node<T>> entry = ditCacheMapIterator.next();
+ Node<T> node = entry.getValue();
+ if ((node != null) && (node.element != null))
+ {
+ currentEntry = entry;
+ hasNext = false;
+ return new DITCacheMapEntry(node.dn, node.element);
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void remove()
+ {
+ if (currentEntry != null)
+ {
+ Entry<DN, Node<T>> oldIteratorEntry = null;
+ if (hasNext())
+ {
+ oldIteratorEntry = nextEntry;
+ }
+ if (DITCacheMap.this.remove(currentEntry.getKey()) != null)
+ {
+ ditCacheMapIterator = ditCacheMap.entrySet().iterator();
+ currentEntry = null;
+ nextEntry = null;
+ hasNext = false;
+ while (hasNext())
+ {
+ Entry<DN, T> newIteratorEntry = next();
+ if ((oldIteratorEntry != null) &&
+ oldIteratorEntry.getKey().equals(
+ newIteratorEntry.getKey()) &&
+ oldIteratorEntry.getValue().element.equals(
+ newIteratorEntry.getValue()))
+ {
+ nextEntry = currentEntry;
+ hasNext = true;
+ return;
+ }
+ }
+ currentEntry = null;
+ nextEntry = null;
+ hasNext = false;
+ return;
+ }
+ }
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size()
+ {
+ return DITCacheMap.this.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<Entry<DN, T>> iterator()
+ {
+ return new EntryIterator();
+ }
+ }
+
+ /**
+ * Map.Entry class implementation for the DITCacheMap.
+ */
+ private class DITCacheMapEntry implements Map.Entry<DN, T>
+ {
+ private DN key;
+ private T value;
+
+ /**
+ * Constructs a new DITCacheMapEntry
+ * with given key and value.
+ * @param key Map.Entry key.
+ * @param value Map.Entry value.
+ */
+ public DITCacheMapEntry(DN key, T value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public DN getKey()
+ {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T getValue()
+ {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T setValue(T value)
+ {
+ Node<T> node = ditCacheMap.get(key);
+ T oldValue = this.value;
+ node.element = value;
+ this.value = value;
+ return oldValue;
+ }
+ }
+
+ /**
+ * SubtreeSet class implementation.
+ */
+ private class DITSubtreeSet extends AbstractSet<T>
+ {
+ // Set key.
+ private DN key;
+
+ /**
+ * Default constructor.
+ */
+ public DITSubtreeSet()
+ {
+ this.key = null;
+ }
+
+ /**
+ * Keyed constructor.
+ * @param key to construct
+ * this set from.
+ */
+ public DITSubtreeSet(DN key)
+ {
+ this.key = key;
+ }
+
+ /**
+ * Iterator class implementation for SubtreeSet.
+ */
+ private class SubtreeSetIterator implements Iterator<T>
+ {
+ // Iterator key.
+ private DN key;
+
+ // Iterator root node.
+ private Node<T> rootNode;
+
+ // Iterator current node.
+ private Node<T> node;
+
+ /**
+ * Default constructor.
+ */
+ public SubtreeSetIterator()
+ {
+ this.key = DITSubtreeSet.this.key;
+ rootNode = ditCacheMap.get(this.key);
+ node = rootNode;
+ }
+
+ /**
+ * Keyed constructor.
+ * @param key to cue this
+ * iterator from.
+ */
+ public SubtreeSetIterator(DN key)
+ {
+ this.key = key;
+ rootNode = ditCacheMap.get(this.key);
+ node = rootNode;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasNext()
+ {
+ if (rootNode != null)
+ {
+ while (node != null)
+ {
+ if (node.element != null)
+ {
+ return true;
+ }
+ if (node.child != null)
+ {
+ node = node.child;
+ }
+ else
+ {
+ if (node != rootNode)
+ {
+ while ((node.next == null) &&
+ (node.parent != rootNode))
+ {
+ node = node.parent;
+ }
+ }
+ node = node.next;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T next()
+ {
+ T element = null;
+
+ if (rootNode != null)
+ {
+ while (node != null)
+ {
+ if (node.element != null)
+ {
+ element = node.element;
+ }
+ else
+ {
+ element = null;
+ }
+ if (node.child != null)
+ {
+ node = node.child;
+ }
+ else
+ {
+ if (node != rootNode)
+ {
+ while ((node.next == null) &&
+ (node.parent != rootNode))
+ {
+ node = node.parent;
+ }
+ }
+ node = node.next;
+ }
+ if (element != null)
+ {
+ return element;
+ }
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<T> iterator()
+ {
+ return new SubtreeSetIterator();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size()
+ {
+ int size = 0;
+
+ Iterator<T> iterator = new SubtreeSetIterator(this.key);
+ while (iterator.hasNext())
+ {
+ iterator.next();
+ size++;
+ }
+
+ return size;
+ }
+ }
+}
diff --git a/opends/src/server/org/opends/server/api/Group.java b/opends/src/server/org/opends/server/api/Group.java
index 60d19fb..d3bf5d6 100644
--- a/opends/src/server/org/opends/server/api/Group.java
+++ b/opends/src/server/org/opends/server/api/Group.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.api;
import org.opends.messages.Message;
@@ -209,6 +209,17 @@
/**
+ * Sets the DN of the entry that contains the definition for
+ * this group.
+ *
+ * @param groupDN The DN of the entry that contains the
+ * definition for this group.
+ */
+ public abstract void setGroupDN(DN groupDN);
+
+
+
+ /**
* Indicates whether this group supports nesting other groups, such
* that the members of the nested groups will also be considered
* members of this group.
diff --git a/opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java b/opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java
index 940ff5d..eaab22e 100644
--- a/opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java
+++ b/opends/src/server/org/opends/server/api/plugin/DirectoryServerPlugin.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2009 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.api.plugin;
import org.opends.messages.Message;
@@ -35,6 +35,7 @@
import org.opends.server.admin.std.server.PluginCfg;
import org.opends.server.api.ClientConnection;
import org.opends.server.config.ConfigException;
+import org.opends.server.core.DeleteOperation;
import org.opends.server.types.*;
import org.opends.server.types.operation.*;
@@ -1115,6 +1116,28 @@
/**
+ * Performs any necessary processing that should be done whenever a
+ * subordinate entry is deleted as part of subtree delete operation.
+ *
+ * @param deleteOperation The delete operation with which the
+ * subordinate entry is associated.
+ * @param entry The subordinate entry being deleted.
+ *
+ * @return Information about the result of the plugin processing.
+ */
+ public PluginResult.SubordinateDelete
+ processSubordinateDelete(DeleteOperation
+ deleteOperation, Entry entry)
+ {
+ Message message = ERR_PLUGIN_TYPE_NOT_SUPPORTED.get(
+ String.valueOf(pluginDN),
+ PluginType.SUBORDINATE_MODIFY_DN.getName());
+ throw new UnsupportedOperationException(message.toString());
+ }
+
+
+
+ /**
* Performs any necessary processing that should be done after the
* Directory Server has completed the core processing for a modify
* DN operation but before the response has been sent to the client.
diff --git a/opends/src/server/org/opends/server/api/plugin/PluginResult.java b/opends/src/server/org/opends/server/api/plugin/PluginResult.java
index 8f16525..4a51e1e 100644
--- a/opends/src/server/org/opends/server/api/plugin/PluginResult.java
+++ b/opends/src/server/org/opends/server/api/plugin/PluginResult.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.api.plugin;
@@ -1035,6 +1035,198 @@
}
/**
+ * Defines a subordinate delete plugin result for core server
+ * operation processing consisting of either continue, skip
+ * further plugins, or stop operation processing with a result
+ * code, matched DN, referral URLs, and error message.
+ */
+ public static final class SubordinateDelete
+ {
+ // Whether to continue operation processing.
+ private final boolean continueProcessing;
+
+ // Whether to invoke the rest of the plugins.
+ private final boolean continuePluginProcessing;
+
+ // An message explaining why processing should stop.
+ private final Message errorMessage;
+
+ // The matched DN for this result.
+ private final DN matchedDN;
+
+ // The set of referral URLs for this result.
+ private final List<String> referralURLs;
+
+ // The result code for this result.
+ private final ResultCode resultCode;
+
+ private static SubordinateDelete DEFAULT_RESULT =
+ new SubordinateDelete(true, true, null, null, null, null);
+
+ /**
+ * Construct a new subordinate delete plugin result.
+ *
+ * @param continueProcessing Whether to continue startup.
+ * @param continuePluginProcessing Whether to invoke the rest
+ * of the plugins.
+ * @param errorMessage An message explaining why processing
+ * should stop.
+ * @param resultCode The result code for this result.
+ * @param matchedDN The matched DN for this result.
+ * @param referralURLs The set of referral URLs for this result.
+ * stop.
+ */
+ private SubordinateDelete(boolean continueProcessing,
+ boolean continuePluginProcessing,
+ Message errorMessage,
+ ResultCode resultCode, DN matchedDN,
+ List<String> referralURLs)
+ {
+ this.continueProcessing = continueProcessing;
+ this.errorMessage = errorMessage;
+ this.continuePluginProcessing = continuePluginProcessing;
+ this.resultCode = resultCode;
+ this.matchedDN = matchedDN;
+ this.referralURLs = referralURLs;
+ }
+
+ /**
+ * Defines a continue processing subordinate delete plugin
+ * result.
+ *
+ * @return a continue processing subordinate delete plugin
+ * result.
+ */
+ public static SubordinateDelete continueOperationProcessing()
+ {
+ return DEFAULT_RESULT;
+ }
+
+ /**
+ * Defines a skip further plugin processing subordinate delete
+ * plugin result.
+ *
+ * @return a skip further plugin processing subordinate delete
+ * plugin result.
+ */
+ public static SubordinateDelete skipFurtherPluginProcesssing()
+ {
+ return new SubordinateDelete(true, false, null, null, null,
+ null);
+ }
+
+ /**
+ * Defines a new stop processing subordinate delete plugin
+ * result.
+ *
+ * @param resultCode The result code for this result.
+ * @param errorMessage An message explaining why processing
+ * should stop.
+ * @param matchedDN The matched DN for this result.
+ * @param referralURLs The set of referral URLs for this result.
+ *
+ * @return a new stop processing subordinate delete plugin
+ * result.
+ */
+ public static SubordinateDelete stopProcessing(
+ ResultCode resultCode, Message errorMessage, DN matchedDN,
+ List<String> referralURLs)
+ {
+ return new SubordinateDelete(false, false, errorMessage,
+ resultCode, matchedDN, referralURLs);
+ }
+
+ /**
+ * Contrust a new stop processing subordinate delete plugin
+ * result.
+ *
+ * @param resultCode The result code for this result.
+ * @param errorMessage An message explaining why processing
+ * should stop.
+ *
+ * @return a new stop processing subordinate delete plugin
+ * result.
+ */
+ public static SubordinateDelete stopProcessing(
+ ResultCode resultCode, Message errorMessage)
+ {
+ return new SubordinateDelete(false, false, errorMessage,
+ resultCode, null, null);
+ }
+
+ /**
+ * Whether to continue operation processing.
+ *
+ * @return <code>true</code> if processing should continue
+ * or <code>false</code> otherwise.
+ */
+ public boolean continueProcessing()
+ {
+ return continueProcessing;
+ }
+
+ /**
+ * Whether to invoke the rest of the plugins.
+ *
+ * @return <code>true</code> if the rest of the plugins should
+ * be invoked for <code>false</code> to skip the rest of the
+ * plugins.
+ */
+ public boolean continuePluginProcessing()
+ {
+ return continuePluginProcessing;
+ }
+
+ /**
+ * Retrieves the error message if <code>continueProcessing</code>
+ * returned <code>false</code>.
+ *
+ * @return An error message explaining why processing should
+ * stop or <code>null</code> if none is provided.
+ */
+ public Message getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * Retrieves the result code for the operation
+ * if <code>continueProcessing</code> returned <code>false</code>.
+ *
+ * @return the result code for the operation or <code>null</code>
+ * if none is provided.
+ */
+ public ResultCode getResultCode()
+ {
+ return resultCode;
+ }
+
+ /**
+ * Retrieves the matched DN for the operation
+ * if <code>continueProcessing</code> returned <code>false</code>.
+ *
+ * @return the matched DN for the operation or <code>null</code>
+ * if none is provided.
+ */
+ public DN getMatchedDN()
+ {
+ return matchedDN;
+ }
+
+ /**
+ * Retrieves the referral URLs for the operation
+ * if <code>continueProcessing</code> returned <code>false</code>.
+ *
+ * @return the refferal URLs for the operation or
+ * <code>null</code> if none is provided.
+ */
+ public List<String> getReferralURLs()
+ {
+ return referralURLs;
+ }
+ }
+
+ /**
* Defines an intermediate response plugin result for core server
* operation processing consisting of either continue, skip further
* plugins, or stop operation processing with a result code,
diff --git a/opends/src/server/org/opends/server/api/plugin/PluginType.java b/opends/src/server/org/opends/server/api/plugin/PluginType.java
index 76033a9..6f95bff 100644
--- a/opends/src/server/org/opends/server/api/plugin/PluginType.java
+++ b/opends/src/server/org/opends/server/api/plugin/PluginType.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2009 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.api.plugin;
@@ -458,6 +458,15 @@
/**
+ * The plugin type for plugins that are to be invoked on each
+ * subordinate entry that is deleted as part of a subtree
+ * delete operation.
+ */
+ SUBORDINATE_DELETE("subordinatedelete"),
+
+
+
+ /**
* The plugin type for plugins that are to be invoked before each
* intermediate response message is sent to a client.
*/
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
index b069c07..0674f03 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2008 Sun Microsystems, Inc.
+ * Copyright 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.authorization.dseecompat;
@@ -32,9 +32,11 @@
import static org.opends.server.authorization.dseecompat.AciHandler.*;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.messages.AccessControlMessages.*;
+import org.opends.server.api.DITCacheMap;
import org.opends.server.types.*;
import java.util.*;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* The AciList class performs caching of the ACI attribute values
@@ -46,8 +48,14 @@
* A map containing all the ACIs.
* We use the copy-on-write technique to avoid locking when reading.
*/
- private volatile LinkedHashMap<DN, List<Aci>> aciList =
- new LinkedHashMap<DN, List<Aci>>();
+ private volatile DITCacheMap<List<Aci>> aciList =
+ new DITCacheMap<List<Aci>>();
+
+ /*
+ * Lock to protect internal data structures.
+ */
+ private final ReentrantReadWriteLock lock =
+ new ReentrantReadWriteLock();
/*
* The configuration DN used to compare against the global ACI entry DN.
@@ -63,23 +71,6 @@
}
/**
- * Accessor to the ACI list intended to be called from within unsynchronized
- * read-only methods.
- * @return The current ACI list.
- */
- private LinkedHashMap<DN, List<Aci>> getList() {
- return aciList;
- }
-
- /**
- * Used by synchronized write methods to make a copy of the ACI list.
- * @return A copy of the ACI list.
- */
- private LinkedHashMap<DN,List<Aci>> copyList() {
- return new LinkedHashMap<DN, List<Aci>>(aciList);
- }
-
- /**
* Using the base DN, return a list of ACIs that are candidates for
* evaluation by walking up from the base DN towards the root of the
* DIT gathering ACIs on parents. Global ACIs use the NULL DN as the key
@@ -93,39 +84,52 @@
public LinkedList<Aci> getCandidateAcis(DN baseDN) {
LinkedList<Aci> candidates = new LinkedList<Aci>();
if(baseDN == null)
+ {
return candidates;
-
- // Save a reference to the current ACI list, in case it gets changed.
- LinkedHashMap<DN, List<Aci>> aciList = getList();
- //Save the baseDN in case we need to evaluate a global ACI.
- DN entryDN=baseDN;
- while(baseDN != null) {
- List<Aci> acis = aciList.get(baseDN);
- if (acis != null) {
- //Check if there are global ACIs. Global ACI has a NULL DN.
- if(baseDN.isNullDN()) {
- for(Aci aci : acis) {
- AciTargets targets=aci.getTargets();
- //If there is a target, evaluate it to see if this ACI should
- //be included in the candidate set.
- if(targets != null) {
- boolean ret=AciTargets.isTargetApplicable(aci, targets,
- entryDN);
- if(ret)
- candidates.add(aci); //Add this ACI to the candidates.
- }
- }
- } else
- candidates.addAll(acis);
- }
- if(baseDN.isNullDN())
- break;
- DN parentDN=baseDN.getParent();
- if(parentDN == null)
- baseDN=DN.nullDN();
- else
- baseDN=parentDN;
}
+
+ lock.readLock().lock();
+ try
+ {
+ //Save the baseDN in case we need to evaluate a global ACI.
+ DN entryDN=baseDN;
+ while (baseDN != null) {
+ List<Aci> acis = aciList.get(baseDN);
+ if (acis != null) {
+ //Check if there are global ACIs. Global ACI has a NULL DN.
+ if (baseDN.isNullDN()) {
+ for (Aci aci : acis) {
+ AciTargets targets = aci.getTargets();
+ //If there is a target, evaluate it to see if this ACI should
+ //be included in the candidate set.
+ if (targets != null) {
+ boolean ret = AciTargets.isTargetApplicable(aci, targets,
+ entryDN);
+ if (ret) {
+ candidates.add(aci); //Add this ACI to the candidates.
+ }
+ }
+ }
+ } else {
+ candidates.addAll(acis);
+ }
+ }
+ if(baseDN.isNullDN()) {
+ break;
+ }
+ DN parentDN=baseDN.getParent();
+ if(parentDN == null) {
+ baseDN=DN.nullDN();
+ } else {
+ baseDN=parentDN;
+ }
+ }
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
+
return candidates;
}
@@ -138,23 +142,27 @@
* exceptions.
* @return The number of valid ACI attribute values added to the ACI list.
*/
- public synchronized int addAci(List<? extends Entry> entries,
+ public int addAci(List<? extends Entry> entries,
LinkedList<Message> failedACIMsgs)
{
- // Copy the ACI list.
- LinkedHashMap<DN,List<Aci>> aciCopy = copyList();
-
int validAcis=0;
- for (Entry entry : entries) {
- DN dn=entry.getDN();
- List<Attribute> attributeList =
- entry.getOperationalAttribute(AciHandler.aciType);
- validAcis += addAciAttributeList(aciCopy, dn, configDN,
- attributeList, failedACIMsgs);
+
+ lock.writeLock().lock();
+ try
+ {
+ for (Entry entry : entries) {
+ DN dn=entry.getDN();
+ List<Attribute> attributeList =
+ entry.getOperationalAttribute(AciHandler.aciType);
+ validAcis += addAciAttributeList(aciList, dn, configDN,
+ attributeList, failedACIMsgs);
+ }
+ }
+ finally
+ {
+ lock.writeLock().unlock();
}
- // Replace the ACI list with the copy.
- aciList = aciCopy;
return validAcis;
}
@@ -167,8 +175,16 @@
* @param acis A set of ACIs to add to the ACI list.
*
*/
- public synchronized void addAci(DN dn, SortedSet<Aci> acis) {
- aciList.put(dn, new LinkedList<Aci>(acis));
+ public void addAci(DN dn, SortedSet<Aci> acis) {
+ lock.writeLock().lock();
+ try
+ {
+ aciList.put(dn, new LinkedList<Aci>(acis));
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
/**
@@ -182,29 +198,34 @@
* exceptions.
* @return The number of valid ACI attribute values added to the ACI list.
*/
- public synchronized int addAci(Entry entry, boolean hasAci,
+ public int addAci(Entry entry, boolean hasAci,
boolean hasGlobalAci,
LinkedList<Message> failedACIMsgs) {
int validAcis=0;
- // Copy the ACI list.
- LinkedHashMap<DN,List<Aci>> aciCopy = copyList();
- //Process global "ds-cfg-global-aci" attribute type. The oldentry
- //DN is checked to verify it is equal to the config DN. If not those
- //attributes are skipped.
- if(hasGlobalAci && entry.getDN().equals(configDN)) {
- List<Attribute> attributeList = entry.getAttribute(globalAciType);
- validAcis = addAciAttributeList(aciCopy, DN.nullDN(), configDN,
- attributeList, failedACIMsgs);
+ lock.writeLock().lock();
+ try
+ {
+ //Process global "ds-cfg-global-aci" attribute type. The oldentry
+ //DN is checked to verify it is equal to the config DN. If not those
+ //attributes are skipped.
+ if(hasGlobalAci && entry.getDN().equals(configDN)) {
+ List<Attribute> attributeList = entry.getAttribute(globalAciType);
+ validAcis = addAciAttributeList(aciList, DN.nullDN(), configDN,
+ attributeList, failedACIMsgs);
+ }
+
+ if(hasAci) {
+ List<Attribute> attributeList = entry.getAttribute(aciType);
+ validAcis += addAciAttributeList(aciList, entry.getDN(), configDN,
+ attributeList, failedACIMsgs);
+ }
+ }
+ finally
+ {
+ lock.writeLock().unlock();
}
- if(hasAci) {
- List<Attribute> attributeList = entry.getAttribute(aciType);
- validAcis += addAciAttributeList(aciCopy, entry.getDN(), configDN,
- attributeList, failedACIMsgs);
- }
- // Replace the ACI list with the copy.
- aciList = aciCopy;
return validAcis;
}
@@ -223,7 +244,7 @@
* exceptions.
* @return The number of valid attribute values added to the ACI list.
*/
- private static int addAciAttributeList(LinkedHashMap<DN,List<Aci>> aciList,
+ private static int addAciAttributeList(DITCacheMap<List<Aci>> aciList,
DN dn, DN configDN,
List<Attribute> attributeList,
LinkedList<Message> failedACIMsgs) {
@@ -271,33 +292,37 @@
* @param hasGlobalAci True if the "ds-cfg-global-aci" attribute type was
* seen in the entry.
*/
- public synchronized void modAciOldNewEntry(Entry oldEntry, Entry newEntry,
+ public void modAciOldNewEntry(Entry oldEntry, Entry newEntry,
boolean hasAci,
boolean hasGlobalAci) {
- // Copy the ACI list.
- LinkedHashMap<DN,List<Aci>> aciCopy = copyList();
+ lock.writeLock().lock();
+ try
+ {
LinkedList<Message>failedACIMsgs=new LinkedList<Message>();
//Process "aci" attribute types.
if(hasAci) {
- aciCopy.remove(oldEntry.getDN());
+ aciList.remove(oldEntry.getDN());
List<Attribute> attributeList =
newEntry.getOperationalAttribute(aciType);
- addAciAttributeList(aciCopy,newEntry.getDN(), configDN,
+ addAciAttributeList(aciList,newEntry.getDN(), configDN,
attributeList, failedACIMsgs);
}
//Process global "ds-cfg-global-aci" attribute type. The oldentry
//DN is checked to verify it is equal to the config DN. If not those
//attributes are skipped.
if(hasGlobalAci && oldEntry.getDN().equals(configDN)) {
- aciCopy.remove(DN.nullDN());
+ aciList.remove(DN.nullDN());
List<Attribute> attributeList =
newEntry.getAttribute(globalAciType);
- addAciAttributeList(aciCopy, DN.nullDN(), configDN,
+ addAciAttributeList(aciList, DN.nullDN(), configDN,
attributeList, failedACIMsgs);
}
- // Replace the ACI list with the copy.
- aciList = aciCopy;
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
/**
@@ -308,7 +333,7 @@
* @param dn The DN to use as the key.
* @param acis The ACI to be added.
*/
- private static void addAci(LinkedHashMap<DN,List<Aci>> aciList, DN dn,
+ private static void addAci(DITCacheMap<List<Aci>> aciList, DN dn,
List<Aci> acis)
{
if(aciList.containsKey(dn)) {
@@ -331,19 +356,33 @@
* seen in the entry.
* @return True if the ACI set was deleted.
*/
- public synchronized boolean removeAci(Entry entry, boolean hasAci,
+ public boolean removeAci(Entry entry, boolean hasAci,
boolean hasGlobalAci) {
- // Copy the ACI list.
- LinkedHashMap<DN,List<Aci>> aciCopy = copyList();
+ DN entryDN = entry.getDN();
- if(hasGlobalAci && entry.getDN().equals(configDN) &&
- aciCopy.remove(DN.nullDN()) == null)
- return false;
- if(hasAci && aciCopy.remove(entry.getDN()) == null)
- return false;
- // Replace the ACI list with the copy.
- aciList = aciCopy;
- return true;
+ lock.writeLock().lock();
+ try
+ {
+ if (hasGlobalAci && entryDN.equals(configDN) &&
+ aciList.remove(DN.nullDN()) == null)
+ {
+ return false;
+ }
+ if (hasAci && aciList.remove(entryDN) == null)
+ {
+ return false;
+ }
+ if (!hasGlobalAci && !hasAci)
+ {
+ return aciList.removeSubtree(entryDN, null);
+ }
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+
+ return true;
}
/**
@@ -351,22 +390,26 @@
* @param backend The backend to check if each DN is handled by that
* backend.
*/
- public synchronized void removeAci(Backend backend) {
- // Copy the ACI list.
- LinkedHashMap<DN,List<Aci>> aciCopy = copyList();
+ public void removeAci(Backend backend) {
- Iterator<Map.Entry<DN,List<Aci>>> iterator = aciCopy.entrySet().iterator();
- while (iterator.hasNext())
+ lock.writeLock().lock();
+ try
{
- Map.Entry<DN,List<Aci>> mapEntry = iterator.next();
- if (backend.handlesEntry(mapEntry.getKey()))
+ Iterator<Map.Entry<DN,List<Aci>>> iterator =
+ aciList.entrySet().iterator();
+ while (iterator.hasNext())
{
- iterator.remove();
+ Map.Entry<DN,List<Aci>> mapEntry = iterator.next();
+ if (backend.handlesEntry(mapEntry.getKey()))
+ {
+ iterator.remove();
+ }
}
}
-
- // Replace the ACI list with the copy.
- aciList = aciCopy;
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
/**
@@ -375,41 +418,54 @@
* @param oldDN The DN of the original entry that was moved.
* @param newDN The DN of the new entry.
*/
- public synchronized void renameAci(DN oldDN, DN newDN ) {
- LinkedHashMap<DN, List<Aci>> newCopyList =
- new LinkedHashMap<DN, List<Aci>>();
+ public void renameAci(DN oldDN, DN newDN ) {
+
int oldRDNCount=oldDN.getNumComponents();
int newRDNCount=newDN.getNumComponents();
- for (Map.Entry<DN,List<Aci>> hashEntry : aciList.entrySet()) {
- if(hashEntry.getKey().isDescendantOf(oldDN)) {
- int keyRDNCount=hashEntry.getKey().getNumComponents();
- int keepRDNCount=keyRDNCount - oldRDNCount;
- RDN[] newRDNs = new RDN[keepRDNCount + newRDNCount];
- for (int i=0; i < keepRDNCount; i++)
- newRDNs[i] = hashEntry.getKey().getRDN(i);
- for (int i=keepRDNCount, j=0; j < newRDNCount; i++,j++)
- newRDNs[i] = newDN.getRDN(j);
- DN relocateDN=new DN(newRDNs);
- List<Aci> acis = new LinkedList<Aci>();
- for(Aci aci : hashEntry.getValue()) {
- try {
- Aci newAci =
- Aci.decode(ByteString.valueOf(aci.toString()), relocateDN);
- acis.add(newAci);
- } catch (AciException ex) {
- //This should never happen since only a copy of the
- //ACI with a new DN is being made. Log a message if it does and
- //keep going.
- Message message = WARN_ACI_ADD_LIST_FAILED_DECODE.get(
- aci.toString(), String.valueOf(relocateDN), ex.getMessage());
- logError(message);
+
+ lock.writeLock().lock();
+ try
+ {
+ Map<DN,List<Aci>> tempAciList = new HashMap<DN,List<Aci>>();
+ Iterator<Map.Entry<DN,List<Aci>>> iterator =
+ aciList.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<DN,List<Aci>> hashEntry = iterator.next();
+ if(hashEntry.getKey().isDescendantOf(oldDN)) {
+ int keyRDNCount=hashEntry.getKey().getNumComponents();
+ int keepRDNCount=keyRDNCount - oldRDNCount;
+ RDN[] newRDNs = new RDN[keepRDNCount + newRDNCount];
+ for (int i=0; i < keepRDNCount; i++) {
+ newRDNs[i] = hashEntry.getKey().getRDN(i);
}
+ for (int i=keepRDNCount, j=0; j < newRDNCount; i++,j++) {
+ newRDNs[i] = newDN.getRDN(j);
+ }
+ DN relocateDN=new DN(newRDNs);
+ List<Aci> acis = new LinkedList<Aci>();
+ for(Aci aci : hashEntry.getValue()) {
+ try {
+ Aci newAci =
+ Aci.decode(ByteString.valueOf(aci.toString()), relocateDN);
+ acis.add(newAci);
+ } catch (AciException ex) {
+ //This should never happen since only a copy of the
+ //ACI with a new DN is being made. Log a message if it does and
+ //keep going.
+ Message message = WARN_ACI_ADD_LIST_FAILED_DECODE.get(
+ aci.toString(), String.valueOf(relocateDN), ex.getMessage());
+ logError(message);
+ }
+ }
+ tempAciList.put(relocateDN, acis);
+ iterator.remove();
}
- newCopyList.put(relocateDN, acis);
- } else
- newCopyList.put(hashEntry.getKey(), hashEntry.getValue());
+ }
+ aciList.putAll(tempAciList);
}
- // Replace the ACI list with the copy.
- aciList = newCopyList;
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
}
diff --git a/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java b/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
index 09cf14b..c4035b4 100644
--- a/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
+++ b/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
@@ -240,14 +240,11 @@
private void doPostDelete(Entry deletedEntry)
{
// This entry might have both global and aci attribute types.
- boolean hasAci, hasGlobalAci = false;
- if ((hasAci = deletedEntry
- .hasOperationalAttribute(AciHandler.aciType))
- || (hasGlobalAci = deletedEntry
- .hasAttribute(AciHandler.globalAciType)))
- {
- aciList.removeAci(deletedEntry, hasAci, hasGlobalAci);
- }
+ boolean hasAci = deletedEntry.hasOperationalAttribute(
+ AciHandler.aciType);
+ boolean hasGlobalAci = deletedEntry.hasAttribute(
+ AciHandler.globalAciType);
+ aciList.removeAci(deletedEntry, hasAci, hasGlobalAci);
}
diff --git a/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java b/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
index 8712bcb..f991b2c 100644
--- a/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
+++ b/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -1977,6 +1977,28 @@
*/
EntryID entryID = new EntryID(data);
DN subordinateDN = DN.decode(ByteString.wrap(key.getData()));
+
+ // Invoke any subordinate delete plugins on the entry.
+ if (!deleteOperation.isSynchronizationOperation())
+ {
+ Entry subordinateEntry = id2entry.get(
+ txn, entryID, LockMode.DEFAULT);
+ PluginConfigManager pluginManager =
+ DirectoryServer.getPluginConfigManager();
+ PluginResult.SubordinateDelete pluginResult =
+ pluginManager.invokeSubordinateDeletePlugins(
+ deleteOperation, subordinateEntry);
+
+ if (!pluginResult.continueProcessing())
+ {
+ Message message =
+ ERR_JEB_DELETE_ABORTED_BY_SUBORDINATE_PLUGIN.get(
+ subordinateDN.toString());
+ throw new DirectoryException(
+ DirectoryServer.getServerErrorResultCode(), message);
+ }
+ }
+
deleteEntry(txn, indexBuffer, true, entryDN, subordinateDN, entryID);
subordinateEntriesDeleted++;
diff --git a/opends/src/server/org/opends/server/core/AuthenticatedUsers.java b/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
index 55e9b70..43226c2 100644
--- a/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
+++ b/opends/src/server/org/opends/server/core/AuthenticatedUsers.java
@@ -22,20 +22,25 @@
* CDDL HEADER END
*
*
- * Copyright 2008 Sun Microsystems, Inc.
+ * Copyright 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
+import java.util.HashSet;
+import java.util.Set;
import org.opends.messages.Message;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.opends.server.api.ChangeNotificationListener;
import org.opends.server.api.ClientConnection;
+import org.opends.server.api.DITCacheMap;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.DN;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.Entry;
import org.opends.server.types.operation.PostResponseAddOperation;
import org.opends.server.types.operation.PostResponseDeleteOperation;
@@ -43,6 +48,8 @@
import org.opends.server.types.operation.PostResponseModifyDNOperation;
import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
/**
* This class provides a data structure which maps an authenticated user DN to
* the set of client connections authenticated as that user. Note that a single
@@ -56,11 +63,17 @@
public class AuthenticatedUsers
implements ChangeNotificationListener
{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
// The mapping between authenticated user DNs and the associated client
// connection objects.
- private ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>
- userMap;
+ private DITCacheMap<CopyOnWriteArraySet<ClientConnection>> userMap;
+ // Lock to protect internal data structures.
+ private final ReentrantReadWriteLock lock;
/**
@@ -68,7 +81,8 @@
*/
public AuthenticatedUsers()
{
- userMap = new ConcurrentHashMap<DN,CopyOnWriteArraySet<ClientConnection>>();
+ userMap = new DITCacheMap<CopyOnWriteArraySet<ClientConnection>>();
+ lock = new ReentrantReadWriteLock();
DirectoryServer.registerChangeNotificationListener(this);
}
@@ -83,18 +97,27 @@
* @param clientConnection The client connection over which the user is
* authenticated.
*/
- public synchronized void put(DN userDN, ClientConnection clientConnection)
+ public void put(DN userDN, ClientConnection clientConnection)
{
- CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
- if (connectionSet == null)
+ lock.writeLock().lock();
+ try
{
- connectionSet = new CopyOnWriteArraySet<ClientConnection>();
- connectionSet.add(clientConnection);
- userMap.put(userDN, connectionSet);
+ CopyOnWriteArraySet<ClientConnection> connectionSet =
+ userMap.get(userDN);
+ if (connectionSet == null)
+ {
+ connectionSet = new CopyOnWriteArraySet<ClientConnection>();
+ connectionSet.add(clientConnection);
+ userMap.put(userDN, connectionSet);
+ }
+ else
+ {
+ connectionSet.add(clientConnection);
+ }
}
- else
+ finally
{
- connectionSet.add(clientConnection);
+ lock.writeLock().unlock();
}
}
@@ -108,17 +131,26 @@
* @param clientConnection The client connection over which the user is
* authenticated.
*/
- public synchronized void remove(DN userDN, ClientConnection clientConnection)
+ public void remove(DN userDN, ClientConnection clientConnection)
{
- CopyOnWriteArraySet<ClientConnection> connectionSet = userMap.get(userDN);
- if (connectionSet != null)
+ lock.writeLock().lock();
+ try
{
- connectionSet.remove(clientConnection);
- if (connectionSet.isEmpty())
+ CopyOnWriteArraySet<ClientConnection> connectionSet =
+ userMap.get(userDN);
+ if (connectionSet != null)
{
- userMap.remove(userDN);
+ connectionSet.remove(clientConnection);
+ if (connectionSet.isEmpty())
+ {
+ userMap.remove(userDN);
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -136,7 +168,15 @@
*/
synchronized CopyOnWriteArraySet<ClientConnection> get(DN userDN)
{
- return userMap.get(userDN);
+ lock.readLock().lock();
+ try
+ {
+ return userMap.get(userDN);
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
}
@@ -172,11 +212,22 @@
PostResponseDeleteOperation deleteOperation,
Entry entry)
{
- // Identify any client connections that may be authenticated or
- // authorized as the user whose entry has been deleted and terminate them.
- CopyOnWriteArraySet<ClientConnection> connectionSet =
- userMap.remove(entry.getDN());
- if (connectionSet != null)
+ // Identify any client connections that may be authenticated
+ // or authorized as the user whose entry has been deleted and
+ // terminate them.
+ Set<CopyOnWriteArraySet<ClientConnection>> arraySet =
+ new HashSet<CopyOnWriteArraySet<ClientConnection>>();
+ lock.writeLock().lock();
+ try
+ {
+ userMap.removeSubtree(entry.getDN(), arraySet);
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+ for (CopyOnWriteArraySet<ClientConnection>
+ connectionSet : arraySet)
{
for (ClientConnection conn : connectionSet)
{
@@ -203,18 +254,26 @@
PostResponseModifyOperation modifyOperation,
Entry oldEntry, Entry newEntry)
{
- // Identify any client connections that may be authenticated or authorized
- // as the user whose entry has been modified and update them with the latest
- // version of the entry.
- CopyOnWriteArraySet<ClientConnection> connectionSet =
- userMap.get(oldEntry.getDN());
- if (connectionSet != null)
+ // Identify any client connections that may be authenticated
+ // or authorized as the user whose entry has been modified
+ // and update them with the latest version of the entry.
+ lock.writeLock().lock();
+ try
{
- for (ClientConnection conn : connectionSet)
+ CopyOnWriteArraySet<ClientConnection> connectionSet =
+ userMap.get(oldEntry.getDN());
+ if (connectionSet != null)
{
- conn.updateAuthenticationInfo(oldEntry, newEntry);
+ for (ClientConnection conn : connectionSet)
+ {
+ conn.updateAuthenticationInfo(oldEntry, newEntry);
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -232,32 +291,108 @@
PostResponseModifyDNOperation modifyDNOperation,
Entry oldEntry, Entry newEntry)
{
- // Identify any client connections that may be authenticated or authorized
- // as the user whose entry has been modified and update them with the latest
- // version of the entry.
- CopyOnWriteArraySet<ClientConnection> connectionSet =
- userMap.remove(oldEntry.getDN());
- if (connectionSet != null)
- {
- synchronized (this)
- {
- CopyOnWriteArraySet<ClientConnection> existingNewSet =
- userMap.get(newEntry.getDN());
- if (existingNewSet == null)
- {
- userMap.put(newEntry.getDN(), connectionSet);
- }
- else
- {
- existingNewSet.addAll(connectionSet);
- }
- }
+ String oldDNString = oldEntry.getDN().toNormalizedString();
+ String newDNString = newEntry.getDN().toNormalizedString();
- for (ClientConnection conn : connectionSet)
+ // Identify any client connections that may be authenticated
+ // or authorized as the user whose entry has been modified
+ // and update them with the latest version of the entry.
+ lock.writeLock().lock();
+ try
+ {
+ Set<CopyOnWriteArraySet<ClientConnection>> arraySet =
+ new HashSet<CopyOnWriteArraySet<ClientConnection>>();
+ userMap.removeSubtree(oldEntry.getDN(), arraySet);
+ for (CopyOnWriteArraySet<ClientConnection>
+ connectionSet : arraySet)
{
- conn.updateAuthenticationInfo(oldEntry, newEntry);
+ DN authNDN = null;
+ DN authZDN = null;
+ DN newAuthNDN = null;
+ DN newAuthZDN = null;
+ CopyOnWriteArraySet<ClientConnection> newAuthNSet = null;
+ CopyOnWriteArraySet<ClientConnection> newAuthZSet = null;
+ for (ClientConnection conn : connectionSet)
+ {
+ if (authNDN == null)
+ {
+ authNDN = conn.getAuthenticationInfo().getAuthenticationDN();
+ try
+ {
+ StringBuilder builder = new StringBuilder(
+ authNDN.toNormalizedString());
+ int oldDNIndex = builder.lastIndexOf(oldDNString);
+ builder.replace(oldDNIndex, builder.length(),
+ newDNString);
+ String newAuthNDNString = builder.toString();
+ newAuthNDN = DN.decode(newAuthNDNString);
+ }
+ catch (Exception e)
+ {
+ // Shouldnt happen.
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ if (authZDN == null)
+ {
+ authZDN = conn.getAuthenticationInfo().getAuthorizationDN();
+ try
+ {
+ StringBuilder builder = new StringBuilder(
+ authZDN.toNormalizedString());
+ int oldDNIndex = builder.lastIndexOf(oldDNString);
+ builder.replace(oldDNIndex, builder.length(),
+ newDNString);
+ String newAuthZDNString = builder.toString();
+ newAuthZDN = DN.decode(newAuthZDNString);
+ }
+ catch (Exception e)
+ {
+ // Shouldnt happen.
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ if ((newAuthNDN != null) && (authNDN != null) &&
+ authNDN.isDescendantOf(oldEntry.getDN()))
+ {
+ if (newAuthNSet == null)
+ {
+ newAuthNSet = new CopyOnWriteArraySet<ClientConnection>();
+ }
+ conn.getAuthenticationInfo().setAuthenticationDN(newAuthNDN);
+ newAuthNSet.add(conn);
+ }
+ if ((newAuthZDN != null) && (authZDN != null) &&
+ authZDN.isDescendantOf(oldEntry.getDN()))
+ {
+ if (newAuthZSet == null)
+ {
+ newAuthZSet = new CopyOnWriteArraySet<ClientConnection>();
+ }
+ conn.getAuthenticationInfo().setAuthorizationDN(newAuthZDN);
+ newAuthZSet.add(conn);
+ }
+ }
+ if ((newAuthNDN != null) && (newAuthNSet != null))
+ {
+ userMap.put(newAuthNDN, newAuthNSet);
+ }
+ if ((newAuthZDN != null) && (newAuthZSet != null))
+ {
+ userMap.put(newAuthZDN, newAuthZSet);
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
}
diff --git a/opends/src/server/org/opends/server/core/GroupManager.java b/opends/src/server/org/opends/server/core/GroupManager.java
index 5eddc18..1cf06c1 100644
--- a/opends/src/server/org/opends/server/core/GroupManager.java
+++ b/opends/src/server/org/opends/server/core/GroupManager.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2007-2008 Sun Microsystems, Inc.
+ * Copyright 2007-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
@@ -31,6 +31,7 @@
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.opends.messages.Message;
import org.opends.server.admin.ClassPropertyDefinition;
@@ -44,6 +45,7 @@
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.api.DITCacheMap;
import org.opends.server.api.Group;
import org.opends.server.config.ConfigException;
import org.opends.server.loggers.debug.DebugTracer;
@@ -55,6 +57,7 @@
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
@@ -103,7 +106,7 @@
//Used by group instances to determine if new groups have been
//registered or groups deleted.
- private long refreshToken=0;
+ private volatile long refreshToken=0;
// A mapping between the DNs of the config entries and the associated
@@ -112,7 +115,10 @@
// A mapping between the DNs of all group entries and the corresponding
// group instances.
- private ConcurrentHashMap<DN,Group> groupInstances;
+ private DITCacheMap<Group> groupInstances;
+
+ // Lock to protect internal data structures.
+ private final ReentrantReadWriteLock lock;
@@ -122,7 +128,9 @@
public GroupManager()
{
groupImplementations = new ConcurrentHashMap<DN,Group>();
- groupInstances = new ConcurrentHashMap<DN,Group>();
+ groupInstances = new DITCacheMap<Group>();
+
+ lock = new ReentrantReadWriteLock();
DirectoryServer.registerBackendInitializationListener(this);
DirectoryServer.registerChangeNotificationListener(this);
@@ -289,15 +297,23 @@
Group group = groupImplementations.remove(configuration.dn());
if (group != null)
{
- Iterator<Group> iterator = groupInstances.values().iterator();
- while (iterator.hasNext())
+ lock.writeLock().lock();
+ try
{
- Group g = iterator.next();
- if (g.getClass().getName().equals(group.getClass().getName()))
+ Iterator<Group> iterator = groupInstances.values().iterator();
+ while (iterator.hasNext())
{
- iterator.remove();
+ Group g = iterator.next();
+ if (g.getClass().getName().equals(group.getClass().getName()))
+ {
+ iterator.remove();
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
group.finalizeGroupImplementation();
}
@@ -360,15 +376,23 @@
Group group = groupImplementations.remove(configuration.dn());
if (group != null)
{
- Iterator<Group> iterator = groupInstances.values().iterator();
- while (iterator.hasNext())
+ lock.writeLock().lock();
+ try
{
- Group g = iterator.next();
- if (g.getClass().getName().equals(group.getClass().getName()))
+ Iterator<Group> iterator = groupInstances.values().iterator();
+ while (iterator.hasNext())
{
- iterator.remove();
+ Group g = iterator.next();
+ if (g.getClass().getName().equals(group.getClass().getName()))
+ {
+ iterator.remove();
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
group.finalizeGroupImplementation();
}
@@ -543,7 +567,18 @@
*/
public Iterable<Group> getGroupInstances()
{
- return groupInstances.values();
+ lock.readLock().lock();
+ try
+ {
+ // Return a copy to protect from structural changes.
+ ArrayList<Group> values = new ArrayList<Group>();
+ values.addAll(groupInstances.values());
+ return values;
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
}
@@ -559,7 +594,18 @@
*/
public Group getGroupInstance(DN entryDN)
{
- Group group = groupInstances.get(entryDN);
+ Group group = null;
+
+ lock.readLock().lock();
+ try
+ {
+ group = groupInstances.get(entryDN);
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
+
if (group == null)
{
// FIXME -- Should we try to retrieve the corresponding entry and see if
@@ -654,25 +700,33 @@
continue;
}
- for (SearchResultEntry entry : internalSearch.getSearchEntries())
+ lock.writeLock().lock();
+ try
{
- try
+ for (SearchResultEntry entry : internalSearch.getSearchEntries())
{
- Group groupInstance = groupImplementation.newInstance(entry);
- groupInstances.put(entry.getDN(), groupInstance);
- refreshToken++;
- }
- catch (Exception e)
- {
- if (debugEnabled())
+ try
{
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ Group groupInstance = groupImplementation.newInstance(entry);
+ groupInstances.put(entry.getDN(), groupInstance);
+ refreshToken++;
}
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
- // FIXME -- Handle this.
- continue;
+ // FIXME -- Handle this.
+ continue;
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
}
}
@@ -685,17 +739,25 @@
*/
public void performBackendFinalizationProcessing(Backend backend)
{
- Iterator<Map.Entry<DN,Group>> iterator =
- groupInstances.entrySet().iterator();
- while (iterator.hasNext())
+ lock.writeLock().lock();
+ try
{
- Map.Entry<DN,Group> mapEntry = iterator.next();
- DN groupEntryDN = mapEntry.getKey();
- if (backend.handlesEntry(groupEntryDN))
+ Iterator<Map.Entry<DN,Group>> iterator =
+ groupInstances.entrySet().iterator();
+ while (iterator.hasNext())
{
- iterator.remove();
+ Map.Entry<DN,Group> mapEntry = iterator.next();
+ DN groupEntryDN = mapEntry.getKey();
+ if (backend.handlesEntry(groupEntryDN))
+ {
+ iterator.remove();
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -719,11 +781,16 @@
}
}
}
- synchronized (groupInstances)
+ lock.writeLock().lock();
+ try
{
createAndRegisterGroup(entry);
refreshToken++;
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -746,11 +813,16 @@
}
}
}
- synchronized (groupInstances)
+ lock.writeLock().lock();
+ try
{
- groupInstances.remove(entry.getDN());
+ groupInstances.removeSubtree(entry.getDN(), null);
refreshToken++;
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -775,21 +847,24 @@
}
}
-
- if (groupInstances.containsKey(oldEntry.getDN()))
+ lock.writeLock().lock();
+ try
{
- synchronized (groupInstances)
+ if (groupInstances.containsKey(oldEntry.getDN()))
{
if (! oldEntry.getDN().equals(newEntry.getDN()))
{
// This should never happen, but check for it anyway.
groupInstances.remove(oldEntry.getDN());
}
-
createAndRegisterGroup(newEntry);
refreshToken++;
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
@@ -816,14 +891,44 @@
}
}
- if (groupInstances.containsKey(oldEntry.getDN()))
+ lock.writeLock().lock();
+ try
{
- synchronized (groupInstances)
+ Set<Group> groupSet = new HashSet<Group>();
+ groupInstances.removeSubtree(oldEntry.getDN(), groupSet);
+ String oldDNString = oldEntry.getDN().toNormalizedString();
+ String newDNString = newEntry.getDN().toNormalizedString();
+ for (Group group : groupSet)
{
- createAndRegisterGroup(newEntry);
- groupInstances.remove(oldEntry.getDN());
- refreshToken++;
+ StringBuilder builder = new StringBuilder(
+ group.getGroupDN().toNormalizedString());
+ int oldDNIndex = builder.lastIndexOf(oldDNString);
+ builder.replace(oldDNIndex, builder.length(),
+ newDNString);
+ String groupDNString = builder.toString();
+ DN groupDN = DN.NULL_DN;
+ try
+ {
+ groupDN = DN.decode(groupDNString);
+ }
+ catch (DirectoryException de)
+ {
+ // Should not happen but if it does all we
+ // can do here is debug log it and continue.
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
+ continue;
+ }
+ group.setGroupDN(groupDN);
+ groupInstances.put(groupDN, group);
}
+ refreshToken++;
+ }
+ finally
+ {
+ lock.writeLock().unlock();
}
}
@@ -845,7 +950,16 @@
if (groupImplementation.isGroupDefinition(entry))
{
Group groupInstance = groupImplementation.newInstance(entry);
- groupInstances.put(entry.getDN(), groupInstance);
+
+ lock.writeLock().lock();
+ try
+ {
+ groupInstances.put(entry.getDN(), groupInstance);
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
}
catch (Exception e)
@@ -869,7 +983,15 @@
*/
void deregisterAllGroups()
{
- groupInstances.clear();
+ lock.writeLock().lock();
+ try
+ {
+ groupInstances.clear();
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
diff --git a/opends/src/server/org/opends/server/core/PluginConfigManager.java b/opends/src/server/org/opends/server/core/PluginConfigManager.java
index 3f0bf15..9a74cd8 100644
--- a/opends/src/server/org/opends/server/core/PluginConfigManager.java
+++ b/opends/src/server/org/opends/server/core/PluginConfigManager.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2009 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
@@ -136,6 +136,7 @@
private DirectoryServerPlugin[] searchResultEntryPlugins;
private DirectoryServerPlugin[] searchResultReferencePlugins;
private DirectoryServerPlugin[] subordinateModifyDNPlugins;
+ private DirectoryServerPlugin[] subordinateDeletePlugins;
private DirectoryServerPlugin[] intermediateResponsePlugins;
@@ -222,6 +223,7 @@
searchResultEntryPlugins = new DirectoryServerPlugin[0];
searchResultReferencePlugins = new DirectoryServerPlugin[0];
subordinateModifyDNPlugins = new DirectoryServerPlugin[0];
+ subordinateDeletePlugins = new DirectoryServerPlugin[0];
intermediateResponsePlugins = new DirectoryServerPlugin[0];
registeredPlugins =
new ConcurrentHashMap<DN,
@@ -481,6 +483,7 @@
case SEARCHRESULTENTRY: return PluginType.SEARCH_RESULT_ENTRY;
case SEARCHRESULTREFERENCE: return PluginType.SEARCH_RESULT_REFERENCE;
case SUBORDINATEMODIFYDN: return PluginType.SUBORDINATE_MODIFY_DN;
+ case SUBORDINATEDELETE: return PluginType.SUBORDINATE_DELETE;
case INTERMEDIATERESPONSE: return PluginType.INTERMEDIATE_RESPONSE;
case POSTSYNCHRONIZATIONADD:
return PluginType.POST_SYNCHRONIZATION_ADD;
@@ -863,6 +866,11 @@
addPlugin(subordinateModifyDNPlugins, plugin, t,
pluginRootConfig.getPluginOrderSubordinateModifyDN());
break;
+ case SUBORDINATE_DELETE:
+ subordinateDeletePlugins =
+ addPlugin(subordinateDeletePlugins, plugin, t,
+ pluginRootConfig.getPluginOrderSubordinateDelete());
+ break;
case INTERMEDIATE_RESPONSE:
intermediateResponsePlugins =
addPlugin(intermediateResponsePlugins, plugin, t,
@@ -1373,6 +1381,10 @@
subordinateModifyDNPlugins =
removePlugin(subordinateModifyDNPlugins, plugin);
break;
+ case SUBORDINATE_DELETE:
+ subordinateDeletePlugins =
+ removePlugin(subordinateDeletePlugins, plugin);
+ break;
case INTERMEDIATE_RESPONSE:
intermediateResponsePlugins =
removePlugin(intermediateResponsePlugins, plugin);
@@ -5289,6 +5301,84 @@
/**
+ * Invokes the set of subordinate delete plugins that have been configured
+ * in the Directory Server.
+ *
+ * @param deleteOperation The delete operation with which the
+ * subordinate entry is associated.
+ * @param entry The subordinate entry being deleted.
+ *
+ * @return The result of processing the subordinate delete plugins.
+ */
+ public PluginResult.SubordinateDelete invokeSubordinateDeletePlugins(
+ DeleteOperation deleteOperation, Entry entry)
+ {
+ PluginResult.SubordinateDelete result = null;
+
+ for (DirectoryServerPlugin p : subordinateDeletePlugins)
+ {
+ if (deleteOperation.isInternalOperation() &&
+ (! p.invokeForInternalOperations()))
+ {
+ continue;
+ }
+
+ try
+ {
+ DirectoryServerPlugin<? extends PluginCfg> gp =
+ (DirectoryServerPlugin<? extends PluginCfg>) p;
+ result = gp.processSubordinateDelete(deleteOperation, entry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message =
+ ERR_PLUGIN_SUBORDINATE_DELETE_PLUGIN_EXCEPTION.get(
+ String.valueOf(p.getPluginEntryDN()),
+ deleteOperation.getConnectionID(),
+ deleteOperation.getOperationID(),
+ stackTraceToSingleLineString(e));
+ logError(message);
+
+ return PluginResult.SubordinateDelete.stopProcessing(
+ DirectoryServer.getServerErrorResultCode(), message);
+ }
+
+ if (result == null)
+ {
+ Message message =
+ ERR_PLUGIN_SUBORDINATE_DELETE_PLUGIN_RETURNED_NULL.get(
+ String.valueOf(p.getPluginEntryDN()),
+ deleteOperation.getConnectionID(),
+ String.valueOf(deleteOperation.getOperationID()));
+ logError(message);
+
+ return PluginResult.SubordinateDelete.stopProcessing(
+ DirectoryServer.getServerErrorResultCode(), message);
+ }
+ else if (! result.continuePluginProcessing())
+ {
+ return result;
+ }
+ }
+
+ if (result == null)
+ {
+ // This should only happen if there were no subordinate modify DN plugins
+ // registered, which is fine.
+ result = PluginResult.SubordinateDelete.continueOperationProcessing();
+ }
+
+ return result;
+ }
+
+
+
+ /**
* Invokes the set of intermediate response plugins that have been configured
* in the Directory Server.
*
diff --git a/opends/src/server/org/opends/server/core/SubentryManager.java b/opends/src/server/org/opends/server/core/SubentryManager.java
index 60a8c4e..b95f626 100644
--- a/opends/src/server/org/opends/server/core/SubentryManager.java
+++ b/opends/src/server/org/opends/server/core/SubentryManager.java
@@ -34,6 +34,7 @@
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
+import org.opends.server.api.DITCacheMap;
import org.opends.server.api.SubentryChangeListener;
import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
import org.opends.server.api.plugin.PluginResult;
@@ -101,6 +102,9 @@
// A mapping between the DNs and applicable collective subentries.
private HashMap<DN,List<SubEntry>> dn2CollectiveSubEntry;
+ // A mapping between subentry DNs and subentry objects.
+ private DITCacheMap<SubEntry> dit2SubEntry;
+
// Internal search all operational attributes.
private LinkedHashSet<String> requestAttrs;
@@ -142,6 +146,7 @@
dn2SubEntry = new HashMap<DN,List<SubEntry>>();
dn2CollectiveSubEntry = new HashMap<DN,List<SubEntry>>();
+ dit2SubEntry = new DITCacheMap<SubEntry>();
changeListeners =
new CopyOnWriteArrayList<SubentryChangeListener>();
@@ -228,6 +233,7 @@
dn2SubEntry.put(subDN, subList);
}
}
+ dit2SubEntry.put(entry.getDN(), subEntry);
subList.add(subEntry);
}
finally
@@ -258,6 +264,7 @@
SubEntry subEntry = listIterator.next();
if (subEntry.getDN().equals(entry.getDN()))
{
+ dit2SubEntry.remove(entry.getDN());
listIterator.remove();
removed = true;
break;
@@ -283,6 +290,7 @@
SubEntry subEntry = listIterator.next();
if (subEntry.getDN().equals(entry.getDN()))
{
+ dit2SubEntry.remove(entry.getDN());
listIterator.remove();
removed = true;
break;
@@ -657,6 +665,7 @@
SubEntry subEntry = listIterator.next();
if (backend.handlesEntry(subEntry.getDN()))
{
+ dit2SubEntry.remove(subEntry.getDN());
listIterator.remove();
// Notify change listeners.
@@ -694,6 +703,7 @@
SubEntry subEntry = listIterator.next();
if (backend.handlesEntry(subEntry.getDN()))
{
+ dit2SubEntry.remove(subEntry.getDN());
listIterator.remove();
// Notify change listeners.
@@ -731,17 +741,63 @@
{
if (entry.isSubentry() || entry.isLDAPSubentry())
{
+ lock.writeLock().lock();
try
{
- addSubEntry(entry);
+ 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)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // FIXME -- Handle this.
+ }
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+ }
+ }
+
+ private void doPostDelete(Entry entry)
+ {
+ lock.writeLock().lock();
+ try
+ {
+ for (SubEntry subEntry : dit2SubEntry.getSubtree(entry.getDN()))
+ {
+ removeSubEntry(subEntry.getEntry());
// Notify change listeners.
for (SubentryChangeListener changeListener :
- changeListeners)
+ changeListeners)
{
try
{
- changeListener.handleSubentryAdd(entry);
+ changeListener.handleSubentryDelete(subEntry.getEntry());
}
catch (Exception e)
{
@@ -752,40 +808,10 @@
}
}
}
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // FIXME -- Handle this.
- }
}
- }
-
- private void doPostDelete(Entry entry)
- {
- if (entry.isSubentry() || entry.isLDAPSubentry())
+ finally
{
- removeSubEntry(entry);
-
- // Notify change listeners.
- for (SubentryChangeListener changeListener :
- changeListeners)
- {
- try
- {
- changeListener.handleSubentryDelete(entry);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
- }
- }
+ lock.writeLock().unlock();
}
}
@@ -793,39 +819,20 @@
{
boolean notify = false;
- if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
+ lock.writeLock().lock();
+ try
{
- removeSubEntry(oldEntry);
- notify = true;
- }
- if (newEntry.isSubentry() || newEntry.isLDAPSubentry())
- {
- try
+ if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
{
- addSubEntry(newEntry);
+ removeSubEntry(oldEntry);
notify = true;
}
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // FIXME -- Handle this.
- }
- }
-
- if (notify)
- {
- // Notify change listeners.
- for (SubentryChangeListener changeListener :
- changeListeners)
+ if (newEntry.isSubentry() || newEntry.isLDAPSubentry())
{
try
{
- changeListener.handleSubentryModify(
- oldEntry, newEntry);
+ addSubEntry(newEntry);
+ notify = true;
}
catch (Exception e)
{
@@ -833,48 +840,96 @@
{
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
+
+ // 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);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ lock.writeLock().unlock();
}
}
private void doPostModifyDN(Entry oldEntry, Entry newEntry)
{
- if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
+ String oldDNString = oldEntry.getDN().toNormalizedString();
+ String newDNString = newEntry.getDN().toNormalizedString();
+
+ lock.writeLock().lock();
+ try
{
- removeSubEntry(oldEntry);
- try
+ Collection<SubEntry> setToDelete =
+ dit2SubEntry.getSubtree(oldEntry.getDN());
+ for (SubEntry subentry : setToDelete)
{
- addSubEntry(newEntry);
- }
- catch (Exception e)
- {
- if (debugEnabled())
- {
- TRACER.debugCaught(DebugLogLevel.ERROR, e);
- }
-
- // FIXME -- Handle this.
- }
-
- // Notify change listeners.
- for (SubentryChangeListener changeListener :
- changeListeners)
- {
+ removeSubEntry(subentry.getEntry());
+ oldEntry = subentry.getEntry();
try
{
- changeListener.handleSubentryModify(
- oldEntry, newEntry);
+ StringBuilder builder = new StringBuilder(
+ subentry.getEntry().getDN().toNormalizedString());
+ int oldDNIndex = builder.lastIndexOf(oldDNString);
+ builder.replace(oldDNIndex, builder.length(),
+ newDNString);
+ String subentryDNString = builder.toString();
+ newEntry = subentry.getEntry().duplicate(false);
+ newEntry.setDN(DN.decode(subentryDNString));
+ addSubEntry(newEntry);
}
catch (Exception e)
{
+ // Shouldnt happen.
if (debugEnabled())
{
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
}
+
+ // Notify change listeners.
+ for (SubentryChangeListener changeListener :
+ changeListeners)
+ {
+ try
+ {
+ changeListener.handleSubentryModify(
+ oldEntry, newEntry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
}
}
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
/**
@@ -920,27 +975,36 @@
{
Entry entry = deleteOperation.getEntryToDelete();
- if (entry.isSubentry() || entry.isLDAPSubentry())
+ lock.readLock().lock();
+ try
{
- for (SubentryChangeListener changeListener :
- changeListeners)
+ for (SubEntry subEntry : dit2SubEntry.getSubtree(entry.getDN()))
{
- try
+ for (SubentryChangeListener changeListener :
+ changeListeners)
{
- changeListener.checkSubentryDeleteAcceptable(entry);
- }
- catch (DirectoryException de)
- {
- if (debugEnabled())
+ try
{
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ changeListener.checkSubentryDeleteAcceptable(
+ subEntry.getEntry());
}
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ }
- return PluginResult.PreOperation.stopProcessing(
- de.getResultCode(), de.getMessageObject());
+ return PluginResult.PreOperation.stopProcessing(
+ de.getResultCode(), de.getMessageObject());
+ }
}
}
}
+ finally
+ {
+ lock.readLock().unlock();
+ }
return PluginResult.PreOperation.continueOperationProcessing();
}
@@ -991,29 +1055,61 @@
{
Entry oldEntry = modifyDNOperation.getOriginalEntry();
Entry newEntry = modifyDNOperation.getUpdatedEntry();
+ String oldDNString = oldEntry.getDN().toNormalizedString();
+ String newDNString = newEntry.getDN().toNormalizedString();
- if (oldEntry.isSubentry() || oldEntry.isLDAPSubentry())
+ lock.readLock().lock();
+ try
{
- for (SubentryChangeListener changeListener :
- changeListeners)
+ Collection<SubEntry> setToDelete =
+ dit2SubEntry.getSubtree(oldEntry.getDN());
+ for (SubEntry subentry : setToDelete)
{
+ oldEntry = subentry.getEntry();
try
{
- changeListener.checkSubentryModifyAcceptable(
- oldEntry, newEntry);
+ StringBuilder builder = new StringBuilder(
+ subentry.getEntry().getDN().toNormalizedString());
+ int oldDNIndex = builder.lastIndexOf(oldDNString);
+ builder.replace(oldDNIndex, builder.length(),
+ newDNString);
+ String subentryDNString = builder.toString();
+ newEntry = subentry.getEntry().duplicate(false);
+ newEntry.setDN(DN.decode(subentryDNString));
}
- catch (DirectoryException de)
+ catch (Exception e)
{
+ // Shouldnt happen.
if (debugEnabled())
{
- TRACER.debugCaught(DebugLogLevel.ERROR, de);
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
+ }
+ 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.stopProcessing(
+ de.getResultCode(), de.getMessageObject());
+ }
}
}
}
+ finally
+ {
+ lock.readLock().unlock();
+ }
return PluginResult.PreOperation.continueOperationProcessing();
}
diff --git a/opends/src/server/org/opends/server/crypto/CryptoManagerSync.java b/opends/src/server/org/opends/server/crypto/CryptoManagerSync.java
index e183408..61e08e9 100644
--- a/opends/src/server/org/opends/server/crypto/CryptoManagerSync.java
+++ b/opends/src/server/org/opends/server/crypto/CryptoManagerSync.java
@@ -551,6 +551,9 @@
RDN srcRDN = entry.getDN().getRDN();
// Only process the entry if it has the expected form of RDN.
+ // FIXME: Technically it is possible to perform a subtree in
+ // this case however such subtree delete would essentially be
+ // removing configuration branches which should not happen.
if (!srcRDN.isMultiValued() &&
srcRDN.getAttributeType(0).equals(attrAlias))
{
@@ -644,5 +647,8 @@
Entry newEntry)
{
// No implementation required.
+ // FIXME: Technically it is possible to perform a subtree modDN
+ // in this case however such subtree modDN would essentially be
+ // moving configuration branches which should not happen.
}
}
diff --git a/opends/src/server/org/opends/server/extensions/DynamicGroup.java b/opends/src/server/org/opends/server/extensions/DynamicGroup.java
index dc81701..6c4c594 100644
--- a/opends/src/server/org/opends/server/extensions/DynamicGroup.java
+++ b/opends/src/server/org/opends/server/extensions/DynamicGroup.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2008 Sun Microsystems, Inc.
+ * Copyright 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.extensions;
@@ -225,6 +225,17 @@
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setGroupDN(DN groupDN)
+ {
+ groupEntryDN = groupDN;
+ }
+
+
+
+ /**
* Retrieves the set of member URLs for this dynamic group. The returned set
* must not be altered by the caller.
*
diff --git a/opends/src/server/org/opends/server/extensions/StaticGroup.java b/opends/src/server/org/opends/server/extensions/StaticGroup.java
index 6804905..49ed4bc 100644
--- a/opends/src/server/org/opends/server/extensions/StaticGroup.java
+++ b/opends/src/server/org/opends/server/extensions/StaticGroup.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2008 Sun Microsystems, Inc.
+ * Copyright 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.extensions;
@@ -346,6 +346,17 @@
/**
* {@inheritDoc}
*/
+ @Override
+ public void setGroupDN(DN groupDN)
+ {
+ groupEntryDN = groupDN;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
@Override()
public boolean supportsNestedGroups()
{
diff --git a/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java b/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java
index 4d8a1a0..af0a76a 100644
--- a/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java
+++ b/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2008 Sun Microsystems, Inc.
+ * Copyright 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.extensions;
@@ -234,6 +234,17 @@
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setGroupDN(DN groupDN)
+ {
+ groupEntryDN = groupDN;
+ }
+
+
+
+ /**
* Retrieves the DN of the target group for this virtual static group.
*
* @return The DN of the target group for this virtual static group.
diff --git a/opends/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java b/opends/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java
index e279194..5dab099 100644
--- a/opends/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java
+++ b/opends/src/server/org/opends/server/plugins/ReferentialIntegrityPlugin.java
@@ -35,6 +35,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -53,6 +54,7 @@
import org.opends.server.api.ServerShutdownListener;
import org.opends.server.api.plugin.*;
import org.opends.server.config.ConfigException;
+import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.loggers.debug.DebugTracer;
@@ -136,6 +138,12 @@
*/
public static final String MODIFYDN_DNS="modifyDNs";
+ /**
+ * Used to save a set in the delete operation attachment map that
+ * holds the subordinate entry DNs related to a delete operation.
+ */
+ public static final String DELETE_DNS="deleteDNs";
+
//The buffered reader that is used to read the log file by the background
//thread.
private BufferedReader reader;
@@ -163,6 +171,7 @@
case POST_OPERATION_DELETE:
case POST_OPERATION_MODIFY_DN:
case SUBORDINATE_MODIFY_DN:
+ case SUBORDINATE_DELETE:
// These are acceptable.
break;
@@ -305,6 +314,7 @@
case POSTOPERATIONDELETE:
case POSTOPERATIONMODIFYDN:
case SUBORDINATEMODIFYDN:
+ case SUBORDINATEDELETE:
// These are acceptable.
break;
default:
@@ -370,23 +380,18 @@
return PluginResult.PostOperation.continueOperationProcessing();
}
- if (modifyDNOperation.getNewSuperior() == null)
+ Map<DN,DN>modDNmap=
+ (Map<DN, DN>) modifyDNOperation.getAttachment(MODIFYDN_DNS);
+ if(modDNmap == null)
{
- // The entry was simply renamed below the same parent.
- DN oldEntryDN=modifyDNOperation.getOriginalEntry().getDN();
- DN newEntryDN=modifyDNOperation.getUpdatedEntry().getDN();
- Map<DN,DN> modDNmap=new LinkedHashMap<DN,DN>();
- modDNmap.put(oldEntryDN, newEntryDN);
- processModifyDN(modDNmap,(interval != 0));
+ modDNmap=new LinkedHashMap<DN,DN>();
+ modifyDNOperation.setAttachment(MODIFYDN_DNS, modDNmap);
}
- else
- {
- // The entry was moved below a new parent. Use the saved map of old DNs
- // and new DNs from the operation attachment.
- Map<DN,DN> modDNmap =
- (Map<DN, DN>) modifyDNOperation.getAttachment(MODIFYDN_DNS);
- processModifyDN(modDNmap, (interval != 0));
- }
+ DN oldEntryDN=modifyDNOperation.getOriginalEntry().getDN();
+ DN newEntryDN=modifyDNOperation.getUpdatedEntry().getDN();
+ modDNmap.put(oldEntryDN, newEntryDN);
+
+ processModifyDN(modDNmap, (interval != 0));
return PluginResult.PostOperation.continueOperationProcessing();
}
@@ -396,6 +401,7 @@
/**
* {@inheritDoc}
*/
+ @SuppressWarnings("unchecked")
public PluginResult.PostOperation doPostOperation(
PostOperationDeleteOperation deleteOperation)
{
@@ -406,7 +412,16 @@
return PluginResult.PostOperation.continueOperationProcessing();
}
- processDelete(deleteOperation.getEntryDN(), (interval != 0));
+ Set<DN> deleteDNset =
+ (Set<DN>) deleteOperation.getAttachment(DELETE_DNS);
+ if(deleteDNset == null)
+ {
+ deleteDNset = new HashSet<DN>();
+ deleteOperation.setAttachment(MODIFYDN_DNS, deleteDNset);
+ }
+ deleteDNset.add(deleteOperation.getEntryDN());
+
+ processDelete(deleteDNset, (interval != 0));
return PluginResult.PostOperation.continueOperationProcessing();
}
@@ -433,6 +448,27 @@
return PluginResult.SubordinateModifyDN.continueOperationProcessing();
}
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public PluginResult.SubordinateDelete processSubordinateDelete(
+ DeleteOperation deleteOperation, Entry entry)
+ {
+ // This cast gives an unchecked cast warning, suppress it
+ // since the cast is ok.
+ Set<DN> deleteDNset =
+ (Set<DN>) deleteOperation.getAttachment(DELETE_DNS);
+ if(deleteDNset == null)
+ {
+ // First time through, create the set and set it in
+ // the operation attachment.
+ deleteDNset = new HashSet<DN>();
+ deleteOperation.setAttachment(DELETE_DNS, deleteDNset);
+ }
+ deleteDNset.add(entry.getDN());
+ return PluginResult.SubordinateDelete.continueOperationProcessing();
+ }
/**
* Verify that the specified attribute has either a distinguished name syntax
@@ -546,17 +582,17 @@
* a later time.
*
*/
- private void processDelete(DN entryDN, boolean log)
+ private void processDelete(Set<DN> deleteDNset, boolean log)
{
if(log)
{
- writeLog(entryDN);
+ writeLog(deleteDNset);
}
else
{
for(DN baseDN : getBaseDNsToSearch())
{
- searchBaseDN(baseDN, entryDN, null);
+ doBaseDN(baseDN, deleteDNset);
}
}
}
@@ -674,6 +710,24 @@
}
/**
+ * This method is used in foreground processing of a delete operation.
+ * It uses the specified set to perform base DN searching for each
+ * element.
+ *
+ * @param baseDN The DN to base the search at.
+ *
+ * @param deleteDNset The set containing the delete DNs.
+ *
+ */
+ private void doBaseDN(DN baseDN, Set<DN> deleteDNset)
+ {
+ for(DN deletedEntryDN : deleteDNset)
+ {
+ searchBaseDN(baseDN, deletedEntryDN, null);
+ }
+ }
+
+ /**
* For each attribute type, delete the specified old entry DN and
* optionally add the specified new entry DN if the DN is not null.
* The specified entry is used to see if it contains each attribute type so
@@ -812,18 +866,23 @@
}
/**
- * Write the specified entry DN to the log file. This entry DN is related to
- * a delete operation.
+ * Write the specified entry DNs to the log file.
+ * These entry DNs are related to a delete operation.
*
* @param deletedEntryDN The DN of the deleted entry.
*
*/
- private void writeLog(DN deletedEntryDN) {
- synchronized(logFile) {
- try {
+ private void writeLog(Set<DN> deleteDNset) {
+ synchronized(logFile)
+ {
+ try
+ {
setupWriter();
- writer.write(deletedEntryDN.toNormalizedString());
- writer.newLine();
+ for (DN deletedEntryDN : deleteDNset)
+ {
+ writer.write(deletedEntryDN.toNormalizedString());
+ writer.newLine();
+ }
writer.flush();
writer.close();
}
@@ -860,7 +919,7 @@
DN origDn = DN.decode(a[0]);
//If there is only a single DN string than it must be a delete.
if(a.length == 1) {
- processDelete(origDn, false);
+ processDelete(Collections.singleton(origDn), false);
} else {
DN movedDN=DN.decode(a[1]);
processModifyDN(origDn, movedDN);
diff --git a/opends/src/server/org/opends/server/types/AuthenticationInfo.java b/opends/src/server/org/opends/server/types/AuthenticationInfo.java
index b43adc3..f9d3875 100644
--- a/opends/src/server/org/opends/server/types/AuthenticationInfo.java
+++ b/opends/src/server/org/opends/server/types/AuthenticationInfo.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2009 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.types;
@@ -362,6 +362,26 @@
/**
+ * Sets the DN of the user as whom the client is authenticated,
+ * does nothing if the client is unauthenticated.
+ *
+ * @param dn authentication identity DN.
+ */
+ public void setAuthenticationDN(DN dn)
+ {
+ if (authenticationEntry == null)
+ {
+ return;
+ }
+ else
+ {
+ authenticationEntry.setDN(dn);
+ }
+ }
+
+
+
+ /**
* Retrieves the entry for the user that should be used as the
* default authorization identity.
*
@@ -401,6 +421,27 @@
/**
+ * Sets the DN for the user that should be used as the default
+ * authorization identity, does nothing if the client is
+ * unauthorized.
+ *
+ * @param dn authorization identity DN.
+ */
+ public void setAuthorizationDN(DN dn)
+ {
+ if (authorizationEntry == null)
+ {
+ return;
+ }
+ else
+ {
+ authorizationEntry.setDN(dn);
+ }
+ }
+
+
+
+ /**
* Retrieves the bind DN that the client used for simple
* authentication.
*
@@ -470,6 +511,7 @@
* @return A string representation of this authentication info
* structure.
*/
+ @Override
public String toString()
{
StringBuilder buffer = new StringBuilder();
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/api/DITCacheMapTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/api/DITCacheMapTestCase.java
new file mode 100644
index 0000000..75c8d50
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/api/DITCacheMapTestCase.java
@@ -0,0 +1,496 @@
+/*
+ * 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 2010 Sun Microsystems, Inc.
+ */
+package org.opends.server.api;
+
+
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opends.server.TestCaseUtils;
+import org.testng.annotations.Test;
+
+import org.opends.server.types.DN;
+import org.testng.annotations.BeforeClass;
+
+import static org.testng.Assert.*;
+
+/**
+ * A set of basic test cases for DITCacheMap class.
+ */
+public class DITCacheMapTestCase extends APITestCase
+{
+ private static final DITCacheMap<String> ditMap =
+ new DITCacheMap<String>();
+
+ private static final String dn0String =
+ "cn=Object0,dc=example,dc=com";
+ private static final String dn1String =
+ "cn=Object1,ou=Objects,dc=example,dc=com";
+ private static final String dn2String =
+ "cn=Object2,ou=Objects,dc=example,dc=com";
+ private static final String dn3String =
+ "cn=Object3,ou=Objects,dc=example,dc=com";
+ private static final String dn4String =
+ "cn=Object4,ou=Classes,dc=example,dc=com";
+ private static final String dn5String =
+ "cn=Object5,ou=Classes,dc=example,dc=com";
+ private static final String dn6String =
+ "cn=Object6,ou=More,ou=Objects,dc=example,dc=com";
+ private static final String dn7String =
+ "cn=Object7,ou=More,ou=Objects,dc=example,dc=com";
+ private static final String dn8String =
+ "cn=Object8,ou=More,ou=Objects,dc=example,dc=com";
+ private static final String dn9String =
+ "cn=Object9,ou=No,ou=More,ou=Objects,dc=example,dc=com";
+
+ private static DN dn0;
+ private static DN dn1;
+ private static DN dn2;
+ private static DN dn3;
+ private static DN dn4;
+ private static DN dn5;
+ private static DN dn6;
+ private static DN dn7;
+ private static DN dn8;
+ private static DN dn9;
+
+ private void putAllAndVerify()
+ {
+ Map<DN,String> hashMap =
+ new HashMap<DN,String>();
+
+ hashMap.put(dn0, dn0String);
+ hashMap.put(dn1, dn1String);
+ hashMap.put(dn2, dn2String);
+ hashMap.put(dn3, dn3String);
+ hashMap.put(dn4, dn4String);
+ hashMap.put(dn5, dn5String);
+ hashMap.put(dn6, dn6String);
+ hashMap.put(dn7, dn7String);
+ hashMap.put(dn8, dn8String);
+ hashMap.put(dn9, dn9String);
+
+ ditMap.putAll(hashMap);
+
+ assertFalse(ditMap.isEmpty());
+ assertEquals(ditMap.size(), 10);
+ assertTrue(ditMap.containsKey(dn0));
+ assertTrue(ditMap.containsKey(dn1));
+ assertTrue(ditMap.containsKey(dn2));
+ assertTrue(ditMap.containsKey(dn3));
+ assertTrue(ditMap.containsKey(dn4));
+ assertTrue(ditMap.containsKey(dn5));
+ assertTrue(ditMap.containsKey(dn6));
+ assertTrue(ditMap.containsKey(dn7));
+ assertTrue(ditMap.containsKey(dn8));
+ assertTrue(ditMap.containsKey(dn9));
+ assertTrue(ditMap.containsValue(dn0String));
+ assertTrue(ditMap.containsValue(dn1String));
+ assertTrue(ditMap.containsValue(dn2String));
+ assertTrue(ditMap.containsValue(dn3String));
+ assertTrue(ditMap.containsValue(dn4String));
+ assertTrue(ditMap.containsValue(dn5String));
+ assertTrue(ditMap.containsValue(dn6String));
+ assertTrue(ditMap.containsValue(dn7String));
+ assertTrue(ditMap.containsValue(dn8String));
+ assertTrue(ditMap.containsValue(dn9String));
+ }
+
+ private void clearTestMap()
+ {
+ ditMap.clear();
+ assertTrue(ditMap.isEmpty());
+ assertEquals(ditMap.size(), 0);
+ }
+
+ @BeforeClass()
+ public void beforeClass()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ dn0 = DN.decode(dn0String);
+ dn1 = DN.decode(dn1String);
+ dn2 = DN.decode(dn2String);
+ dn3 = DN.decode(dn3String);
+ dn4 = DN.decode(dn4String);
+ dn5 = DN.decode(dn5String);
+ dn6 = DN.decode(dn6String);
+ dn7 = DN.decode(dn7String);
+ dn8 = DN.decode(dn8String);
+ dn9 = DN.decode(dn9String);
+ }
+
+ @Test()
+ public void testDITCacheMapBasicOps()
+ throws Exception
+ {
+ clearTestMap();
+
+ ditMap.put(dn0, dn0String);
+ ditMap.put(dn1, dn1String);
+ ditMap.put(dn2, dn2String);
+ ditMap.put(dn3, dn3String);
+ ditMap.put(dn4, dn4String);
+ ditMap.put(dn5, dn5String);
+ ditMap.put(dn6, dn6String);
+ ditMap.put(dn7, dn7String);
+ ditMap.put(dn8, dn8String);
+ ditMap.put(dn9, dn9String);
+
+ assertFalse(ditMap.isEmpty());
+ assertEquals(ditMap.size(), 10);
+
+ assertTrue(ditMap.containsKey(dn0));
+ assertTrue(ditMap.containsKey(dn1));
+ assertTrue(ditMap.containsKey(dn2));
+ assertTrue(ditMap.containsKey(dn3));
+ assertTrue(ditMap.containsKey(dn4));
+ assertTrue(ditMap.containsKey(dn5));
+ assertTrue(ditMap.containsKey(dn6));
+ assertTrue(ditMap.containsKey(dn7));
+ assertTrue(ditMap.containsKey(dn8));
+ assertTrue(ditMap.containsKey(dn9));
+
+ assertFalse(ditMap.containsKey(DN.decode(
+ "ou=No,ou=More,ou=Objects,dc=example,dc=com")));
+ assertFalse(ditMap.containsKey(DN.decode(
+ "ou=More,ou=Objects,dc=example,dc=com")));
+ assertFalse(ditMap.containsKey(DN.decode(
+ "ou=Objects,dc=example,dc=com")));
+ assertFalse(ditMap.containsKey(DN.decode(
+ "ou=Classes,dc=example,dc=com")));
+ assertFalse(ditMap.containsKey(DN.decode(
+ "dc=example,dc=com")));
+ assertFalse(ditMap.containsKey(DN.decode(
+ "dc=com")));
+
+ assertTrue(ditMap.containsValue(dn0String));
+ assertTrue(ditMap.containsValue(dn1String));
+ assertTrue(ditMap.containsValue(dn2String));
+ assertTrue(ditMap.containsValue(dn3String));
+ assertTrue(ditMap.containsValue(dn4String));
+ assertTrue(ditMap.containsValue(dn5String));
+ assertTrue(ditMap.containsValue(dn6String));
+ assertTrue(ditMap.containsValue(dn7String));
+ assertTrue(ditMap.containsValue(dn8String));
+ assertTrue(ditMap.containsValue(dn9String));
+
+ assertEquals(ditMap.get(dn0), dn0String);
+ assertEquals(ditMap.get(dn1), dn1String);
+ assertEquals(ditMap.get(dn2), dn2String);
+ assertEquals(ditMap.get(dn3), dn3String);
+ assertEquals(ditMap.get(dn4), dn4String);
+ assertEquals(ditMap.get(dn5), dn5String);
+ assertEquals(ditMap.get(dn6), dn6String);
+ assertEquals(ditMap.get(dn7), dn7String);
+ assertEquals(ditMap.get(dn8), dn8String);
+ assertEquals(ditMap.get(dn9), dn9String);
+
+ assertNull(ditMap.get(DN.decode(
+ "ou=No,ou=More,ou=Objects,dc=example,dc=com")));
+ assertNull(ditMap.get(DN.decode(
+ "ou=More,ou=Objects,dc=example,dc=com")));
+ assertNull(ditMap.get(DN.decode(
+ "ou=Objects,dc=example,dc=com")));
+ assertNull(ditMap.get(DN.decode(
+ "ou=Classes,dc=example,dc=com")));
+ assertNull(ditMap.get(DN.decode(
+ "dc=example,dc=com")));
+ assertNull(ditMap.get(DN.decode(
+ "dc=com")));
+ }
+
+ @Test()
+ public void testDITCacheMapGetSubTree()
+ throws Exception
+ {
+ clearTestMap();
+
+ putAllAndVerify();
+
+ Collection<String> subtreeSet = ditMap.getSubtree(
+ DN.decode("dc=example,dc=com"));
+ assertFalse(subtreeSet.isEmpty());
+ assertEquals(subtreeSet.size(), 10);
+ assertTrue(subtreeSet.contains(dn0String));
+ assertTrue(subtreeSet.contains(dn1String));
+ assertTrue(subtreeSet.contains(dn2String));
+ assertTrue(subtreeSet.contains(dn3String));
+ assertTrue(subtreeSet.contains(dn4String));
+ assertTrue(subtreeSet.contains(dn5String));
+ assertTrue(subtreeSet.contains(dn6String));
+ assertTrue(subtreeSet.contains(dn7String));
+ assertTrue(subtreeSet.contains(dn8String));
+ assertTrue(subtreeSet.contains(dn9String));
+
+ subtreeSet = ditMap.getSubtree(
+ DN.decode("ou=Objects,dc=example,dc=com"));
+ assertFalse(subtreeSet.isEmpty());
+ assertEquals(subtreeSet.size(), 7);
+ assertTrue(subtreeSet.contains(dn1String));
+ assertTrue(subtreeSet.contains(dn2String));
+ assertTrue(subtreeSet.contains(dn3String));
+ assertTrue(subtreeSet.contains(dn6String));
+ assertTrue(subtreeSet.contains(dn7String));
+ assertTrue(subtreeSet.contains(dn8String));
+ assertTrue(subtreeSet.contains(dn9String));
+
+
+ subtreeSet = ditMap.getSubtree(
+ DN.decode("ou=Classes,dc=example,dc=com"));
+ assertFalse(subtreeSet.isEmpty());
+ assertEquals(subtreeSet.size(), 2);
+ assertTrue(subtreeSet.contains(dn4String));
+ assertTrue(subtreeSet.contains(dn5String));
+
+ subtreeSet = ditMap.getSubtree(
+ DN.decode("ou=More,ou=Objects,dc=example,dc=com"));
+ assertFalse(subtreeSet.isEmpty());
+ assertEquals(subtreeSet.size(), 4);
+ assertTrue(subtreeSet.contains(dn6String));
+ assertTrue(subtreeSet.contains(dn7String));
+ assertTrue(subtreeSet.contains(dn8String));
+ assertTrue(subtreeSet.contains(dn9String));
+
+ subtreeSet = ditMap.getSubtree(
+ DN.decode("ou=No,ou=More,ou=Objects,dc=example,dc=com"));
+ assertFalse(subtreeSet.isEmpty());
+ assertEquals(subtreeSet.size(), 1);
+ assertTrue(subtreeSet.contains(dn9String));
+
+ subtreeSet = ditMap.getSubtree(dn0);
+ assertFalse(subtreeSet.isEmpty());
+ assertEquals(subtreeSet.size(), 1);
+ assertTrue(subtreeSet.contains(dn0String));
+ }
+
+ @Test()
+ public void testDITCacheMapKeyAndEntrySet()
+ throws Exception
+ {
+ clearTestMap();
+
+ putAllAndVerify();
+
+ Set<DN> dnSet = ditMap.keySet();
+ assertFalse(dnSet.isEmpty());
+ assertEquals(dnSet.size(), 10);
+ assertTrue(dnSet.contains(dn0));
+ assertTrue(dnSet.contains(dn1));
+ assertTrue(dnSet.contains(dn2));
+ assertTrue(dnSet.contains(dn3));
+ assertTrue(dnSet.contains(dn4));
+ assertTrue(dnSet.contains(dn5));
+ assertTrue(dnSet.contains(dn6));
+ assertTrue(dnSet.contains(dn7));
+ assertTrue(dnSet.contains(dn8));
+ assertTrue(dnSet.contains(dn9));
+
+ Set<Entry<DN,String>> entrySet = ditMap.entrySet();
+ assertFalse(entrySet.isEmpty());
+ assertEquals(entrySet.size(), 10);
+ Iterator<Entry<DN,String>> iterator = entrySet.iterator();
+ Map<DN,String> tempMap = new HashMap<DN,String>();
+ while (iterator.hasNext())
+ {
+ Entry<DN,String> entry = iterator.next();
+ if ((entry.getKey().equals(dn0) &&
+ entry.getValue().equals(dn0String)) ||
+ (entry.getKey().equals(dn1) &&
+ entry.getValue().equals(dn1String)) ||
+ (entry.getKey().equals(dn2) &&
+ entry.getValue().equals(dn2String)) ||
+ (entry.getKey().equals(dn3) &&
+ entry.getValue().equals(dn3String)) ||
+ (entry.getKey().equals(dn4) &&
+ entry.getValue().equals(dn4String)) ||
+ (entry.getKey().equals(dn5) &&
+ entry.getValue().equals(dn5String)) ||
+ (entry.getKey().equals(dn6) &&
+ entry.getValue().equals(dn6String)) ||
+ (entry.getKey().equals(dn7) &&
+ entry.getValue().equals(dn7String)) ||
+ (entry.getKey().equals(dn8) &&
+ entry.getValue().equals(dn8String)) ||
+ (entry.getKey().equals(dn9) &&
+ entry.getValue().equals(dn9String)))
+ {
+ assertFalse(tempMap.containsKey(entry.getKey()));
+ assertFalse(tempMap.containsValue(entry.getValue()));
+ assertNull(tempMap.put(entry.getKey(), entry.getValue()));
+ }
+ else
+ {
+ fail();
+ }
+ iterator.remove();
+ }
+ assertEquals(tempMap.size(), 10);
+ assertEquals(ditMap.size(), 0);
+ assertTrue(ditMap.isEmpty());
+ }
+
+ @Test()
+ public void testDITCacheMapRemoveSubTree()
+ throws Exception
+ {
+ clearTestMap();
+
+ putAllAndVerify();
+
+ Set<String> removeSet = new HashSet<String>();
+ assertTrue(ditMap.removeSubtree(DN.decode(
+ "dc=example,dc=com"),
+ removeSet));
+ assertFalse(removeSet.isEmpty());
+ assertEquals(removeSet.size(), 10);
+ assertTrue(removeSet.contains(dn0String));
+ assertTrue(removeSet.contains(dn1String));
+ assertTrue(removeSet.contains(dn2String));
+ assertTrue(removeSet.contains(dn3String));
+ assertTrue(removeSet.contains(dn4String));
+ assertTrue(removeSet.contains(dn5String));
+ assertTrue(removeSet.contains(dn6String));
+ assertTrue(removeSet.contains(dn7String));
+ assertTrue(removeSet.contains(dn8String));
+ assertTrue(removeSet.contains(dn9String));
+ assertTrue(ditMap.isEmpty());
+
+ putAllAndVerify();
+
+ removeSet.clear();
+ assertTrue(ditMap.removeSubtree(DN.decode(
+ "ou=Objects,dc=example,dc=com"),
+ removeSet));
+ assertFalse(removeSet.isEmpty());
+ assertEquals(removeSet.size(), 7);
+ assertTrue(removeSet.contains(dn1String));
+ assertTrue(removeSet.contains(dn2String));
+ assertTrue(removeSet.contains(dn3String));
+ assertTrue(removeSet.contains(dn6String));
+ assertTrue(removeSet.contains(dn7String));
+ assertTrue(removeSet.contains(dn8String));
+ assertTrue(removeSet.contains(dn9String));
+ assertEquals(ditMap.size(), 3);
+ assertTrue(ditMap.containsKey(dn0));
+ assertTrue(ditMap.containsKey(dn4));
+ assertTrue(ditMap.containsKey(dn5));
+
+ clearTestMap();
+
+ putAllAndVerify();
+
+ removeSet.clear();
+ assertTrue(ditMap.removeSubtree(DN.decode(
+ "ou=Classes,dc=example,dc=com"),
+ removeSet));
+ assertFalse(removeSet.isEmpty());
+ assertEquals(removeSet.size(), 2);
+ assertTrue(removeSet.contains(dn4String));
+ assertTrue(removeSet.contains(dn5String));
+ assertEquals(ditMap.size(), 8);
+ assertTrue(ditMap.containsKey(dn0));
+ assertTrue(ditMap.containsKey(dn1));
+ assertTrue(ditMap.containsKey(dn2));
+ assertTrue(ditMap.containsKey(dn3));
+ assertTrue(ditMap.containsKey(dn6));
+ assertTrue(ditMap.containsKey(dn7));
+ assertTrue(ditMap.containsKey(dn8));
+ assertTrue(ditMap.containsKey(dn9));
+
+ clearTestMap();
+
+ putAllAndVerify();
+
+ removeSet.clear();
+ assertTrue(ditMap.removeSubtree(DN.decode(
+ "ou=More,ou=Objects,dc=example,dc=com"),
+ removeSet));
+ assertFalse(removeSet.isEmpty());
+ assertEquals(removeSet.size(), 4);
+ assertTrue(removeSet.contains(dn6String));
+ assertTrue(removeSet.contains(dn7String));
+ assertTrue(removeSet.contains(dn8String));
+ assertTrue(removeSet.contains(dn9String));
+ assertEquals(ditMap.size(), 6);
+ assertTrue(ditMap.containsKey(dn0));
+ assertTrue(ditMap.containsKey(dn1));
+ assertTrue(ditMap.containsKey(dn2));
+ assertTrue(ditMap.containsKey(dn3));
+ assertTrue(ditMap.containsKey(dn4));
+ assertTrue(ditMap.containsKey(dn5));
+
+ clearTestMap();
+
+ putAllAndVerify();
+
+ removeSet.clear();
+ assertTrue(ditMap.removeSubtree(DN.decode(
+ "ou=No,ou=More,ou=Objects,dc=example,dc=com"),
+ removeSet));
+ assertFalse(removeSet.isEmpty());
+ assertEquals(removeSet.size(), 1);
+ assertTrue(removeSet.contains(dn9String));
+ assertEquals(ditMap.size(), 9);
+ assertTrue(ditMap.containsKey(dn0));
+ assertTrue(ditMap.containsKey(dn1));
+ assertTrue(ditMap.containsKey(dn2));
+ assertTrue(ditMap.containsKey(dn3));
+ assertTrue(ditMap.containsKey(dn4));
+ assertTrue(ditMap.containsKey(dn5));
+ assertTrue(ditMap.containsKey(dn6));
+ assertTrue(ditMap.containsKey(dn7));
+ assertTrue(ditMap.containsKey(dn8));
+
+ clearTestMap();
+
+ putAllAndVerify();
+
+ removeSet.clear();
+ assertTrue(ditMap.removeSubtree(dn0,
+ removeSet));
+ assertFalse(removeSet.isEmpty());
+ assertEquals(removeSet.size(), 1);
+ assertTrue(removeSet.contains(dn0String));
+ assertEquals(ditMap.size(), 9);
+ assertTrue(ditMap.containsKey(dn1));
+ assertTrue(ditMap.containsKey(dn2));
+ assertTrue(ditMap.containsKey(dn3));
+ assertTrue(ditMap.containsKey(dn4));
+ assertTrue(ditMap.containsKey(dn5));
+ assertTrue(ditMap.containsKey(dn6));
+ assertTrue(ditMap.containsKey(dn7));
+ assertTrue(ditMap.containsKey(dn8));
+ assertTrue(ditMap.containsKey(dn9));
+ }
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java
index 90d7f9a..271e8c4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/api/plugin/DirectoryServerPluginTestCase.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2009 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.api.plugin;
@@ -551,6 +551,15 @@
expectedPublicMethods.add(sigList);
sigList = new LinkedList<String>();
+ sigList.add("processSubordinateDelete");
+ sigList.add("org.opends.server.api.plugin." +
+ "PluginResult$SubordinateDelete");
+ sigList.add("org.opends.server.core." +
+ "DeleteOperation");
+ sigList.add("org.opends.server.types.Entry");
+ expectedPublicMethods.add(sigList);
+
+ sigList = new LinkedList<String>();
sigList.add("processIntermediateResponse");
sigList.add("org.opends.server.api.plugin." +
"PluginResult$IntermediateResponse");
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
index c8e9610..976e0d9 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2009 Sun Microsystems, Inc.
+ * Copyright 2006-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
@@ -45,11 +45,13 @@
import org.opends.server.tools.LDAPReader;
import org.opends.server.tools.LDAPWriter;
import org.opends.server.types.*;
+import org.opends.messages.Message;
+import org.opends.server.tools.LDAPDelete;
+import org.opends.server.tools.LDAPModify;
import static org.testng.Assert.*;
-
+import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.protocols.ldap.LDAPConstants.*;
-import org.opends.messages.Message;
/**
@@ -2023,6 +2025,170 @@
/**
+ * Tests to ensure that performing subtree delete will
+ * cause the connection to no longer be associated
+ * with the previous identity.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(groups = "slow")
+ public void testSubtreeDeleteClearsAuthInfo()
+ throws Exception
+ {
+ TestCaseUtils.restartServer();
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+ TestCaseUtils.addEntries(
+ "dn: ou=people,dc=example,dc=com",
+ "objectClass: organizationalUnit",
+ "objectClass: top",
+ "ou: people",
+ "",
+ "dn: uid=test,ou=people,dc=example,dc=com",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test",
+ "givenName: test",
+ "sn: Test",
+ "cn: Test",
+ "userPassword: password");
+
+ String dnString = "uid=test,ou=people,dc=example,dc=com";
+ DN userDN = DN.decode(dnString);
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ s.setSoTimeout(6000);
+ LDAPReader r = new LDAPReader(s);
+ LDAPWriter w = new LDAPWriter(s);
+
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(ByteString.valueOf(dnString),
+ 3, ByteString.valueOf("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeMessage(message);
+
+ message = r.readMessage();
+ BindResponseProtocolOp bindResponse =
+ message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+ assertNotNull(DirectoryServer.getAuthenticatedUsers().get(
+ userDN));
+ assertEquals(DirectoryServer.getAuthenticatedUsers().get(
+ userDN).size(), 1);
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-J", OID_SUBTREE_DELETE_CONTROL + ":true",
+ "--noPropertiesFile",
+ "ou=people,dc=example,dc=com"
+ };
+ assertEquals(LDAPDelete.mainDelete(args, false, null, System.err), 0);
+
+ assertNull(DirectoryServer.getAuthenticatedUsers().get(userDN));
+
+ s.close();
+
+ // Cleanup.
+ TestCaseUtils.clearJEBackend(false,
+ "userRoot", "dc=example,dc=com");
+ }
+
+
+
+ /**
+ * Tests to ensure that performing subtree modify will
+ * cause the connection to be associated with new auth
+ * identity instead of the previous identity.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(groups = "slow")
+ public void testSubtreeModifyUpdatesAuthInfo()
+ throws Exception
+ {
+ TestCaseUtils.restartServer();
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+ TestCaseUtils.addEntries(
+ "dn: ou=people,dc=example,dc=com",
+ "objectClass: organizationalUnit",
+ "objectClass: top",
+ "ou: people",
+ "",
+ "dn: uid=test,ou=people,dc=example,dc=com",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test",
+ "givenName: test",
+ "sn: Test",
+ "cn: Test",
+ "userPassword: password");
+
+ String dnString = "uid=test,ou=people,dc=example,dc=com";
+ DN userDN = DN.decode(dnString);
+
+ Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
+ s.setSoTimeout(6000);
+ LDAPReader r = new LDAPReader(s);
+ LDAPWriter w = new LDAPWriter(s);
+
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(ByteString.valueOf(dnString),
+ 3, ByteString.valueOf("password"));
+ LDAPMessage message = new LDAPMessage(1, bindRequest);
+ w.writeMessage(message);
+
+ message = r.readMessage();
+ BindResponseProtocolOp bindResponse =
+ message.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), 0);
+
+ assertNotNull(DirectoryServer.getAuthenticatedUsers().get(
+ userDN));
+ assertEquals(DirectoryServer.getAuthenticatedUsers().get(
+ userDN).size(), 1);
+
+ String path = TestCaseUtils.createTempFile(
+ "dn: ou=people,dc=example,dc=com",
+ "changetype: moddn",
+ "newRDN: ou=users",
+ "deleteOldRDN: 1");
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "--noPropertiesFile",
+ "-f", path
+ };
+ assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+
+ String newDNString = "uid=test,ou=users,dc=example,dc=com";
+ DN newUserDN = DN.decode(newDNString);
+
+ assertNotNull(DirectoryServer.getAuthenticatedUsers().get(
+ newUserDN));
+ assertEquals(DirectoryServer.getAuthenticatedUsers().get(
+ newUserDN).size(), 1);
+
+ s.close();
+
+ // Cleanup.
+ TestCaseUtils.clearJEBackend(false,
+ "userRoot", "dc=example,dc=com");
+ }
+
+
+
+ /**
* Tests to ensure that the "ignore" password policy state update policy
* works as expected.
*
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
index 971b761..b8856bd 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2008 Sun Microsystems, Inc.
+ * Copyright 2008-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
@@ -45,6 +45,8 @@
import org.opends.server.extensions.VirtualStaticGroup;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.tools.LDAPDelete;
+import org.opends.server.tools.LDAPModify;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
import org.opends.server.types.DereferencePolicy;
@@ -60,6 +62,7 @@
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
+import static org.opends.server.util.ServerConstants.*;
import static org.testng.Assert.*;
@@ -2281,6 +2284,170 @@
}
/**
+ * Tests subtree delete operation on groups tree.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSubtreeDelete() throws Exception {
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ addSubtreeGroupTestEntries();
+
+ Group group1 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=moregroups,dc=example,dc=com"));
+ assertNotNull(group1);
+ Group group2 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=groups,dc=example,dc=com"));
+ assertNotNull(group2);
+ Group group3 = groupManager.getGroupInstance(
+ DN.decode("cn=group2,ou=groups,dc=example,dc=com"));
+ assertNotNull(group3);
+
+ // Get a client connection authenticated as user1 and make sure it handles
+ // group operations correctly.
+ DN userDN = DN.decode("uid=test1,ou=people,dc=example,dc=com");
+ InternalClientConnection conn = new InternalClientConnection(userDN);
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null, DN.nullDN(),
+ SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString("(objectClass=*)"), null,
+ null);
+
+ assertTrue(conn.isMemberOf(group1, null));
+ assertTrue(conn.isMemberOf(group2, null));
+ assertTrue(conn.isMemberOf(group3, null));
+
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-J", OID_SUBTREE_DELETE_CONTROL + ":true",
+ "--noPropertiesFile",
+ "ou=groups,dc=example,dc=com"
+ };
+ assertEquals(LDAPDelete.mainDelete(args, false, null, System.err), 0);
+
+ InternalClientConnection conn1 =
+ new InternalClientConnection(userDN);
+ searchOperation =
+ new InternalSearchOperation(conn1, conn1.nextOperationID(),
+ conn1.nextMessageID(), null, DN.nullDN(),
+ SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString("(objectClass=*)"), null,
+ null);
+
+ group1 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=moregroups,dc=example,dc=com"));
+ assertNotNull(group1);
+ assertTrue(conn1.isMemberOf(group1, null));
+ group2 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=groups,dc=example,dc=com"));
+ assertNull(group2);
+ group3 = groupManager.getGroupInstance(
+ DN.decode("cn=group2,ou=groups,dc=example,dc=com"));
+ assertNull(group3);
+
+ // Cleanup.
+ TestCaseUtils.clearJEBackend(false,
+ "userRoot", "dc=example,dc=com");
+ }
+
+ /**
+ * Tests subtree modify operation on groups tree.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testSubtreeModify() throws Exception {
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+ GroupManager groupManager = DirectoryServer.getGroupManager();
+ groupManager.deregisterAllGroups();
+ addSubtreeGroupTestEntries();
+
+ Group group1 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=moregroups,dc=example,dc=com"));
+ assertNotNull(group1);
+ Group group2 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=groups,dc=example,dc=com"));
+ assertNotNull(group2);
+ Group group3 = groupManager.getGroupInstance(
+ DN.decode("cn=group2,ou=groups,dc=example,dc=com"));
+ assertNotNull(group3);
+
+ // Get a client connection authenticated as user1 and make sure it handles
+ // group operations correctly.
+ DN userDN = DN.decode("uid=test1,ou=people,dc=example,dc=com");
+ InternalClientConnection conn = new InternalClientConnection(userDN);
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null, DN.nullDN(),
+ SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString("(objectClass=*)"), null,
+ null);
+
+ assertTrue(conn.isMemberOf(group1, null));
+ assertTrue(conn.isMemberOf(group2, null));
+ assertTrue(conn.isMemberOf(group3, null));
+
+ String path = TestCaseUtils.createTempFile(
+ "dn: ou=groups,dc=example,dc=com",
+ "changetype: moddn",
+ "newRDN: ou=newgroups",
+ "deleteOldRDN: 1");
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "--noPropertiesFile",
+ "-f", path
+ };
+ assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+
+ InternalClientConnection conn1 =
+ new InternalClientConnection(userDN);
+ searchOperation =
+ new InternalSearchOperation(conn1, conn1.nextOperationID(),
+ conn1.nextMessageID(), null, DN.nullDN(),
+ SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString("(objectClass=*)"), null,
+ null);
+
+ group1 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=moregroups,dc=example,dc=com"));
+ assertNotNull(group1);
+ assertTrue(conn1.isMemberOf(group1, null));
+ group2 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=groups,dc=example,dc=com"));
+ assertNull(group2);
+ group3 = groupManager.getGroupInstance(
+ DN.decode("cn=group2,ou=groups,dc=example,dc=com"));
+ assertNull(group3);
+ Group newGroup2 = groupManager.getGroupInstance(
+ DN.decode("cn=group1,ou=newgroups,dc=example,dc=com"));
+ assertNotNull(newGroup2);
+ assertTrue(conn.isMemberOf(newGroup2, null));
+ Group newGroup3 = groupManager.getGroupInstance(
+ DN.decode("cn=group2,ou=newgroups,dc=example,dc=com"));
+ assertNotNull(newGroup3);
+ assertTrue(conn.isMemberOf(newGroup3, null));
+
+ // Cleanup.
+ TestCaseUtils.clearJEBackend(false,
+ "userRoot", "dc=example,dc=com");
+ }
+
+ /**
* Adds nested group entries.
*
* @throws Exception If a problem adding the entries occurs.
@@ -2374,5 +2541,58 @@
"cn: User 5",
"userPassword: password");
}
+
+ /**
+ * Adds entries for subtree operations tests.
+ *
+ * @throws Exception If a problem adding the entries occurs.
+ */
+ private void addSubtreeGroupTestEntries() throws Exception {
+
+ TestCaseUtils.addEntries(
+ "dn: ou=people,dc=example,dc=com",
+ "objectClass: organizationalUnit",
+ "objectClass: top",
+ "ou: people",
+ "",
+ "dn: uid=test1,ou=people,dc=example,dc=com",
+ "objectClass: person",
+ "objectClass: inetOrgPerson",
+ "objectClass: organizationalPerson",
+ "objectClass: top",
+ "uid: test1",
+ "cn: test",
+ "sn: test",
+ "userPassword: password",
+ "",
+ "dn: ou=groups,dc=example,dc=com",
+ "objectClass: organizationalUnit",
+ "objectClass: top",
+ "ou: groups",
+ "",
+ "dn: cn=group1,ou=groups,dc=example,dc=com",
+ "objectClass: groupOfNames",
+ "objectClass: top",
+ "member: uid=test1,ou=people,dc=example,dc=com",
+ "cn: group1",
+ "",
+ "dn: cn=group2,ou=groups,dc=example,dc=com",
+ "objectClass: groupOfNames",
+ "objectClass: top",
+ "member: uid=test1,ou=people,dc=example,dc=com",
+ "cn: group2",
+ "",
+ "dn: ou=moregroups,dc=example,dc=com",
+ "objectClass: organizationalUnit",
+ "objectClass: top",
+ "ou: moregroups",
+ "",
+ "dn: cn=group1,ou=moregroups,dc=example,dc=com",
+ "objectClass: groupOfNames",
+ "objectClass: top",
+ "member: uid=test1,ou=people,dc=example,dc=com",
+ "cn: group1"
+ );
+ }
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
index 068ab7f..e357678 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2009 Sun Microsystems, Inc.
+ * Copyright 2009-2010 Sun Microsystems, Inc.
*/
package org.opends.server.core;
@@ -37,6 +37,8 @@
import org.opends.server.protocols.ldap.LDAPAttribute;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.tools.LDAPDelete;
+import org.opends.server.tools.LDAPModify;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValues;
import org.opends.server.types.ByteString;
@@ -55,6 +57,7 @@
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import static org.opends.server.util.ServerConstants.*;
import static org.testng.Assert.*;
public class SubentryManagerTestCase extends CoreTestCase
@@ -72,94 +75,7 @@
{
TestCaseUtils.startServer();
TestCaseUtils.clearJEBackend(false, "userRoot", SUFFIX);
-
- InternalClientConnection connection =
- InternalClientConnection.getRootConnection();
-
- // Add suffix entry.
- DN suffixDN = DN.decode(SUFFIX);
- if (DirectoryServer.getEntry(suffixDN) == null)
- {
- Entry suffixEntry = StaticUtils.createEntry(suffixDN);
- AddOperation addOperation =
- connection.processAdd(suffixEntry.getDN(),
- suffixEntry.getObjectClasses(),
- suffixEntry.getUserAttributes(),
- suffixEntry.getOperationalAttributes());
- assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
- assertNotNull(DirectoryServer.getEntry(suffixEntry.getDN()));
- }
-
- // Add base entry.
- DN baseDN = DN.decode(BASE);
- if (DirectoryServer.getEntry(baseDN) == null)
- {
- Entry baseEntry = StaticUtils.createEntry(baseDN);
- AddOperation addOperation =
- connection.processAdd(baseEntry.getDN(),
- baseEntry.getObjectClasses(),
- baseEntry.getUserAttributes(),
- baseEntry.getOperationalAttributes());
- assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
- assertNotNull(DirectoryServer.getEntry(baseEntry.getDN()));
- }
-
- // Add test entry.
- testEntry = TestCaseUtils.makeEntry(
- "dn: uid=rogasawara," + BASE,
- "objectclass: top",
- "objectclass: person",
- "objectclass: organizationalPerson",
- "objectclass: inetOrgPerson",
- "uid: rogasawara",
- "userpassword: password",
- "mail: rogasawara@example.com",
- "givenname: Rodney",
- "sn: Ogasawara",
- "cn: Rodney Ogasawara",
- "title: Sales, Director"
- );
- AddOperation addOperation =
- connection.processAdd(testEntry.getDN(),
- testEntry.getObjectClasses(),
- testEntry.getUserAttributes(),
- testEntry.getOperationalAttributes());
- assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
- assertNotNull(DirectoryServer.getEntry(testEntry.getDN()));
-
- // Add test subentry.
- ldapSubentry = TestCaseUtils.makeEntry(
- "dn: cn=Subentry," + SUFFIX,
- "objectClass: top",
- "objectclass: subentry",
- "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}",
- "cn: Subentry");
- addOperation =
- connection.processAdd(ldapSubentry.getDN(),
- ldapSubentry.getObjectClasses(),
- ldapSubentry.getUserAttributes(),
- ldapSubentry.getOperationalAttributes());
- assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
- assertNotNull(DirectoryServer.getEntry(ldapSubentry.getDN()));
-
- // Add test collective subentry.
- collectiveSubentry = TestCaseUtils.makeEntry(
- "dn: cn=Collective Subentry," + SUFFIX,
- "objectClass: top",
- "objectclass: subentry",
- "objectClass: collectiveAttributeSubentry",
- "objectClass: extensibleObject",
- "c-l: Savoie",
- "preferredLanguage;collective: fr",
- "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}",
- "cn: Collective Subentry");
- addOperation =
- connection.processAdd(collectiveSubentry.getDN(),
- collectiveSubentry.getObjectClasses(),
- collectiveSubentry.getUserAttributes(),
- collectiveSubentry.getOperationalAttributes());
- assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
- assertNotNull(DirectoryServer.getEntry(collectiveSubentry.getDN()));
+ addTestEntries();
}
@AfterClass
@@ -452,4 +368,175 @@
testEntry.getDN().toNormalizedString()), mods);
assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
}
+
+ @Test
+ public void testSubtreeDelete() throws Exception
+ {
+ String[] args =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "-J", OID_SUBTREE_DELETE_CONTROL + ":true",
+ "--noPropertiesFile",
+ SUFFIX
+ };
+ assertEquals(LDAPDelete.mainDelete(args, false, null, System.err), 0);
+
+ assertTrue(DirectoryServer.getSubentryManager().getCollectiveSubentries(
+ DN.decode("uid=rogasawara," + BASE)).isEmpty());
+
+ assertTrue(DirectoryServer.getSubentryManager().getSubentries(
+ DN.decode("uid=rogasawara," + BASE)).isEmpty());
+
+ // Re-add entries.
+ addTestEntries();
+ }
+
+ @Test
+ public void testSubtreeModify() throws Exception
+ {
+ String OLDBASE = "ou=Test SubEntry Manager";
+ String NEWBASE = "ou=New SubEntry Manager Base";
+
+ String newPath = TestCaseUtils.createTempFile(
+ "dn: " + BASE,
+ "changetype: moddn",
+ "newRDN: " + NEWBASE,
+ "deleteOldRDN: 1");
+
+ String[] newArgs =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "--noPropertiesFile",
+ "-f", newPath
+ };
+ assertEquals(LDAPModify.mainModify(newArgs, false, null, System.err), 0);
+
+ assertNotNull(DirectoryServer.getEntry(DN.decode(
+ "uid=rogasawara," + NEWBASE + "," + SUFFIX)));
+ assertTrue(DirectoryServer.getSubentryManager().getCollectiveSubentries(
+ DN.decode("uid=rogasawara," + NEWBASE + "," + SUFFIX)).isEmpty());
+ assertTrue(DirectoryServer.getSubentryManager().getSubentries(
+ DN.decode("uid=rogasawara," + NEWBASE + "," + SUFFIX)).isEmpty());
+
+ // Move it back.
+ String oldPath = TestCaseUtils.createTempFile(
+ "dn: " + NEWBASE + "," + SUFFIX,
+ "changetype: moddn",
+ "newRDN: " + OLDBASE,
+ "deleteOldRDN: 1");
+ String[] oldArgs =
+ {
+ "-h", "127.0.0.1",
+ "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+ "-D", "cn=Directory Manager",
+ "-w", "password",
+ "--noPropertiesFile",
+ "-f", oldPath
+ };
+ assertEquals(LDAPModify.mainModify(oldArgs, false, null, System.err), 0);
+
+ assertNotNull(DirectoryServer.getEntry(DN.decode(
+ "uid=rogasawara," + OLDBASE + "," + SUFFIX)));
+ assertFalse(DirectoryServer.getSubentryManager().getCollectiveSubentries(
+ DN.decode("uid=rogasawara," + OLDBASE + "," + SUFFIX)).isEmpty());
+ assertFalse(DirectoryServer.getSubentryManager().getSubentries(
+ DN.decode("uid=rogasawara," + OLDBASE + "," + SUFFIX)).isEmpty());
+ }
+
+ private void addTestEntries() throws Exception
+ {
+ InternalClientConnection connection =
+ InternalClientConnection.getRootConnection();
+
+ // Add suffix entry.
+ DN suffixDN = DN.decode(SUFFIX);
+ if (DirectoryServer.getEntry(suffixDN) == null)
+ {
+ Entry suffixEntry = StaticUtils.createEntry(suffixDN);
+ AddOperation addOperation =
+ connection.processAdd(suffixEntry.getDN(),
+ suffixEntry.getObjectClasses(),
+ suffixEntry.getUserAttributes(),
+ suffixEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNotNull(DirectoryServer.getEntry(suffixEntry.getDN()));
+ }
+
+ // Add base entry.
+ DN baseDN = DN.decode(BASE);
+ if (DirectoryServer.getEntry(baseDN) == null)
+ {
+ Entry baseEntry = StaticUtils.createEntry(baseDN);
+ AddOperation addOperation =
+ connection.processAdd(baseEntry.getDN(),
+ baseEntry.getObjectClasses(),
+ baseEntry.getUserAttributes(),
+ baseEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNotNull(DirectoryServer.getEntry(baseEntry.getDN()));
+ }
+
+ // Add test entry.
+ testEntry = TestCaseUtils.makeEntry(
+ "dn: uid=rogasawara," + BASE,
+ "objectclass: top",
+ "objectclass: person",
+ "objectclass: organizationalPerson",
+ "objectclass: inetOrgPerson",
+ "uid: rogasawara",
+ "userpassword: password",
+ "mail: rogasawara@example.com",
+ "givenname: Rodney",
+ "sn: Ogasawara",
+ "cn: Rodney Ogasawara",
+ "title: Sales, Director"
+ );
+ AddOperation addOperation =
+ connection.processAdd(testEntry.getDN(),
+ testEntry.getObjectClasses(),
+ testEntry.getUserAttributes(),
+ testEntry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNotNull(DirectoryServer.getEntry(testEntry.getDN()));
+
+ // Add test subentry.
+ ldapSubentry = TestCaseUtils.makeEntry(
+ "dn: cn=Subentry," + SUFFIX,
+ "objectClass: top",
+ "objectclass: subentry",
+ "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}",
+ "cn: Subentry");
+ addOperation =
+ connection.processAdd(ldapSubentry.getDN(),
+ ldapSubentry.getObjectClasses(),
+ ldapSubentry.getUserAttributes(),
+ ldapSubentry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNotNull(DirectoryServer.getEntry(ldapSubentry.getDN()));
+
+ // Add test collective subentry.
+ collectiveSubentry = TestCaseUtils.makeEntry(
+ "dn: cn=Collective Subentry," + SUFFIX,
+ "objectClass: top",
+ "objectclass: subentry",
+ "objectClass: collectiveAttributeSubentry",
+ "objectClass: extensibleObject",
+ "c-l: Savoie",
+ "preferredLanguage;collective: fr",
+ "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}",
+ "cn: Collective Subentry");
+ addOperation =
+ connection.processAdd(collectiveSubentry.getDN(),
+ collectiveSubentry.getObjectClasses(),
+ collectiveSubentry.getUserAttributes(),
+ collectiveSubentry.getOperationalAttributes());
+ assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+ assertNotNull(DirectoryServer.getEntry(collectiveSubentry.getDN()));
+ }
}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java
index 90453af..b5e1e49 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java
@@ -144,15 +144,13 @@
/**
- * Test that a delete subtree changes the correct entries under
- * the correct suffixes.
- *
- * FIXME: re-enable when CR 6959320 is fixed.
+ * Test that a delete subtree changes the correct entries
+ * under the correct suffixes.
*
* @throws Exception If an unexpected result is returned.
*
*/
- @Test(enabled=false)
+ @Test()
public void testReferentialDeleteTree() throws Exception {
// Add attributes interested in: member, uniquemember, seealso.
replaceAttrEntry(configDN, dsConfigAttrType,"member");
@@ -181,12 +179,8 @@
// Perform the subtree delete.
deleteSubtree(oldSuperior);
- // Check group membership before delete.
- //
- // This simply checks that the group cache is updated
- // rather than RI plugin works (it fails at the moment).
- //
- // isMember(tgroup, false, user1, user2, user3);
+ // Check that the group cache is updated.
+ isMember(tgroup, false, user1, user2, user3);
// Check values exist as before delete.
isAttributeValueEntry(tgroup, false, "member", user1, user2, user3);
--
Gitblit v1.10.0