From 6912362c6715306768c8fc14dd18ad9293e4c2ca Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Fri, 25 Dec 2009 18:28:32 +0000
Subject: [PATCH] - land Subentry Manager and Collective Attributes implementations; Merry XMAS to yall!
---
opends/resource/schema/02-config.ldif | 5
opends/src/messages/messages/core.properties | 3
opends/src/server/org/opends/server/types/Entry.java | 264 ++++++++
opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java | 153 +++++
opends/src/messages/messages/schema.properties | 6
opends/src/server/org/opends/server/core/SubentryManager.java | 647 +++++++++++++++++++++
opends/src/server/org/opends/server/types/CollectiveVirtualAttribute.java | 131 ++++
opends/tests/unit-tests-testng/src/server/org/opends/server/schema/AttributeTypeSyntaxTest.java | 6
opends/resource/config/config.ldif | 10
opends/src/server/org/opends/server/types/SubEntry.java | 281 +++++++++
opends/src/server/org/opends/server/util/ServerConstants.java | 92 +++
opends/src/server/org/opends/server/core/DirectoryServer.java | 22
opends/src/server/org/opends/server/core/SearchOperationBasis.java | 8
opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java | 25
opends/resource/schema/00-core.ldif | 43 +
opends/src/messages/messages/extension.properties | 3
opends/src/admin/defn/org/opends/server/admin/std/CollectiveAttributeSubentriesVirtualAttributeConfiguration.xml | 70 ++
17 files changed, 1,737 insertions(+), 32 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 8d1599f..a9ad022 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -2460,6 +2460,16 @@
ds-cfg-filter: (&(objectClass=groupOfUniqueNames)(objectClass=ds-virtual-static-group))
ds-cfg-allow-retrieving-membership: false
+dn: cn=Collective Attribute Subentries,cn=Virtual Attributes,cn=config
+objectClass: top
+objectClass: ds-cfg-virtual-attribute
+objectClass: ds-cfg-collective-attribute-subentries-virtual-attribute
+cn: Collective Attribute Subentries
+ds-cfg-java-class: org.opends.server.extensions.CollectiveAttributeSubentriesVirtualAttributeProvider
+ds-cfg-enabled: true
+ds-cfg-attribute-type: collectiveAttributeSubentries
+ds-cfg-conflict-behavior: virtual-overrides-real
+
dn: cn=Work Queue,cn=config
objectClass: top
objectClass: ds-cfg-work-queue
diff --git a/opends/resource/schema/00-core.ldif b/opends/resource/schema/00-core.ldif
index 606a8ad..c8b66a8 100644
--- a/opends/resource/schema/00-core.ldif
+++ b/opends/resource/schema/00-core.ldif
@@ -381,6 +381,43 @@
attributeTypes: ( 1.3.6.1.4.1.7628.5.4.2 NAME 'blockInheritance'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE NO-USER-MODIFICATION
USAGE dSAOperation X-ORIGIN 'draft-ietf-ldup-subentry' )
+attributeTypes: ( 2.5.18.6 NAME 'subtreeSpecification'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.45 SINGLE-VALUE
+ USAGE directoryOperation X-ORIGIN 'RFC 3672' )
+attributeTypes: ( 2.5.18.12 NAME 'collectiveAttributeSubentries'
+ EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ USAGE directoryOperation NO-USER-MODIFICATION X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.18.7 NAME 'collectiveExclusions'
+ EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+ USAGE directoryOperation X-ORIGIN 'RFC 3671' )
+ldapSyntaxes: ( 1.3.6.1.4.1.26027.1.3.6 DESC 'Collective Conflict Behavior'
+ X-ENUM ( 'real-overrides-virtual' 'virtual-overrides-real'
+ 'merge-real-and-virtual' ) )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.606
+ NAME 'collectiveConflictBehavior' SYNTAX 1.3.6.1.4.1.26027.1.3.6
+ SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 2.5.4.7.1 NAME 'c-l' SUP l COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.8.1 NAME 'c-st' SUP st COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.9.1 NAME 'c-street' SUP street COLLECTIVE
+ X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.10.1 NAME 'c-o' SUP o COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.11.1 NAME 'c-ou' SUP ou COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.16.1 NAME 'c-PostalAddress' SUP postalAddress
+ COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.17.1 NAME 'c-PostalCode' SUP postalCode COLLECTIVE
+ X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.18.1 NAME 'c-PostOfficeBox' SUP postOfficeBox
+ COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.19.1 NAME 'c-PhysicalDeliveryOfficeName'
+ SUP physicalDeliveryOfficeName COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.20.1 NAME 'c-TelephoneNumber' SUP telephoneNumber
+ COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.21.1 NAME 'c-TelexNumber' SUP telexNumber COLLECTIVE
+ X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.23.1 NAME 'c-FacsimileTelephoneNumber'
+ SUP facsimileTelephoneNumber COLLECTIVE X-ORIGIN 'RFC 3671' )
+attributeTypes: ( 2.5.4.25.1 NAME 'c-InternationalISDNNumber'
+ SUP internationalISDNNumber COLLECTIVE X-ORIGIN 'RFC 3671' )
attributeTypes: ( 2.16.840.1.113730.3.1.55 NAME 'aci'
DESC 'Sun-defined access control information attribute type'
EQUALITY octetStringMatch
@@ -613,6 +650,12 @@
DESC 'Inheritable LDAP Subentry class, version 1' SUP ldapSubEntry
STRUCTURAL MUST ( inheritable ) MAY ( blockInheritance )
X-ORIGIN 'draft-ietf-ldup-subentry' )
+objectClasses: ( 2.5.17.0 NAME 'subentry'
+ DESC 'LDAP Subentry class' SUP top STRUCTURAL MUST ( cn $
+ subtreeSpecification ) X-ORIGIN 'RFC 3672' )
+objectClasses: ( 2.5.17.2 NAME 'collectiveAttributeSubentry'
+ DESC 'LDAP Collective Attributes Subentry class' AUXILIARY
+ X-ORIGIN 'RFC 3671' )
objectClasses: ( 2.16.840.1.113730.3.2.33 NAME 'groupOfURLs'
DESC 'Sun-defined objectclass' SUP top STRUCTURAL MUST ( cn )
MAY ( memberURL $ businessCategory $ description $ o $ ou $ owner $ seeAlso )
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 05ec7bf..a35daf3 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -4151,4 +4151,9 @@
ds-cfg-enabled )
MAY ( ds-cfg-ecl-include )
X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.235
+ NAME 'ds-cfg-collective-attribute-subentries-virtual-attribute'
+ SUP ds-cfg-virtual-attribute
+ STRUCTURAL
+ X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/CollectiveAttributeSubentriesVirtualAttributeConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/CollectiveAttributeSubentriesVirtualAttributeConfiguration.xml
new file mode 100644
index 0000000..57eab55
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/CollectiveAttributeSubentriesVirtualAttributeConfiguration.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ! CDDL HEADER START
+ !
+ ! The contents of this file are subject to the terms of the
+ ! Common Development and Distribution License, Version 1.0 only
+ ! (the "License"). You may not use this file except in compliance
+ ! with the License.
+ !
+ ! You can obtain a copy of the license at
+ ! trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ ! or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ ! See the License for the specific language governing permissions
+ ! and limitations under the License.
+ !
+ ! When distributing Covered Code, include this CDDL HEADER in each
+ ! file and include the License file at
+ ! trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ ! add the following below this CDDL HEADER, with the fields enclosed
+ ! by brackets "[]" replaced with your own identifying information:
+ ! Portions Copyright [yyyy] [name of copyright owner]
+ !
+ ! CDDL HEADER END
+ !
+ !
+ ! Copyright 2009 Sun Microsystems, Inc.
+ ! -->
+<adm:managed-object name="collective-attribute-subentries-virtual-attribute"
+ plural-name="collective-attribute-subentries-virtual-attributes"
+ package="org.opends.server.admin.std" extends="virtual-attribute"
+ xmlns:adm="http://www.opends.org/admin"
+ xmlns:ldap="http://www.opends.org/admin-ldap">
+ <adm:synopsis>
+ The
+ <adm:user-friendly-name />
+ generates a virtual attribute that specifies all collective
+ attribute subentries that affect the entry.
+ </adm:synopsis>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:name>
+ ds-cfg-collective-attribute-subentries-virtual-attribute
+ </ldap:name>
+ <ldap:superior>ds-cfg-virtual-attribute</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+ <adm:property-override name="java-class" advanced="true">
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>
+ org.opends.server.extensions.CollectiveAttributeSubentriesVirtualAttributeProvider
+ </adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ </adm:property-override>
+ <adm:property-override name="conflict-behavior" advanced="true">
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>virtual-overrides-real</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ </adm:property-override>
+ <adm:property-override name="attribute-type">
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>collectiveAttributeSubentries</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ </adm:property-override>
+</adm:managed-object>
diff --git a/opends/src/messages/messages/core.properties b/opends/src/messages/messages/core.properties
index 686477e..17052e4 100644
--- a/opends/src/messages/messages/core.properties
+++ b/opends/src/messages/messages/core.properties
@@ -1825,3 +1825,6 @@
60 seconds. This option cannot be used with the -N, --nodetach option
FATAL_ERR_DSCORE_ERROR_NODETACH_TIMEOUT_723=In no-detach mode, the 'timeout' \
option cannot be used
+SEVERE_WARN_SUBENTRY_FILTER_NOT_INDEXED_724=The search filter "%s" used by \
+ subentry manager is not indexed in backend %s. Backend initialization \
+ for subentry manager processing might take a very long time to complete
diff --git a/opends/src/messages/messages/extension.properties b/opends/src/messages/messages/extension.properties
index e63b8aa..9f200ea 100644
--- a/opends/src/messages/messages/extension.properties
+++ b/opends/src/messages/messages/extension.properties
@@ -1422,3 +1422,6 @@
INFO_GSSAPI_STARTED_574=The GSSAPI SASL mechanism handler initialization \
was successful
INFO_GSSAPI_STOPPED_575=The GSSAPI SASL mechanism handler has been stopped
+MILD_ERR_COLLECTIVEATTRIBUTESUBENTRIES_VATTR_NOT_SEARCHABLE_576=The %s \
+ attribute is not searchable and should not be included in otherwise \
+ unindexed search filters
diff --git a/opends/src/messages/messages/schema.properties b/opends/src/messages/messages/schema.properties
index 2fff3d5..15c5608 100644
--- a/opends/src/messages/messages/schema.properties
+++ b/opends/src/messages/messages/schema.properties
@@ -881,9 +881,6 @@
SEVERE_WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_SUPERIOR_USAGE_268=The definition \
for attribute type %s is invalid because its attribute usage %s is not the \
same as the usage for its superior type %s
-SEVERE_WARN_ATTR_SYNTAX_ATTRTYPE_COLLECTIVE_FROM_NONCOLLECTIVE_269=The \
- definition for attribute type %s is invalid because it is defined as a \
- collective type but the superior type %s is not collective
SEVERE_WARN_ATTR_SYNTAX_ATTRTYPE_NONCOLLECTIVE_FROM_COLLECTIVE_270=The \
definition for attribute type %s is invalid because it is not defined as a \
collective type but the superior type %s is collective
@@ -893,9 +890,6 @@
MILD_ERR_ATTR_SYNTAX_DCR_PROHIBITED_REQUIRED_BY_AUXILIARY_272=The DIT content \
rule "%s" is not valid because it prohibits the use of attribute type %s \
which is required by the associated auxiliary object class %s
-SEVERE_WARN_ATTR_SYNTAX_ATTRTYPE_COLLECTIVE_IS_OPERATIONAL_273=The definition \
- for attribute type %s is invalid because it is declared COLLECTIVE but does \
- not have a usage of userApplications
SEVERE_WARN_ATTR_SYNTAX_ATTRTYPE_NO_USER_MOD_NOT_OPERATIONAL_274=The \
definition for attribute type %s is invalid because it is declared \
NO-USER-MODIFICATION but does not have an operational usage
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 62d23f2..3f3659d 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -594,6 +594,9 @@
// The group manager for the Directory Server.
private GroupManager groupManager;
+ // The subentry manager for the Directory Server.
+ private SubentryManager subentryManager;
+
// The configuration manager for identity mappers.
private IdentityMapperConfigManager identityMapperConfigManager;
@@ -1420,6 +1423,13 @@
rootDNConfigManager.initializeRootDNs();
+ // Initialize the subentry manager.
+ subentryManager = new SubentryManager();
+ // The configuration backend has already been registered at this point
+ // so we need to handle it explicitly.
+ subentryManager.performBackendInitializationProcessing(configHandler);
+
+
// Initialize the group manager.
initializeGroupManager();
@@ -2671,6 +2681,18 @@
/**
+ * Retrieves the Directory Server subentry manager.
+ *
+ * @return The Directory Server subentry manager.
+ */
+ public static SubentryManager getSubentryManager()
+ {
+ return directoryServer.subentryManager;
+ }
+
+
+
+ /**
* Initializes the set of supported controls for the Directory Server.
*
* @throws ConfigException If there is a configuration problem with the
diff --git a/opends/src/server/org/opends/server/core/SearchOperationBasis.java b/opends/src/server/org/opends/server/core/SearchOperationBasis.java
index 5a8d35f..d1d2ba1 100644
--- a/opends/src/server/org/opends/server/core/SearchOperationBasis.java
+++ b/opends/src/server/org/opends/server/core/SearchOperationBasis.java
@@ -609,7 +609,7 @@
// Determine whether the provided entry is a subentry and if so whether it
// should be returned.
- if (entry.isLDAPSubentry())
+ if (entry.isSubentry() || entry.isLDAPSubentry())
{
if ((getScope() != SearchScope.BASE_OBJECT) &&
(! isReturnLDAPSubentries()))
@@ -1494,7 +1494,11 @@
if (filter.getAttributeType().isObjectClassType())
{
AttributeValue v = filter.getAssertionValue();
- if (toLowerCase(v.getValue().toString()).equals("ldapsubentry"))
+ // FIXME : technically this is not correct since the presense
+ // of draft oc would trigger rfc oc visibility and visa versa.
+ String stringValueLC = toLowerCase(v.getValue().toString());
+ if (stringValueLC.equals(OC_LDAP_SUBENTRY_LC) ||
+ stringValueLC.equals(OC_SUBENTRY))
{
setReturnLDAPSubentries(true);
}
diff --git a/opends/src/server/org/opends/server/core/SubentryManager.java b/opends/src/server/org/opends/server/core/SubentryManager.java
new file mode 100644
index 0000000..008abcf
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/SubentryManager.java
@@ -0,0 +1,647 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.util.*;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.opends.server.api.Backend;
+import org.opends.server.api.BackendInitializationListener;
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.controls.SubentriesControl;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Control;
+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.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SubEntry;
+import org.opends.server.types.operation.PostResponseAddOperation;
+import org.opends.server.types.operation.PostResponseDeleteOperation;
+import org.opends.server.types.operation.PostResponseModifyOperation;
+import org.opends.server.types.operation.PostResponseModifyDNOperation;
+import org.opends.server.workflowelement.localbackend.
+ LocalBackendSearchOperation;
+
+import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.config.ConfigConstants.*;
+
+
+
+/**
+ * This class provides a mechanism for interacting with subentries defined in
+ * the Directory Server. It will handle all necessary processing at server
+ * startup to identify and load subentries within the server.
+ * <BR><BR>
+ * FIXME: At the present time, it assumes that all of the necessary
+ * information about subentries defined in the server can be held in
+ * memory. If it is determined that this approach is not workable
+ * in all cases, then we will need an alternate strategy.
+ */
+public class SubentryManager
+ implements BackendInitializationListener, ChangeNotificationListener
+{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ // A mapping between the DNs and applicable subentries.
+ private HashMap<DN,List<SubEntry>> dn2SubEntry;
+
+ // A mapping between the DNs and applicable collective subentries.
+ private HashMap<DN,List<SubEntry>> dn2CollectiveSubEntry;
+
+ // Internal search all operational attributes.
+ private LinkedHashSet<String> requestAttrs;
+
+ // Lock to protect internal data structures.
+ private final ReentrantReadWriteLock lock;
+
+ /**
+ * Creates a new instance of this group manager.
+ */
+ public SubentryManager()
+ {
+ lock = new ReentrantReadWriteLock();
+
+ dn2SubEntry = new HashMap<DN,List<SubEntry>>();
+ dn2CollectiveSubEntry = new HashMap<DN,List<SubEntry>>();
+
+ requestAttrs = new LinkedHashSet<String>();
+ requestAttrs.add("subtreespecification");
+ requestAttrs.add("*");
+
+ DirectoryServer.registerBackendInitializationListener(this);
+ DirectoryServer.registerChangeNotificationListener(this);
+ }
+
+ /**
+ * Add a given entry to this subentry manager.
+ * @param entry to add.
+ */
+ private void addSubEntry(Entry entry) throws DirectoryException
+ {
+ SubEntry subEntry = new SubEntry(entry);
+ RFC3672SubtreeSpecification subSpec =
+ subEntry.getSubTreeSpecification();
+ DN subDN = subSpec.getBaseDN();
+ List<SubEntry> subList = null;
+ lock.writeLock().lock();
+ try
+ {
+ if (subEntry.isCollective())
+ {
+ subList = dn2CollectiveSubEntry.get(subDN);
+ }
+ else
+ {
+ subList = dn2SubEntry.get(subDN);
+ }
+ if (subList == null)
+ {
+ subList = new ArrayList<SubEntry>();
+ if (subEntry.isCollective())
+ {
+ dn2CollectiveSubEntry.put(subDN, subList);
+ }
+ else
+ {
+ dn2SubEntry.put(subDN, subList);
+ }
+ }
+ subList.add(subEntry);
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Remove a given entry from this subentry manager.
+ * @param entry to remove.
+ */
+ private void removeSubEntry(Entry entry)
+ {
+ lock.writeLock().lock();
+ try
+ {
+ boolean removed = false;
+ Iterator<Map.Entry<DN, List<SubEntry>>> iterator =
+ dn2SubEntry.entrySet().iterator();
+ while (iterator.hasNext())
+ {
+ Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ List<SubEntry> subList = mapEntry.getValue();
+ for (SubEntry subEntry : subList)
+ {
+ if (subEntry.getDN().equals(entry.getDN()))
+ {
+ removed = subList.remove(subEntry);
+ break;
+ }
+ }
+ if (subList.isEmpty())
+ {
+ iterator.remove();
+ }
+ if (removed)
+ {
+ return;
+ }
+ }
+ iterator = dn2CollectiveSubEntry.entrySet().iterator();
+ while (iterator.hasNext())
+ {
+ Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ List<SubEntry> subList = mapEntry.getValue();
+ for (SubEntry subEntry : subList)
+ {
+ if (subEntry.getDN().equals(entry.getDN()))
+ {
+ removed = subList.remove(subEntry);
+ break;
+ }
+ }
+ if (subList.isEmpty())
+ {
+ iterator.remove();
+ }
+ if (removed)
+ {
+ return;
+ }
+ }
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc} In this case, the server will search the backend to find
+ * all subentries that it may contain and register them with this manager.
+ */
+ public void performBackendInitializationProcessing(Backend backend)
+ {
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ LinkedList<Control> requestControls = new LinkedList<Control>();
+ requestControls.add(new SubentriesControl(true, true));
+
+ SearchFilter filter = null;
+ try
+ {
+ filter = SearchFilter.createFilterFromString("(" +
+ ATTR_OBJECTCLASS + "=" + OC_SUBENTRY + ")");
+ if (backend.getEntryCount() > 0 && ! backend.isIndexed(filter))
+ {
+ logError(WARN_SUBENTRY_FILTER_NOT_INDEXED.get(
+ String.valueOf(filter), backend.getBackendID()));
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ for (DN baseDN : backend.getBaseDNs())
+ {
+ try
+ {
+ if (! backend.entryExists(baseDN))
+ {
+ continue;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // FIXME -- Is there anything that we need to do here?
+ continue;
+ }
+
+ InternalSearchOperation internalSearch = new InternalSearchOperation(
+ conn, InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ requestControls, baseDN, SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, requestAttrs, null);
+ LocalBackendSearchOperation localSearch =
+ new LocalBackendSearchOperation(internalSearch);
+
+ try
+ {
+ backend.search(localSearch);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // FIXME -- Is there anything that we need to do here?
+ continue;
+ }
+
+ for (SearchResultEntry entry : internalSearch.getSearchEntries())
+ {
+ if (entry.isSubentry())
+ {
+ try
+ {
+ addSubEntry(entry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // FIXME -- Handle this.
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Return subentries applicable to specific DN.
+ * Note that this getter will skip any collective subentries,
+ * returning only applicable regular subentries.
+ * @param dn for which to retrieve applicable
+ * subentries.
+ * @return applicable subentries.
+ */
+ public List<SubEntry> getSubentries(DN dn)
+ {
+ if (dn2SubEntry.isEmpty())
+ {
+ return Collections.emptyList();
+ }
+
+ List<SubEntry> subentries = new ArrayList<SubEntry>();
+
+ lock.readLock().lock();
+ try
+ {
+ for (DN subDN = dn; subDN != null;
+ subDN = subDN.getParent())
+ {
+ List<SubEntry> subList = dn2SubEntry.get(subDN);
+ if (subList != null)
+ {
+ for (SubEntry subEntry : subList)
+ {
+ RFC3672SubtreeSpecification subSpec =
+ subEntry.getSubTreeSpecification();
+ if (subSpec.isDNWithinScope(dn))
+ {
+ subentries.add(subEntry);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
+
+ return subentries;
+ }
+
+ /**
+ * Return subentries applicable to specific entry.
+ * Note that this getter will skip any collective subentries,
+ * returning only applicable regular subentries.
+ * @param entry for which to retrieve applicable
+ * subentries.
+ * @return applicable subentries.
+ */
+ public List<SubEntry> getSubentries(Entry entry)
+ {
+ if (dn2SubEntry.isEmpty())
+ {
+ return Collections.emptyList();
+ }
+
+ List<SubEntry> subentries = new ArrayList<SubEntry>();
+
+ lock.readLock().lock();
+ try
+ {
+ for (DN subDN = entry.getDN(); subDN != null;
+ subDN = subDN.getParent())
+ {
+ List<SubEntry> subList = dn2SubEntry.get(subDN);
+ if (subList != null)
+ {
+ for (SubEntry subEntry : subList)
+ {
+ RFC3672SubtreeSpecification subSpec =
+ subEntry.getSubTreeSpecification();
+ if (subSpec.isWithinScope(entry))
+ {
+ subentries.add(subEntry);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
+
+ return subentries;
+ }
+
+ /**
+ * Return collective subentries applicable to specific DN.
+ * Note that this getter will skip any regular subentries,
+ * returning only applicable collective subentries.
+ * @param dn for which to retrieve applicable
+ * subentries.
+ * @return applicable subentries.
+ */
+ public List<SubEntry> getCollectiveSubentries(DN dn)
+ {
+ if (dn2CollectiveSubEntry.isEmpty())
+ {
+ return Collections.emptyList();
+ }
+
+ List<SubEntry> subentries = new ArrayList<SubEntry>();
+
+ lock.readLock().lock();
+ try
+ {
+ for (DN subDN = dn; subDN != null;
+ subDN = subDN.getParent())
+ {
+ List<SubEntry> subList = dn2CollectiveSubEntry.get(subDN);
+ if (subList != null)
+ {
+ for (SubEntry subEntry : subList)
+ {
+ RFC3672SubtreeSpecification subSpec =
+ subEntry.getSubTreeSpecification();
+ if (subSpec.isDNWithinScope(dn))
+ {
+ subentries.add(subEntry);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
+
+ return subentries;
+ }
+
+ /**
+ * Return collective subentries applicable to specific entry.
+ * Note that this getter will skip any regular subentries,
+ * returning only applicable collective subentries.
+ * @param entry for which to retrieve applicable
+ * subentries.
+ * @return applicable subentries.
+ */
+ public List<SubEntry> getCollectiveSubentries(Entry entry)
+ {
+ if (dn2CollectiveSubEntry.isEmpty())
+ {
+ return Collections.emptyList();
+ }
+
+ List<SubEntry> subentries = new ArrayList<SubEntry>();
+
+ lock.readLock().lock();
+ try
+ {
+ for (DN subDN = entry.getDN(); subDN != null;
+ subDN = subDN.getParent())
+ {
+ List<SubEntry> subList = dn2CollectiveSubEntry.get(subDN);
+ if (subList != null)
+ {
+ for (SubEntry subEntry : subList)
+ {
+ RFC3672SubtreeSpecification subSpec =
+ subEntry.getSubTreeSpecification();
+ if (subSpec.isWithinScope(entry))
+ {
+ subentries.add(subEntry);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ lock.readLock().unlock();
+ }
+
+ return subentries;
+ }
+
+ /**
+ * {@inheritDoc} In this case, the server will de-register
+ * all subentries associated with the provided backend.
+ */
+ public void performBackendFinalizationProcessing(Backend backend)
+ {
+ lock.writeLock().lock();
+ try
+ {
+ Iterator<Map.Entry<DN, List<SubEntry>>> iterator =
+ dn2SubEntry.entrySet().iterator();
+ while (iterator.hasNext())
+ {
+ Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ List<SubEntry> subList = mapEntry.getValue();
+ for (SubEntry subEntry : subList)
+ {
+ if (backend.handlesEntry(subEntry.getDN()))
+ {
+ subList.remove(subEntry);
+ }
+ }
+ if (subList.isEmpty())
+ {
+ iterator.remove();
+ }
+ }
+ iterator = dn2CollectiveSubEntry.entrySet().iterator();
+ while (iterator.hasNext())
+ {
+ Map.Entry<DN, List<SubEntry>> mapEntry = iterator.next();
+ List<SubEntry> subList = mapEntry.getValue();
+ for (SubEntry subEntry : subList)
+ {
+ if (backend.handlesEntry(subEntry.getDN()))
+ {
+ subList.remove(subEntry);
+ }
+ }
+ if (subList.isEmpty())
+ {
+ iterator.remove();
+ }
+ }
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc} In this case, each entry is checked to see if it is
+ * a subentry, and if so it will be registered with this manager.
+ */
+ public void handleAddOperation(PostResponseAddOperation addOperation,
+ Entry entry)
+ {
+ if (entry.isSubentry())
+ {
+ try
+ {
+ addSubEntry(entry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // FIXME -- Handle this.
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc} In this case, each entry is checked to see if it is
+ * a subentry, and if so it will be deregistered with this manager.
+ */
+ public void handleDeleteOperation(PostResponseDeleteOperation deleteOperation,
+ Entry entry)
+ {
+ if (entry.isSubentry())
+ {
+ removeSubEntry(entry);
+ }
+ }
+
+ /**
+ * {@inheritDoc} In this case, if the entry is a registered subentry
+ * then it will be recreated from the contents of the provided entry
+ * and re-registered with this manager.
+ */
+ public void handleModifyOperation(PostResponseModifyOperation modifyOperation,
+ Entry oldEntry, Entry newEntry)
+ {
+ if (oldEntry.isSubentry())
+ {
+ removeSubEntry(oldEntry);
+ }
+ if (newEntry.isSubentry())
+ {
+ try
+ {
+ addSubEntry(newEntry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // FIXME -- Handle this.
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc} In this case, if the subentry is registered then it
+ * will be recreated from the contents of the provided entry and re-
+ * registered with this manager under the new DN and the old instance
+ * will be deregistered.
+ */
+ public void handleModifyDNOperation(
+ PostResponseModifyDNOperation modifyDNOperation,
+ Entry oldEntry, Entry newEntry)
+ {
+ if (oldEntry.isSubentry())
+ {
+ removeSubEntry(oldEntry);
+ try
+ {
+ addSubEntry(newEntry);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // FIXME -- Handle this.
+ }
+ }
+ }
+}
diff --git a/opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java
new file mode 100644
index 0000000..c8d9b37
--- /dev/null
+++ b/opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java
@@ -0,0 +1,153 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.extensions;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.std.server.
+ CollectiveAttributeSubentriesVirtualAttributeCfg;
+import org.opends.server.api.VirtualAttributeProvider;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.*;
+
+import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+
+/**
+ * This class implements a virtual attribute provider to serve the
+ * collectiveAttributeSubentries operational attribute as described
+ * in RFC 3671.
+ */
+public class CollectiveAttributeSubentriesVirtualAttributeProvider
+ extends VirtualAttributeProvider<
+ CollectiveAttributeSubentriesVirtualAttributeCfg>
+{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ /**
+ * Creates a new instance of this HasSubordinates virtual attribute provider.
+ */
+ public CollectiveAttributeSubentriesVirtualAttributeProvider()
+ {
+ super();
+
+ // All initialization should be performed in the
+ // initializeVirtualAttributeProvider method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeVirtualAttributeProvider(
+ CollectiveAttributeSubentriesVirtualAttributeCfg configuration)
+ throws ConfigException, InitializationException
+ {
+ // No initialization is required.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isMultiValued()
+ {
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public Set<AttributeValue> getValues(Entry entry,
+ VirtualAttributeRule rule)
+ {
+ Set<AttributeValue> valueSet = new HashSet<AttributeValue>();
+ List<SubEntry> subentries =
+ DirectoryServer.getSubentryManager().getCollectiveSubentries(entry);
+
+ AttributeType dnAttrType =
+ DirectoryServer.getAttributeType("2.5.4.49");
+ for (SubEntry subentry : subentries)
+ {
+ if (subentry.isCollective())
+ {
+ DN subentryDN = subentry.getDN();
+ AttributeValue value = AttributeValues.create(
+ dnAttrType, subentryDN.toString());
+ valueSet.add(value);
+ }
+ }
+
+ return valueSet;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isSearchable(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void processSearch(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ Message message =
+ ERR_COLLECTIVEATTRIBUTESUBENTRIES_VATTR_NOT_SEARCHABLE.get(
+ rule.getAttributeType().getNameOrOID());
+ searchOperation.appendErrorMessage(message);
+ }
+}
diff --git a/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java b/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java
index 579826b..b31af2e 100644
--- a/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java
+++ b/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java
@@ -983,35 +983,20 @@
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
}
- if (superiorType.isCollective() != isCollective)
+ if (superiorType.isCollective())
{
- Message message;
- if (isCollective)
+ if (!isCollective)
{
- message = WARN_ATTR_SYNTAX_ATTRTYPE_COLLECTIVE_FROM_NONCOLLECTIVE.get(
- oid, superiorType.getNameOrOID());
- }
- else
- {
- message =
+ Message message =
WARN_ATTR_SYNTAX_ATTRTYPE_NONCOLLECTIVE_FROM_COLLECTIVE.get(
oid, superiorType.getNameOrOID());
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
+ message);
}
- throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
}
}
- // If the attribute type is COLLECTIVE, then it must have a usage of
- // userApplications.
- if (isCollective && (attributeUsage != AttributeUsage.USER_APPLICATIONS))
- {
- Message message =
- WARN_ATTR_SYNTAX_ATTRTYPE_COLLECTIVE_IS_OPERATIONAL.get(oid);
- throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
- }
-
-
// If the attribute type is NO-USER-MODIFICATION, then it must not have a
// usage of userApplications.
if (isNoUserModification &&
diff --git a/opends/src/server/org/opends/server/types/CollectiveVirtualAttribute.java b/opends/src/server/org/opends/server/types/CollectiveVirtualAttribute.java
new file mode 100644
index 0000000..68e3489
--- /dev/null
+++ b/opends/src/server/org/opends/server/types/CollectiveVirtualAttribute.java
@@ -0,0 +1,131 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.types;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class defines a collective virtual attribute, which is a
+ * special kind of attribute whose values do not actually exist
+ * in persistent storage but rather are obtained dynamically
+ * from applicable collective attribute subentry.
+ */
+public class CollectiveVirtualAttribute extends AbstractAttribute
+{
+ // The attribute this collective virtual attribute is based on.
+ private Attribute attribute;
+
+ /**
+ * Creates a new collective virtual attribute.
+ * @param attribute The attribute this collective
+ * virtual attribute is based on.
+ */
+ public CollectiveVirtualAttribute(Attribute attribute) {
+ this.attribute = attribute;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConditionResult approximatelyEqualTo(AttributeValue value) {
+ return attribute.approximatelyEqualTo(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean contains(AttributeValue value) {
+ return attribute.contains(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public AttributeType getAttributeType() {
+ return attribute.getAttributeType();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<String> getOptions() {
+ return attribute.getOptions();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConditionResult greaterThanOrEqualTo(AttributeValue value) {
+ return attribute.greaterThanOrEqualTo(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isVirtual() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<AttributeValue> iterator() {
+ return attribute.iterator();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConditionResult lessThanOrEqualTo(AttributeValue value) {
+ return attribute.lessThanOrEqualTo(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConditionResult matchesSubstring(ByteString subInitial,
+ List<ByteString> subAny, ByteString subFinal) {
+ return attribute.matchesSubstring(subInitial, subAny, subFinal);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size() {
+ return attribute.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void toString(StringBuilder buffer) {
+ attribute.toString(buffer);
+ }
+}
diff --git a/opends/src/server/org/opends/server/types/Entry.java b/opends/src/server/org/opends/server/types/Entry.java
index 2942527..98350a6 100644
--- a/opends/src/server/org/opends/server/types/Entry.java
+++ b/opends/src/server/org/opends/server/types/Entry.java
@@ -1763,7 +1763,10 @@
{
if (a.optionsEqual(options))
{
- return a.contains(value);
+ if (a.contains(value))
+ {
+ return true;
+ }
}
}
@@ -3304,6 +3307,92 @@
/**
+ * Indicates whether this entry meets the criteria to consider it
+ * an RFC 3672 LDAP subentry (i.e., it contains the "subentry"
+ * objectclass).
+ *
+ * @return <CODE>true</CODE> if this entry meets the criteria to
+ * consider it an RFC 3672 LDAP subentry, or <CODE>false
+ * </CODE> if not.
+ */
+ public boolean isSubentry()
+ {
+ ObjectClass subentryOC =
+ DirectoryServer.getObjectClass(OC_SUBENTRY);
+ if (subentryOC == null)
+ {
+ // This should not happen -- The server doesn't
+ // have a subentry objectclass defined.
+ if (debugEnabled())
+ {
+ TRACER.debugWarning(
+ "No %s objectclass is defined in the server schema.",
+ OC_SUBENTRY);
+ }
+
+ for (String ocName : objectClasses.values())
+ {
+ if (ocName.equalsIgnoreCase(OC_SUBENTRY))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ // Make the determination based on whether this
+ // entry has the subentry objectclass.
+ return objectClasses.containsKey(subentryOC);
+ }
+
+
+
+ /**
+ * Indicates whether the entry meets the criteria to consider it an
+ * RFC 3671 LDAP collective attributes subentry (i.e., it contains
+ * the "collectiveAttributeSubentry" objectclass).
+ *
+ * @return <CODE>true</CODE> if this entry meets the criteria to
+ * consider it an RFC 3671 LDAP collective attributes
+ * subentry, or <CODE>false</CODE> if not.
+ */
+ public boolean isCollectiveAttributeSubentry()
+ {
+ ObjectClass collectiveAttributeSubentryOC =
+ DirectoryServer.getObjectClass(OC_COLLECTIVE_ATTR_SUBENTRY);
+ if (collectiveAttributeSubentryOC == null)
+ {
+ // This should not happen -- The server doesn't have
+ // a collectiveAttributeSubentry objectclass defined.
+ if (debugEnabled())
+ {
+ TRACER.debugWarning(
+ "No %s objectclass is defined in the server schema.",
+ OC_COLLECTIVE_ATTR_SUBENTRY);
+ }
+
+ for (String ocName : objectClasses.values())
+ {
+ if (ocName.equalsIgnoreCase(OC_COLLECTIVE_ATTR_SUBENTRY))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ // Make the determination based on whether this entry
+ // has the collectiveAttributeSubentry objectclass.
+ return objectClasses.containsKey(collectiveAttributeSubentryOC);
+ }
+
+
+
+ /**
* Indicates whether this entry falls within the range of the
* provided search base DN and scope.
*
@@ -3322,12 +3411,185 @@
/**
+ * Performs any necessary collective attribute processing for this
+ * entry. This should only be called at the time the entry is
+ * decoded or created within the backend.
+ */
+ private void processCollectiveAttributes()
+ {
+ if (this.isSubentry() || this.isLDAPSubentry())
+ {
+ return;
+ }
+
+ // Get applicable collective subentries.
+ List<SubEntry> collectiveAttrSubentries =
+ DirectoryServer.getSubentryManager(
+ ).getCollectiveSubentries(this);
+ if ((collectiveAttrSubentries == null) ||
+ collectiveAttrSubentries.isEmpty())
+ {
+ // Nothing to see here, move along.
+ return;
+ }
+
+ // Get collective attribute exclusions.
+ AttributeType exclusionsType = DirectoryServer.getAttributeType(
+ ATTR_COLLECTIVE_EXCLUSIONS_LC);
+ List<Attribute> exclusionsAttrList =
+ operationalAttributes.get(exclusionsType);
+ Set<String> exclusionsNameSet = new HashSet<String>();
+ if ((exclusionsAttrList != null) && !exclusionsAttrList.isEmpty())
+ {
+ for (Attribute attr : exclusionsAttrList)
+ {
+ for (AttributeValue attrValue : attr)
+ {
+ String exclusionsName = attrValue.toString().toLowerCase();
+ if (exclusionsName.equals(
+ VALUE_COLLECTIVE_EXCLUSIONS_EXCLUDE_ALL_LC) ||
+ exclusionsName.equals(
+ OID_COLLECTIVE_EXCLUSIONS_EXCLUDE_ALL))
+ {
+ return;
+ }
+ exclusionsNameSet.add(exclusionsName);
+ }
+ }
+ }
+
+ // Process collective attributes.
+ for (SubEntry subEntry : collectiveAttrSubentries)
+ {
+ if (subEntry.isCollective())
+ {
+ List<Attribute> collectiveAttrList =
+ subEntry.getCollectiveAttributes();
+ for (Attribute collectiveAttr : collectiveAttrList)
+ {
+ AttributeType attributeType =
+ collectiveAttr.getAttributeType();
+ if (exclusionsNameSet.contains(
+ attributeType.getNormalizedPrimaryNameOrOID()))
+ {
+ continue;
+ }
+ List<Attribute> attrList =
+ userAttributes.get(attributeType);
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ attrList = operationalAttributes.get(attributeType);
+ if ((attrList == null) || attrList.isEmpty())
+ {
+ // There aren't any conflicts, so we can just add the
+ // attribute to the entry.
+ attrList = new LinkedList<Attribute>();
+ attrList.add(collectiveAttr);
+ if (attributeType.isOperational())
+ {
+ operationalAttributes.put(attributeType, attrList);
+ }
+ else
+ {
+ userAttributes.put(attributeType, attrList);
+ }
+ }
+ else
+ {
+ // There is a conflict with an existing operational
+ // attribute.
+ if (attrList.get(0).isVirtual())
+ {
+ // The existing attribute is already virtual,
+ // so we've got a different conflict, but
+ // we'll let the first win.
+ // FIXME -- Should we handle this differently?
+ continue;
+ }
+
+ // The conflict is with a real attribute. See what the
+ // conflict behavior is and figure out how to handle it.
+ switch (subEntry.getConflictBehavior())
+ {
+ case REAL_OVERRIDES_VIRTUAL:
+ // We don't need to update the entry because
+ // the real attribute will take precedence.
+ break;
+
+ case VIRTUAL_OVERRIDES_REAL:
+ // We need to move the real attribute to the
+ // suppressed list and replace it with the
+ // virtual attribute.
+ suppressedAttributes.put(attributeType, attrList);
+ attrList = new LinkedList<Attribute>();
+ attrList.add(collectiveAttr);
+ operationalAttributes.put(attributeType, attrList);
+ break;
+
+ case MERGE_REAL_AND_VIRTUAL:
+ // We need to add the virtual attribute to the
+ // list and keep the existing real attribute(s).
+ attrList.add(collectiveAttr);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // There is a conflict with an existing user attribute.
+ if (attrList.get(0).isVirtual())
+ {
+ // The existing attribute is already virtual,
+ // so we've got a different conflict, but
+ // we'll let the first win.
+ // FIXME -- Should we handle this differently?
+ continue;
+ }
+
+ // The conflict is with a real attribute. See what the
+ // conflict behavior is and figure out how to handle it.
+ switch (subEntry.getConflictBehavior())
+ {
+ case REAL_OVERRIDES_VIRTUAL:
+ // We don't need to update the entry because the real
+ // attribute will take precedence.
+ break;
+
+ case VIRTUAL_OVERRIDES_REAL:
+ // We need to move the real attribute to the
+ // suppressed list and replace it with the
+ // virtual attribute.
+ suppressedAttributes.put(attributeType, attrList);
+ attrList = new LinkedList<Attribute>();
+ attrList.add(collectiveAttr);
+ userAttributes.put(attributeType, attrList);
+ break;
+
+ case MERGE_REAL_AND_VIRTUAL:
+ // We need to add the virtual attribute to the
+ // list and keep the existing real attribute(s).
+ attrList.add(collectiveAttr);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /**
* Performs any necessary virtual attribute processing for this
* entry. This should only be called at the time the entry is
* decoded or created within the backend.
*/
public void processVirtualAttributes()
{
+ // Collective attributes.
+ processCollectiveAttributes();
+
+ // Virtual attributes.
for (VirtualAttributeRule rule :
DirectoryServer.getVirtualAttributes(this))
{
diff --git a/opends/src/server/org/opends/server/types/SubEntry.java b/opends/src/server/org/opends/server/types/SubEntry.java
new file mode 100644
index 0000000..ef93ea0
--- /dev/null
+++ b/opends/src/server/org/opends/server/types/SubEntry.java
@@ -0,0 +1,281 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.types;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.LinkedHashSet;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.RFC3672SubtreeSpecification;
+
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+/**
+ * This class represents RFC 3672 subentries and RFC 3671
+ * collective attribute subentries objects.
+ */
+public class SubEntry {
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ /**
+ * Defines the set of permissable values for the conflict behavior.
+ * Specifies the behavior that the server is to exhibit for entries
+ * that already contain one or more real values for the associated
+ * collective attribute.
+ */
+ public static enum CollectiveConflictBehavior {
+ /**
+ * Indicates that the virtual attribute provider is to preserve
+ * any real values contained in the entry and merge them with the
+ * set of generated virtual values so that both the real and
+ * virtual values are used.
+ */
+ MERGE_REAL_AND_VIRTUAL("merge-real-and-virtual"),
+
+ /**
+ * Indicates that any real values contained in the entry are
+ * preserved and used, and virtual values are not generated.
+ */
+ REAL_OVERRIDES_VIRTUAL("real-overrides-virtual"),
+
+ /**
+ * Indicates that the virtual attribute provider suppresses any
+ * real values contained in the entry and generates virtual values
+ * and uses them.
+ */
+ VIRTUAL_OVERRIDES_REAL("virtual-overrides-real");
+
+ // String representation of the value.
+ private final String name;
+
+ /**
+ * Private constructor.
+ * @param name for this conflict behavior.
+ */
+ private CollectiveConflictBehavior(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+ }
+
+ /**
+ * The name of the "collectiveConflictBehavior" attribute type,
+ * formatted in all lowercase characters.
+ */
+ public static final String ATTR_COLLECTIVE_CONFLICT_BEHAVIOR =
+ "collectiveconflictbehavior";
+
+ // Attribute option to mark attributes collective.
+ private static final String ATTR_OPTION_COLLECTIVE =
+ "collective";
+
+ // Entry object.
+ private Entry entry;
+
+ // Subtree specification.
+ private RFC3672SubtreeSpecification subTreeSpec;
+
+ // Collective subentry flag.
+ private boolean isCollective = false;
+
+ // Collective attributes.
+ private List<Attribute> collectiveAttributes;
+
+ // Conflict behavior.
+ private CollectiveConflictBehavior conflictBehavior =
+ CollectiveConflictBehavior.REAL_OVERRIDES_VIRTUAL;
+
+ /**
+ * Constructs a subentry object from a given entry object.
+ * @param entry LDAP subentry to construct from.
+ * @throws DirectoryException if there is a problem with
+ * constructing a subentry from a given entry.
+ */
+ public SubEntry(Entry entry) throws DirectoryException
+ {
+ // Entry object.
+ this.entry = entry;
+
+ // Process subtree specification.
+ this.subTreeSpec = null;
+ AttributeType specAttrType = DirectoryServer.getAttributeType(
+ ATTR_SUBTREE_SPEC_LC, true);
+ List<Attribute> specAttrList =
+ entry.getAttribute(specAttrType);
+ for (Attribute attr : specAttrList)
+ {
+ for (AttributeValue value : attr)
+ {
+ this.subTreeSpec = RFC3672SubtreeSpecification.valueOf(
+ entry.getDN().getParent(), value.toString());
+ break;
+ }
+ if (this.subTreeSpec != null)
+ {
+ break;
+ }
+ }
+ // Subentry has to to have a subtree specification.
+ if (this.subTreeSpec == null)
+ {
+ // There is none for some reason so create a dummy.
+ this.subTreeSpec = new RFC3672SubtreeSpecification(
+ entry.getDN().getParent(), null, -1, -1,
+ null, null, null);
+ }
+
+ // Determine if this subentry is collective attribute subentry.
+ this.isCollective = entry.isCollectiveAttributeSubentry();
+
+ // Process collective attributes.
+ this.collectiveAttributes = new ArrayList<Attribute>();
+ if (this.isCollective)
+ {
+ List<Attribute> subAttrList = entry.getAttributes();
+ for (Attribute subAttr : subAttrList)
+ {
+ AttributeType attrType = subAttr.getAttributeType();
+ if (attrType.isCollective())
+ {
+ CollectiveVirtualAttribute collectiveAttr =
+ new CollectiveVirtualAttribute(subAttr);
+ this.collectiveAttributes.add(collectiveAttr);
+ }
+ else if (subAttr.hasOption(ATTR_OPTION_COLLECTIVE))
+ {
+ AttributeBuilder builder = new AttributeBuilder(
+ subAttr.getAttributeType());
+ builder.addAll(subAttr);
+ Set<String> options = new LinkedHashSet<String>(
+ subAttr.getOptions());
+ options.remove(ATTR_OPTION_COLLECTIVE);
+ builder.setOptions(options);
+ Attribute attr = builder.toAttribute();
+ CollectiveVirtualAttribute collectiveAttr =
+ new CollectiveVirtualAttribute(attr);
+ this.collectiveAttributes.add(collectiveAttr);
+ }
+ }
+ // Conflict behavior.
+ List<Attribute> attrList = entry.getAttribute(
+ ATTR_COLLECTIVE_CONFLICT_BEHAVIOR);
+ if ((attrList != null) && !attrList.isEmpty())
+ {
+ for (Attribute attr : attrList)
+ {
+ for (AttributeValue value : attr)
+ {
+ for (CollectiveConflictBehavior behavior :
+ CollectiveConflictBehavior.values())
+ {
+ if (behavior.toString().equals(value.toString()))
+ {
+ this.conflictBehavior = behavior;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieves the distinguished name for this subentry.
+ * @return The distinguished name for this subentry.
+ */
+ public DN getDN()
+ {
+ return this.entry.getDN();
+ }
+
+ /**
+ * Getter to retrieve the actual entry object
+ * for this subentry.
+ * @return entry object for this subentry.
+ */
+ public Entry getEntry()
+ {
+ return this.entry;
+ }
+
+ /**
+ * Indicates whether or not this subentry is
+ * a collective attribute subentry.
+ * @return <code>true</code> if collective,
+ * <code>false</code> otherwise.
+ */
+ public boolean isCollective()
+ {
+ return this.isCollective;
+ }
+
+ /**
+ * Getter for subentry subtree specification.
+ * @return subtree specification for this subentry.
+ */
+ public RFC3672SubtreeSpecification getSubTreeSpecification()
+ {
+ return this.subTreeSpec;
+ }
+
+ /**
+ * Getter for collective attributes contained within this subentry.
+ * @return collective attributes contained within this subentry.
+ */
+ public List<Attribute> getCollectiveAttributes()
+ {
+ return this.collectiveAttributes;
+ }
+
+ /**
+ * Getter for collective conflict behavior defined for this
+ * collective attributes subentry.
+ * @return conflict behavior for this collective attributes
+ * subentry.
+ */
+ public CollectiveConflictBehavior getConflictBehavior()
+ {
+ return this.conflictBehavior;
+ }
+}
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index b80e4c1..235bb32 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -224,6 +224,69 @@
/**
+ * The name of the standard "subtreeSpecification" attribute type,
+ * formatted in camel case.
+ */
+ public static final String ATTR_SUBTREE_SPEC = "subtreeSpecification";
+
+
+
+ /**
+ * The name of the standard "subtreeSpecification" attribute type,
+ * formatted in all lowercase characters.
+ */
+ public static final String ATTR_SUBTREE_SPEC_LC = "subtreespecification";
+
+
+
+ /**
+ * The name of the standard "collectiveExclusions" attribute type,
+ * formatted in camel case.
+ */
+ public static final String ATTR_COLLECTIVE_EXCLUSIONS =
+ "collectiveExclusions";
+
+
+
+ /**
+ * The name of the standard "collectiveExclusions" attribute type,
+ * formatted in all lowercase characters.
+ */
+ public static final String ATTR_COLLECTIVE_EXCLUSIONS_LC =
+ "collectiveexclusions";
+
+
+
+ /**
+ * The value of the standard "excludeAllCollectiveAttributes" attribute
+ * value of the standard "collectiveExclusions" attribute type,
+ * formatted in camel case.
+ */
+ public static final String VALUE_COLLECTIVE_EXCLUSIONS_EXCLUDE_ALL =
+ "excludeAllCollectiveAttributes";
+
+
+
+ /**
+ * The value of the standard "excludeAllCollectiveAttributes" attribute
+ * value of the standard "collectiveExclusions" attribute type,
+ * formatted in all lowercase characters.
+ */
+ public static final String VALUE_COLLECTIVE_EXCLUSIONS_EXCLUDE_ALL_LC =
+ "excludeallcollectiveattributes";
+
+
+
+ /**
+ * The OID of the standard "excludeAllCollectiveAttributes" attribute
+ * value of the standard "collectiveExclusions" attribute type.
+ */
+ public static final String OID_COLLECTIVE_EXCLUSIONS_EXCLUDE_ALL =
+ "2.5.18.0";
+
+
+
+ /**
* The name of the monitor attribute that is used to hold a backend ID.
*/
public static final String ATTR_MONITOR_BACKEND_ID = "ds-backend-id";
@@ -837,6 +900,35 @@
/**
+ * The name of the RFC 3672 "subentry" objectclass (which is a special
+ * type of objectclass that makes a kind of "operational" entry),
+ * formatted in all lowercase.
+ */
+ public static final String OC_SUBENTRY = "subentry";
+
+
+
+ /**
+ * The name of the RFC 3671 "collectiveAttributeSubentry" objectclass
+ * (which is a special type of objectclass that makes a kind of shared
+ * attributes subentry), formatted in camel case.
+ */
+ public static final String OC_COLLECTIVE_ATTR_SUBENTRY =
+ "collectiveAttributeSubentry";
+
+
+
+ /**
+ * The name of the RFC 3671 "collectiveAttributeSubentry" objectclass
+ * (which is a special type of objectclass that makes a kind of shared
+ * attributes subentry), formatted in all lowercase.
+ */
+ public static final String OC_COLLECTIVE_ATTR_SUBENTRY_LC =
+ "collectiveattributesubentry";
+
+
+
+ /**
* The name of the custom objectclass that will be included in backend monitor
* entries.
*/
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/AttributeTypeSyntaxTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/AttributeTypeSyntaxTest.java
index 9f9e23a..425b3c8 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/AttributeTypeSyntaxTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/AttributeTypeSyntaxTest.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2006-2009 Sun Microsystems, Inc.
*/
package org.opends.server.schema;
@@ -81,13 +81,13 @@
" SUBSTR caseIgnoreSubstringsMatch" +
" SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" +
" COLLECTIVE USAGE userApplications )",
- false}, // Collective can't inherit from non-collective
+ true}, // Collective can inherit from non-collective
{"(1.2.8.5 NAME 'testtype' DESC 'full type' OBSOLETE " +
" EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch" +
" SUBSTR caseIgnoreSubstringsMatch" +
" SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" +
" COLLECTIVE USAGE directoryOperation )",
- false}, // Collective can't be operational
+ true}, // Collective can be operational
{"(1.2.8.5 NAME 'testtype' DESC 'full type' OBSOLETE SUP cn " +
" EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch" +
" SUBSTR caseIgnoreSubstringsMatch" +
--
Gitblit v1.10.0