From 09dc131d36b33e58f728316c3d445dfc3b865be4 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Tue, 03 Apr 2007 18:52:11 +0000
Subject: [PATCH] Add initial support for a virtual attribute subsystem, and implement a few different kinds of virtual attributes. This commit addresses the following issues:
---
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java | 10
opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java | 2
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java | 1116 ++++++++
opendj-sdk/opends/src/server/org/opends/server/types/LDIFExportConfig.java | 38
opendj-sdk/opends/src/server/org/opends/server/config/ConfigEntry.java | 2
opendj-sdk/opends/src/server/org/opends/server/api/Group.java | 3
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JebFormat.java | 8
opendj-sdk/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java | 437 +++
opendj-sdk/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java | 399 ++
opendj-sdk/opends/src/server/org/opends/server/backends/BackupBackend.java | 15
opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java | 6
opendj-sdk/opends/resource/config/config.ldif | 28
opendj-sdk/opends/src/server/org/opends/server/types/DN.java | 58
opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java | 22
opendj-sdk/opends/src/server/org/opends/server/types/Entry.java | 444 ++
opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttribute.java | 258 +
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java | 2
opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java | 15
opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttributeRule.java | 408 ++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java | 2
opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java | 63
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java | 192 +
opendj-sdk/opends/src/server/org/opends/server/backends/RootDSEBackend.java | 35
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/interop/LazyDNTestCase.java | 39
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeRuleTestCase.java | 337 ++
opendj-sdk/opends/src/server/org/opends/server/core/VirtualAttributeConfigManager.java | 578 ++++
opendj-sdk/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java | 210 +
opendj-sdk/opends/resource/schema/00-core.ldif | 4
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java | 13
opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java | 6
opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java | 5
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java | 2
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/VirtualAttributeConfiguration.xml | 183 +
opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java | 637 ++++
opendj-sdk/opends/src/server/org/opends/server/backends/MonitorBackend.java | 17
opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java | 15
opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java | 31
opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java | 14
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java | 1277 +++++++++
opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java | 9
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java | 125
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java | 2
opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java | 2
opendj-sdk/opends/src/server/org/opends/server/util/LDIFWriter.java | 2
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java | 12
opendj-sdk/opends/src/server/org/opends/server/interop/LazyDN.java | 23
/dev/null | 141 -
opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java | 10
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml | 8
opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java | 50
opendj-sdk/opends/src/server/org/opends/server/types/Attribute.java | 54
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java | 848 ++++++
opendj-sdk/opends/resource/schema/02-config.ldif | 28
opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java | 32
54 files changed, 7,907 insertions(+), 370 deletions(-)
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index 6c192c1..5cf8c5b 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -1669,6 +1669,34 @@
objectClass: ds-cfg-branch
cn: Virtual Attributes
+dn: cn=entryDN,cn=Virtual Attributes,cn=config
+objectClass: top
+objectClass: ds-cfg-virtual-attribute
+cn: entryDN
+ds-cfg-virtual-attribute-class: org.opends.server.extensions.EntryDNVirtualAttributeProvider
+ds-cfg-virtual-attribute-enabled: true
+ds-cfg-virtual-attribute-type: entryDN
+ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
+
+dn: cn=isMemberOf,cn=Virtual Attributes,cn=config
+objectClass: top
+objectClass: ds-cfg-virtual-attribute
+cn: isMemberOf
+ds-cfg-virtual-attribute-class: org.opends.server.extensions.IsMemberOfVirtualAttributeProvider
+ds-cfg-virtual-attribute-enabled: true
+ds-cfg-virtual-attribute-type: isMemberOf
+ds-cfg-virtual-attribute-filter: (objectClass=person)
+ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
+
+dn: cn=subschemaSubentry,cn=Virtual Attributes,cn=config
+objectClass: top
+objectClass: ds-cfg-virtual-attribute
+cn: subschemaSubentry
+ds-cfg-virtual-attribute-class: org.opends.server.extensions.SubschemaSubentryVirtualAttributeProvider
+ds-cfg-virtual-attribute-enabled: true
+ds-cfg-virtual-attribute-type: subschemaSubentry
+ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
+
dn: cn=Work Queue,cn=config
objectClass: top
objectClass: ds-cfg-work-queue
diff --git a/opendj-sdk/opends/resource/schema/00-core.ldif b/opendj-sdk/opends/resource/schema/00-core.ldif
index 295178c..cbe3be8 100644
--- a/opendj-sdk/opends/resource/schema/00-core.ldif
+++ b/opendj-sdk/opends/resource/schema/00-core.ldif
@@ -420,6 +420,10 @@
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 1274' )
attributeTypes: ( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'RFC 1274' )
+attributeTypes: ( 2.16.840.1.113730.3.1.602 NAME 'entryDN'
+ DESC 'DN of the entry' EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation X-ORIGIN 'draft-zeilenga-ldap-entrydn' )
objectClasses: ( 2.5.6.0 NAME 'top' ABSTRACT MUST objectClass
X-ORIGIN 'RFC 4512' )
objectClasses: ( 2.5.6.1 NAME 'alias' SUP top STRUCTURAL MUST aliasedObjectName
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index b5791ca..a54ec37 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1103,6 +1103,28 @@
NAME 'ds-cfg-case-sensitive-validation'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.325
+ NAME 'ds-cfg-virtual-attribute-class' 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.326
+ NAME 'ds-cfg-virtual-attribute-enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.327
+ NAME 'ds-cfg-virtual-attribute-type' 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.328
+ NAME 'ds-cfg-virtual-attribute-base-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.329
+ NAME 'ds-cfg-virtual-attribute-group-dn' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.330
+ NAME 'ds-cfg-virtual-attribute-filter' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.331
+ NAME 'ds-cfg-virtual-attribute-conflict-behavior'
+ 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 STRUCTURAL
MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1532,4 +1554,10 @@
SUP ds-cfg-password-validator STRUCTURAL
MUST ( ds-cfg-maximum-consecutive-length $ ds-cfg-case-sensitive-validation )
X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.91 NAME 'ds-cfg-virtual-attribute'
+ SUP top STRUCTURAL MUST ( cn $ ds-cfg-virtual-attribute-class $
+ ds-cfg-virtual-attribute-enabled $ ds-cfg-virtual-attribute-type $
+ ds-cfg-virtual-attribute-conflict-behavior )
+ MAY ( ds-cfg-virtual-attribute-base-dn $ ds-cfg-virtual-attribute-group-dn $
+ ds-cfg-virtual-attribute-filter ) X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
index 72785fe..d3a9ab8 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -61,5 +61,13 @@
</ldap:rdn-sequence>
</adm:profile>
</adm:relation>
+ <adm:relation name="virtual-attribute">
+ <adm:one-to-many />
+ <adm:profile name="ldap">
+ <ldap:rdn-sequence>
+ cn=Virtual Attributes,cn=config
+ </ldap:rdn-sequence>
+ </adm:profile>
+ </adm:relation>
<adm:product-name>OpenDS Directory Server</adm:product-name>
</adm:root-managed-object>
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/VirtualAttributeConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/VirtualAttributeConfiguration.xml
new file mode 100644
index 0000000..c0a1eb6
--- /dev/null
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/VirtualAttributeConfiguration.xml
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adm:managed-object name="virtual-attribute"
+plural-name="virtual-attributes"
+package="org.opends.server.admin.std"
+xmlns:adm="http://www.opends.org/admin"
+xmlns:ldap="http://www.opends.org/admin-ldap">
+ <adm:synopsis>
+ <adm:user-friendly-plural-name />
+ are responsible for dynamically generating attribute values that appear in
+ entries but are not persistently stored in the backend.
+ </adm:synopsis>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:oid>1.3.6.1.4.1.26027.1.2.91</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute</ldap:name>
+ <ldap:superior>top</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+
+ <adm:property name="provider-class" mandatory="true">
+ <adm:synopsis>
+ The fully-qualified name of the Java class that provides the
+ <adm:user-friendly-name />
+ implementation.
+ </adm:synopsis>
+ <adm:syntax>
+ <adm:java-class>
+ <adm:instance-of>
+ org.opends.server.api.VirtualAttributeProvider
+ </adm:instance-of>
+ </adm:java-class>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.325</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute-class</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+ <adm:property name="enabled" mandatory="true">
+ <adm:synopsis>
+ Indicate whether the
+ <adm:user-friendly-name />
+ is enabled for use.
+ </adm:synopsis>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.326</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute-enabled</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+ <adm:property name="attribute-type" mandatory="true">
+ <adm:synopsis>
+ Specifies the attribute type for the attribute whose values should be
+ dynamically assigned by the virtual attribute.
+ </adm:synopsis>
+ <adm:syntax>
+ <adm:attribute-type />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.327</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute-type</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+ <adm:property name="base-dn" mandatory="false" multi-valued="true">
+ <adm:synopsis>
+ Specifies the base DNs for the branches containing entries that may be
+ eligible to use this virtual attribute.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>
+ The location of the entry in the server will not be taken into account
+ when determining whether an entry is eligible to use this virtual
+ attribute.
+ </adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:dn />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.328</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute-base-dn</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+ <adm:property name="group-dn" mandatory="false" multi-valued="true">
+ <adm:synopsis>
+ Specifies the DNs for the groups whose members may be eligible to use this
+ virtual attribute.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>
+ Group membership will not be taken into accountwhen determining
+ whether an entry is eligible to use this virtual attribute.
+ </adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:dn />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.329</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute-group-dn</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+ <adm:property name="filter" mandatory="false" multi-valued="true">
+ <adm:synopsis>
+ Specifies the search filters for entries that may be eligible to use this
+ virtual attribute.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>(objectClass=*)</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.330</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute-filter</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
+ <adm:property name="conflict-behavior" mandatory="false">
+ <adm:synopsis>
+ Specifies the behavior that the server should exhibit for entries that
+ contain one or more real values for the associated attribute.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>real-overrides-virtual</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:enumeration>
+ <adm:value name="real-overrides-virtual">
+ <adm:synopsis>
+ Any real values contained in the entry should be preserved and
+ virtual values should not be generated.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="virtual-overrides-real">
+ <adm:synopsis>
+ Any real values contained in the entry should be suppressed and
+ virtual values should be generated.
+ </adm:synopsis>
+ </adm:value>
+ <adm:value name="merge-real-and-virtual">
+ <adm:synopsis>
+ Any real values contained in the entry should be preserved and
+ merged with the set of generated virtual values.
+ </adm:synopsis>
+ </adm:value>
+ </adm:enumeration>
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.331</ldap:oid>
+ <ldap:name>ds-cfg-virtual-attribute-conflict-behavior</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+</adm:managed-object>
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java b/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
index 4eaf510..75740c4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -73,9 +73,6 @@
*/
public abstract class ClientConnection
{
-
-
-
// The set of authentication information for this client connection.
private AuthenticationInfo authenticationInfo;
@@ -1197,7 +1194,7 @@
* searches performed using this client
* connection.
*/
- public final void setSizeLimit(int sizeLimit)
+ public void setSizeLimit(int sizeLimit)
{
this.sizeLimit = sizeLimit;
}
@@ -1226,7 +1223,7 @@
* entries that should be check for
* matches during a search.
*/
- public final void setLookthroughLimit(int lookthroughLimit)
+ public void setLookthroughLimit(int lookthroughLimit)
{
this.lookthroughLimit = lookthroughLimit;
}
@@ -1255,7 +1252,7 @@
* searches performed using this client
* connection.
*/
- public final void setTimeLimit(int timeLimit)
+ public void setTimeLimit(int timeLimit)
{
this.timeLimit = timeLimit;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/Group.java b/opendj-sdk/opends/src/server/org/opends/server/api/Group.java
index 4c89272..25e9e38 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/Group.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/Group.java
@@ -63,9 +63,6 @@
*/
public abstract class Group
{
-
-
-
/**
* Initializes a "shell" instance of this group implementation that
* may be used to identify and instantiate instances of this type of
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttribute.java b/opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttribute.java
deleted file mode 100644
index 5104aec..0000000
--- a/opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttribute.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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
- *
- *
- * Portions Copyright 2006-2007 Sun Microsystems, Inc.
- */
-package org.opends.server.api;
-
-
-
-import org.opends.server.config.ConfigEntry;
-import org.opends.server.config.ConfigException;
-import org.opends.server.core.SearchOperation;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.ByteString;
-import org.opends.server.types.Entry;
-import org.opends.server.types.InitializationException;
-import org.opends.server.types.SearchResultEntry;
-
-
-
-/**
- * This class defines the set of methods and structures that must be
- * implemented by a Directory Server module that implements the
- * functionality required for one or more virtual attributes.
- */
-public abstract class VirtualAttribute
-{
- /**
- * Initializes this virtual attribute based on the information in
- * the provided configuration entry.
- *
- * @param configEntry The configuration entry that contains the
- * information to use to initialize this
- * virtual attribute.
- *
- * @throws ConfigException If an unrecoverable problem arises in
- * the process of performing the
- * initialization.
- *
- * @throws InitializationException If a problem occurs during
- * initialization that is not
- * related to the server
- * configuration.
- */
- public abstract void initializeVirtualAttribute(
- ConfigEntry configEntry)
- throws ConfigException, InitializationException;
-
-
-
- /**
- * Retrieves the name of this virtual attribute.
- *
- * @return The name of this virtual attribute.
- */
- public abstract String getName();
-
-
-
- /**
- * Indicates whether the provided entry matches the given
- * attribute-value assertion.
- *
- * @param entry The entry for which to make the
- * determination.
- * @param attributeType The attribute type for which to make the
- * determination.
- * @param assertionValue The value for which to make the
- * determination.
- *
- * @return <CODE>true</CODE> if the given entry matches the
- * provided assertion details, or <CODE>false</CODE> if
- * not.
- */
- public abstract boolean entryMatches(Entry entry,
- AttributeType attributeType,
- ByteString assertionValue);
-
-
-
- /**
- * Updates the provided entry if appropriate to include one or more
- * values for this virtual attribute. The entry may be left
- * unaltered if it is not one that should contain this virtual
- * attribute. This method may alter real attributes if appropriate
- * as well.
- *
- * @param entry The entry to be updated if necessary.
- * @param searchOperation The search operation with which the
- * entry is associated.
- */
- public abstract void updateEntry(SearchResultEntry entry,
- SearchOperation searchOperation);
-
-
-
- /**
- * Indicates whether this attribute may be included in search
- * filters as part of the criteria for locating entries.
- *
- * @return <CODE>true</CODE> if this attribute may be included in
- * search filters, or <CODE>false</CODE> if not.
- */
- public abstract boolean isSearchable();
-
-
-
- /**
- * Processes the provided search operation in which the search
- * criteria includes an operation targeted at this virtual
- * attribute. This method will only be called if
- * <CODE>isSearchable</CODE> returns true and it is not possible to
- * construct a manageable candidate list by processing other
- * elements of the search criteria.
- *
- * @param searchOperation The search operation to be processed.
- */
- public abstract void processSearch(SearchOperation searchOperation);
-}
-
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java b/opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java
new file mode 100644
index 0000000..bb25ed4
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/VirtualAttributeProvider.java
@@ -0,0 +1,637 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.api;
+
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+
+
+
+/**
+ * This class defines the set of methods and structures that must be
+ * implemented by a Directory Server module that implements the
+ * functionality required for one or more virtual attributes.
+ *
+ * @param <T> The type of configuration handled by this virtual
+ * attribute provider.
+ */
+public abstract class VirtualAttributeProvider
+ <T extends VirtualAttributeCfg>
+{
+ /**
+ * Initializes this virtual attribute based on the information in
+ * the provided configuration entry.
+ *
+ * @param configuration The configuration to use to initialize
+ * this virtual attribute provider.
+ *
+ * @throws ConfigException If an unrecoverable problem arises in
+ * the process of performing the
+ * initialization.
+ *
+ * @throws InitializationException If a problem occurs during
+ * initialization that is not
+ * related to the server
+ * configuration.
+ */
+ public abstract void initializeVirtualAttributeProvider(
+ T configuration)
+ throws ConfigException, InitializationException;
+
+
+
+ /**
+ * Performs any finalization that may be necessary whenever this
+ * virtual attribute provider is taken out of service.
+ */
+ public void finalizeVirtualAttributeProvider()
+ {
+ // No implementation required by default.
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider may generate
+ * multiple values.
+ *
+ * @return {@code true} if this virtual attribute provider may
+ * generate multiple values, or {@code false} if not.
+ */
+ public abstract boolean isMultiValued();
+
+
+
+ /**
+ * Generates a set of values for the provided entry.
+ *
+ * @param entry The entry for which the values are to be
+ * generated.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ *
+ * @return The set of values generated for the provided entry. It
+ * may be empty, but it must not be {@code null}.
+ */
+ public abstract LinkedHashSet<AttributeValue>
+ getValues(Entry entry,
+ VirtualAttributeRule rule);
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider will generate
+ * at least one value for the provided entry.
+ *
+ * @param entry The entry for which to make the determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ *
+ * @return {@code true} if this virtual attribute provider will
+ * generate at least one value for the provided entry, or
+ * {@code false} if not.
+ */
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule)
+ {
+ return (! getValues(entry, rule).isEmpty());
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider will generate
+ * the provided value.
+ *
+ * @param entry The entry for which to make the determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ * @param value The value for which to make the determination.
+ *
+ * @return {@code true} if this virtual attribute provider will
+ * generate the specified vaule for the provided entry, or
+ * {@code false} if not.
+ */
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ return getValues(entry, rule).contains(value);
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider will generate
+ * all of the values in the provided collection.
+ *
+ * @param entry The entry for which to make the determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ * @param values The set of values for which to make the
+ * determination.
+ *
+ * @return {@code true} if this attribute provider will generate
+ * all of the values in the provided collection, or
+ * {@code false} if it will not generate at least one of
+ * them.
+ */
+ public boolean hasAllValues(Entry entry, VirtualAttributeRule rule,
+ Collection<AttributeValue> values)
+ {
+ for (AttributeValue value : values)
+ {
+ if (! getValues(entry, rule).contains(value))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+ /**
+ * Indicates whether this virutal attribute provider will generate
+ * any of the values in the provided collection.
+ *
+ * @param entry The entry for which to make the determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ * @param values The set of values for which to make the
+ * determination.
+ *
+ * @return {@code true} if this attribute provider will generate
+ * at least one of the values in the provided collection,
+ * or {@code false} if it will not generate any of them.
+ */
+ public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
+ Collection<AttributeValue> values)
+ {
+ for (AttributeValue value : values)
+ {
+ if (getValues(entry, rule).contains(value))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider will generate
+ * any value which matches the provided substring.
+ *
+ * @param entry The entry for which to make the
+ * determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ * @param subInitial The subInitial component to use in the
+ * determination.
+ * @param subAny The subAny components to use in the
+ * determination.
+ * @param subFinal The subFinal component to use in the
+ * determination.
+ *
+ * @return <CODE>UNDEFINED</CODE> if this attribute does not have a
+ * substring matching rule, <CODE>TRUE</CODE> if at least
+ * one value matches the provided substring, or
+ * <CODE>FALSE</CODE> otherwise.
+ */
+ public ConditionResult matchesSubstring(Entry entry,
+ VirtualAttributeRule rule,
+ ByteString subInitial,
+ List<ByteString> subAny,
+ ByteString subFinal)
+ {
+ SubstringMatchingRule matchingRule =
+ rule.getAttributeType().getSubstringMatchingRule();
+ if (matchingRule == null)
+ {
+ return ConditionResult.UNDEFINED;
+ }
+
+
+ ByteString normalizedSubInitial;
+ if (subInitial == null)
+ {
+ normalizedSubInitial = null;
+ }
+ else
+ {
+ try
+ {
+ normalizedSubInitial =
+ matchingRule.normalizeSubstring(subInitial);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // The substring couldn't be normalized. We have to return
+ // "undefined".
+ return ConditionResult.UNDEFINED;
+ }
+ }
+
+
+ ArrayList<ByteString> normalizedSubAny;
+ if (subAny == null)
+ {
+ normalizedSubAny = null;
+ }
+ else
+ {
+ normalizedSubAny =
+ new ArrayList<ByteString>(subAny.size());
+ for (ByteString subAnyElement : subAny)
+ {
+ try
+ {
+ normalizedSubAny.add(matchingRule.normalizeSubstring(
+ subAnyElement));
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // The substring couldn't be normalized. We have to return
+ // "undefined".
+ return ConditionResult.UNDEFINED;
+ }
+ }
+ }
+
+
+ ByteString normalizedSubFinal;
+ if (subFinal == null)
+ {
+ normalizedSubFinal = null;
+ }
+ else
+ {
+ try
+ {
+ normalizedSubFinal =
+ matchingRule.normalizeSubstring(subFinal);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // The substring couldn't be normalized. We have to return
+ // "undefined".
+ return ConditionResult.UNDEFINED;
+ }
+ }
+
+
+ ConditionResult result = ConditionResult.FALSE;
+ for (AttributeValue value : getValues(entry, rule))
+ {
+ try
+ {
+ if (matchingRule.valueMatchesSubstring(
+ value.getNormalizedValue(),
+ normalizedSubInitial,
+ normalizedSubAny,
+ normalizedSubFinal))
+ {
+ return ConditionResult.TRUE;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // The value couldn't be normalized. If we can't find a
+ // definite match, then we should return "undefined".
+ result = ConditionResult.UNDEFINED;
+ }
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider will generate
+ * any value for the provided entry that is greater than or equal to
+ * the given value.
+ *
+ * @param entry The entry for which to make the determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ * @param value The value for which to make the determination.
+ *
+ * @return {@code UNDEFINED} if the associated attribute type does
+ * not have an ordering matching rule, {@code TRUE} if at
+ * least one of the generated values will be greater than
+ * or equal to the specified value, or {@code FALSE} if
+ * none of the generated values will be greater than or
+ * equal to the specified value.
+ */
+ public ConditionResult greaterThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ OrderingMatchingRule matchingRule =
+ rule.getAttributeType().getOrderingMatchingRule();
+ if (matchingRule == null)
+ {
+ return ConditionResult.UNDEFINED;
+ }
+
+ ByteString normalizedValue;
+ try
+ {
+ normalizedValue = value.getNormalizedValue();
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // We couldn't normalize the provided value. We should return
+ // "undefined".
+ return ConditionResult.UNDEFINED;
+ }
+
+ ConditionResult result = ConditionResult.FALSE;
+ for (AttributeValue v : getValues(entry, rule))
+ {
+ try
+ {
+ ByteString nv = v.getNormalizedValue();
+ int comparisonResult =
+ matchingRule.compareValues(nv, normalizedValue);
+ if (comparisonResult >= 0)
+ {
+ return ConditionResult.TRUE;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // We couldn't normalize one of the attribute values. If we
+ // can't find a definite match, then we should return
+ // "undefined".
+ result = ConditionResult.UNDEFINED;
+ }
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider will generate
+ * any value for the provided entry that is less than or equal to
+ * the given value.
+ *
+ * @param entry The entry for which to make the determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ * @param value The value for which to make the determination.
+ *
+ * @return {@code UNDEFINED} if the associated attribute type does
+ * not have an ordering matching rule, {@code TRUE} if at
+ * least one of the generated values will be less than or
+ * equal to the specified value, or {@code FALSE} if none
+ * of the generated values will be greater than or equal to
+ * the specified value.
+ */
+ public ConditionResult lessThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ OrderingMatchingRule matchingRule =
+ rule.getAttributeType().getOrderingMatchingRule();
+ if (matchingRule == null)
+ {
+ return ConditionResult.UNDEFINED;
+ }
+
+ ByteString normalizedValue;
+ try
+ {
+ normalizedValue = value.getNormalizedValue();
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // We couldn't normalize the provided value. We should return
+ // "undefined".
+ return ConditionResult.UNDEFINED;
+ }
+
+ ConditionResult result = ConditionResult.FALSE;
+ for (AttributeValue v : getValues(entry, rule))
+ {
+ try
+ {
+ ByteString nv = v.getNormalizedValue();
+ int comparisonResult =
+ matchingRule.compareValues(nv, normalizedValue);
+ if (comparisonResult <= 0)
+ {
+ return ConditionResult.TRUE;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // We couldn't normalize one of the attribute values. If we
+ // can't find a definite match, then we should return
+ // "undefined".
+ result = ConditionResult.UNDEFINED;
+ }
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute provider will generate
+ * any value for the provided entry that is approximately equal to
+ * the given value.
+ *
+ * @param entry The entry for which to make the determination.
+ * @param rule The virtual attribute rule which defines the
+ * constraints for the virtual attribute.
+ * @param value The value for which to make the determination.
+ *
+ * @return {@code UNDEFINED} if the associated attribute type does
+ * not have an aproximate matching rule, {@code TRUE} if at
+ * least one of the generated values will be approximately
+ * equal to the specified value, or {@code FALSE} if none
+ * of the generated values will be approximately equal to
+ * the specified value.
+ */
+ public ConditionResult approximatelyEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ ApproximateMatchingRule matchingRule =
+ rule.getAttributeType().getApproximateMatchingRule();
+ if (matchingRule == null)
+ {
+ return ConditionResult.UNDEFINED;
+ }
+
+ ByteString normalizedValue;
+ try
+ {
+ normalizedValue = matchingRule.normalizeValue(value.getValue());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // We couldn't normalize the provided value. We should return
+ // "undefined".
+ return ConditionResult.UNDEFINED;
+ }
+
+ ConditionResult result = ConditionResult.FALSE;
+ for (AttributeValue v : getValues(entry, rule))
+ {
+ try
+ {
+ ByteString nv = matchingRule.normalizeValue(v.getValue());
+ if (matchingRule.approximatelyMatch(nv, normalizedValue))
+ {
+ return ConditionResult.TRUE;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ // We couldn't normalize one of the attribute values. If we
+ // can't find a definite match, then we should return
+ // "undefined".
+ result = ConditionResult.UNDEFINED;
+ }
+ }
+
+ return result;
+ }
+
+
+
+ /**
+ * Indicates whether this attribute may be included in search
+ * filters as part of the criteria for locating entries.
+ *
+ * @param rule The virtual attribute rule which defines
+ * the constraints for the virtual
+ * attribute.
+ * @param searchOperation The search operation for which to make
+ * the determination.
+ *
+ * @return <CODE>true</CODE> if this attribute may be included in
+ * search filters, or <CODE>false</CODE> if not.
+ */
+ public abstract boolean isSearchable(VirtualAttributeRule rule,
+ SearchOperation
+ searchOperation);
+
+
+
+ /**
+ * Processes the provided search operation in which the search
+ * criteria includes an operation targeted at this virtual
+ * attribute. This method should only be called if
+ * <CODE>isSearchable</CODE> returns true and it is not possible to
+ * construct a manageable candidate list by processing other
+ * elements of the search criteria.
+ *
+ * @param rule The virtual attribute rule which defines
+ * the constraints for the virtual
+ * attribute.
+ * @param searchOperation The search operation to be processed.
+ */
+ public abstract void processSearch(VirtualAttributeRule rule,
+ SearchOperation searchOperation);
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/BackupBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/BackupBackend.java
index 41dc517..b0d888a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/BackupBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -97,9 +97,6 @@
extends Backend
implements ConfigurableComponent
{
-
-
-
// The DN of the configuration entry for this backend.
private DN configEntryDN;
@@ -132,8 +129,6 @@
{
super();
-
-
// Perform all initialization in initializeBackend.
}
@@ -413,7 +408,7 @@
// If the requested entry was the backend base entry, then retrieve it.
if (entryDN.equals(backupBaseDN))
{
- return backupBaseEntry;
+ return backupBaseEntry.duplicate(true);
}
@@ -543,7 +538,9 @@
userAttrs.put(t, attrList);
- return new Entry(entryDN, ocMap, userAttrs, opAttrs);
+ Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
+ e.processVirtualAttributes();
+ return e;
}
@@ -764,7 +761,9 @@
}
- return new Entry(entryDN, ocMap, userAttrs, opAttrs);
+ Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
+ e.processVirtualAttributes();
+ return e;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java
index 07c7345..3dda52d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -102,9 +102,6 @@
public class MemoryBackend
extends Backend
{
-
-
-
// The base DNs for this backend.
private DN[] baseDNs;
@@ -134,8 +131,6 @@
{
super();
-
-
// Perform all initialization in initializeBackend.
}
@@ -297,8 +292,10 @@
public synchronized void addEntry(Entry entry, AddOperation addOperation)
throws DirectoryException
{
+ Entry e = entry.duplicate(true);
+
// See if the target entry already exists. If so, then fail.
- DN entryDN = entry.getDN();
+ DN entryDN = e.getDN();
if (entryMap.containsKey(entryDN))
{
int msgID = MSGID_MEMORYBACKEND_ENTRY_ALREADY_EXISTS;
@@ -311,7 +308,7 @@
// If the entry is one of the base DNs, then add it.
if (baseDNSet.contains(entryDN))
{
- entryMap.put(entryDN, entry);
+ entryMap.put(entryDN, e);
return;
}
@@ -332,7 +329,7 @@
throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
}
- entryMap.put(entryDN, entry);
+ entryMap.put(entryDN, e);
HashSet<DN> children = childDNs.get(parentDN);
if (children == null)
{
@@ -441,8 +438,10 @@
ModifyOperation modifyOperation)
throws DirectoryException
{
+ Entry e = entry.duplicate(true);
+
// Make sure the entry exists. If not, then throw an exception.
- DN entryDN = entry.getDN();
+ DN entryDN = e.getDN();
if (! entryMap.containsKey(entryDN))
{
int msgID = MSGID_MEMORYBACKEND_ENTRY_DOESNT_EXIST;
@@ -452,7 +451,7 @@
// Replace the old entry with the new one.
- entryMap.put(entryDN, entry);
+ entryMap.put(entryDN, e);
}
@@ -464,6 +463,8 @@
ModifyDNOperation modifyDNOperation)
throws DirectoryException
{
+ Entry e = entry.duplicate(true);
+
// Make sure that the target entry exists.
if (! entryMap.containsKey(currentDN))
{
@@ -492,10 +493,10 @@
// Make sure that no entry exists with the new DN.
- if (entryMap.containsKey(entry.getDN()))
+ if (entryMap.containsKey(e.getDN()))
{
int msgID = MSGID_MEMORYBACKEND_ENTRY_ALREADY_EXISTS;
- String message = getMessage(msgID, String.valueOf(entry.getDN()));
+ String message = getMessage(msgID, String.valueOf(e.getDN()));
throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, msgID);
}
@@ -504,7 +505,7 @@
boolean matchFound = false;
for (DN dn : baseDNs)
{
- if (dn.isAncestorOf(entry.getDN()))
+ if (dn.isAncestorOf(e.getDN()))
{
matchFound = true;
break;
@@ -521,7 +522,7 @@
// Make sure that the parent of the new entry exists.
- DN parentDN = entry.getDN().getParentDNInSuffix();
+ DN parentDN = e.getDN().getParentDNInSuffix();
if ((parentDN == null) || (! entryMap.containsKey(parentDN)))
{
int msgID = MSGID_MEMORYBACKEND_RENAME_PARENT_DOESNT_EXIST;
@@ -534,7 +535,7 @@
// Delete the current entry and add the new one.
deleteEntry(currentDN, null);
- addEntry(entry, null);
+ addEntry(e, null);
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/MonitorBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/MonitorBackend.java
index 169b6c8..ef39963 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/MonitorBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -92,9 +92,6 @@
extends Backend
implements ConfigurableComponent
{
-
-
-
// The set of user-defined attributes that will be included in the base
// monitor entry.
private ArrayList<Attribute> userDefinedAttributes;
@@ -128,8 +125,6 @@
{
super();
-
-
// Perform all initialization in initializeBackend.
}
@@ -640,8 +635,10 @@
// Construct and return the entry.
- return new Entry(baseMonitorDN, monitorClasses, monitorUserAttrs,
- monitorOperationalAttrs);
+ Entry e = new Entry(baseMonitorDN, monitorClasses, monitorUserAttrs,
+ monitorOperationalAttrs);
+ e.processVirtualAttributes();
+ return e;
}
@@ -706,8 +703,10 @@
}
}
- return new Entry(entryDN, monitorClasses, attrMap,
- new HashMap<AttributeType,List<Attribute>>(0));
+ Entry e = new Entry(entryDN, monitorClasses, attrMap,
+ new HashMap<AttributeType,List<Attribute>>(0));
+ e.processVirtualAttributes();
+ return e;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/RootDSEBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/RootDSEBackend.java
index 9c7daf1..142f6e9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/RootDSEBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -105,9 +105,6 @@
extends Backend
implements ConfigurableComponent
{
-
-
-
// The set of standard "static" attributes that we will always include in the
// root DSE entry and won't change while the server is running.
private ArrayList<Attribute> staticDSEAttributes;
@@ -153,8 +150,6 @@
{
super();
-
-
// Perform all initialization in initializeBackend.
}
@@ -644,30 +639,6 @@
}
-
- // Add the "subschemaSubentry" attribute.
- DN schemaDN = DirectoryServer.getSchemaDN();
- if (schemaDN != null)
- {
- Attribute subschemaSubentryAttr =
- createAttribute(ATTR_SUBSCHEMA_SUBENTRY, ATTR_SUBSCHEMA_SUBENTRY_LC,
- String.valueOf(schemaDN));
- ArrayList<Attribute> subschemaSubentryAttrs = new ArrayList<Attribute>(1);
- subschemaSubentryAttrs.add(subschemaSubentryAttr);
- if (showAllAttributes ||
- (! subschemaSubentryAttr.getAttributeType().isOperational()))
- {
- dseUserAttrs.put(subschemaSubentryAttr.getAttributeType(),
- subschemaSubentryAttrs);
- }
- else
- {
- dseOperationalAttrs.put(subschemaSubentryAttr.getAttributeType(),
- subschemaSubentryAttrs);
- }
- }
-
-
// Add all the standard "static" attributes.
for (Attribute a : staticDSEAttributes)
{
@@ -741,8 +712,10 @@
// Construct and return the entry.
- return new Entry(rootDSEDN, dseObjectClasses, dseUserAttrs,
- dseOperationalAttrs);
+ Entry e = new Entry(rootDSEDN, dseObjectClasses, dseUserAttrs,
+ dseOperationalAttrs);
+ e.processVirtualAttributes();
+ return e;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
index 7a8034f..042bafe 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -952,7 +952,10 @@
// Construct and return the entry.
- return new Entry(entryDN, schemaObjectClasses, userAttrs, operationalAttrs);
+ Entry e = new Entry(entryDN, schemaObjectClasses, userAttrs,
+ operationalAttrs);
+ e.processVirtualAttributes();
+ return e;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
index 5f2559d..be17587 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/ID2Entry.java
@@ -256,12 +256,13 @@
//Try to decode the entry based on the version number. On later versions,
//a case could be written to upgrade entries if it is not the current
//version
+ Entry entry = null;
switch(entryVersion)
{
case JebFormat.FORMAT_VERSION :
try
{
- return JebFormat.entryFromDatabase(entryBytes);
+ entry = JebFormat.entryFromDatabase(entryBytes);
}
catch (Exception e)
{
@@ -269,6 +270,8 @@
String message = getMessage(msgID, id.toString());
throw new JebException(msgID, message);
}
+ break;
+
//case 0x00 :
// Call upgrade method? Call 0x00 decode method?
default :
@@ -276,6 +279,13 @@
String message = getMessage(msgID, id.toString(), entryVersion);
throw new JebException(msgID, message);
}
+
+ if (entry != null)
+ {
+ entry.processVirtualAttributes();
+ }
+
+ return entry;
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JebFormat.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JebFormat.java
index 7883c28..c5e141f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JebFormat.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/JebFormat.java
@@ -356,6 +356,10 @@
{
for (Attribute a : list)
{
+ if (a.isVirtual())
+ {
+ continue;
+ }
userAttrElements.add(new LDAPAttribute(a).encode());
}
}
@@ -367,6 +371,10 @@
{
for (Attribute a : list)
{
+ if (a.isVirtual())
+ {
+ continue;
+ }
opAttrElements.add(new LDAPAttribute(a).encode());
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
index e6e42ca..aa3ba12 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -84,9 +84,6 @@
extends Backend
implements ConfigurableComponent
{
-
-
-
/**
* The set of time units that will be used for expressing the task retention
* time.
@@ -157,8 +154,6 @@
{
super();
-
-
// Perform all initialization in initializeBackend.
}
@@ -586,8 +581,10 @@
public void addEntry(Entry entry, AddOperation addOperation)
throws DirectoryException
{
+ Entry e = entry.duplicate(false);
+
// Get the DN for the entry and then get its parent.
- DN entryDN = entry.getDN();
+ DN entryDN = e.getDN();
DN parentDN = entryDN.getParentDNInSuffix();
if (parentDN == null)
@@ -603,7 +600,7 @@
// treat the provided entry like a scheduled task.
if (parentDN.equals(scheduledTaskParentDN))
{
- Task task = taskScheduler.entryToScheduledTask(entry, addOperation);
+ Task task = taskScheduler.entryToScheduledTask(e, addOperation);
taskScheduler.scheduleTask(task, true);
return;
}
@@ -612,7 +609,7 @@
// treat the provided entry like a recurring task.
if (parentDN.equals(recurringTaskParentDN))
{
- RecurringTask recurringTask = taskScheduler.entryToRecurringTask(entry);
+ RecurringTask recurringTask = taskScheduler.entryToRecurringTask(e);
taskScheduler.addRecurringTask(recurringTask, true);
return;
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
index 3294a46..4e0ecc4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -1360,7 +1360,7 @@
*/
public Entry getTaskRootEntry()
{
- return taskRootEntry;
+ return taskRootEntry.duplicate(true);
}
@@ -1374,7 +1374,7 @@
*/
public Entry getScheduledTaskParentEntry()
{
- return scheduledTaskParentEntry;
+ return scheduledTaskParentEntry.duplicate(true);
}
@@ -1388,7 +1388,7 @@
*/
public Entry getRecurringTaskParentEntry()
{
- return recurringTaskParentEntry;
+ return recurringTaskParentEntry.duplicate(true);
}
@@ -1540,7 +1540,7 @@
if (scheduledTaskEntryDN.equals(taskEntry.getDN()))
{
- return taskEntry;
+ return taskEntry.duplicate(true);
}
}
@@ -1586,7 +1586,7 @@
try
{
- Entry e = t.getTaskEntry();
+ Entry e = t.getTaskEntry().duplicate(true);
if (filter.matchesEntry(e))
{
if (! searchOperation.returnEntry(e, null))
@@ -1691,7 +1691,7 @@
if (recurringTaskEntryDN.equals(recurringTaskEntry.getDN()))
{
- return recurringTaskEntry;
+ return recurringTaskEntry.duplicate(true);
}
}
@@ -1737,7 +1737,7 @@
try
{
- Entry e = rt.getRecurringTaskEntry();
+ Entry e = rt.getRecurringTaskEntry().duplicate(true);
if (filter.matchesEntry(e))
{
if (! searchOperation.returnEntry(e, null))
diff --git a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigEntry.java b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigEntry.java
index fe3396d..85ff2fb 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/config/ConfigEntry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/config/ConfigEntry.java
@@ -459,7 +459,7 @@
*/
public ConfigEntry duplicate()
{
- return new ConfigEntry(entry.duplicate(), parent);
+ return new ConfigEntry(entry.duplicate(false), parent);
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
index 25e398f..ef72f64 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
@@ -2014,7 +2014,7 @@
if (postReadRequest != null)
{
- Entry addedEntry = entry.duplicate();
+ Entry addedEntry = entry.duplicate(true);
if (! postReadRequest.allowsAttribute(
DirectoryServer.getObjectClassAttributeType()))
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java
index bfb74fc..32ac395 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java
@@ -1085,7 +1085,7 @@
if (preReadRequest != null)
{
- Entry entryCopy = entry.duplicate();
+ Entry entryCopy = entry.duplicate(true);
if (! preReadRequest.allowsAttribute(
DirectoryServer.getObjectClassAttributeType()))
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
index 43fb847..205e04c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -296,10 +296,6 @@
// between the mechanism name and the handler).
private ConcurrentHashMap<String,SASLMechanismHandler> saslMechanismHandlers;
- // The set of virtual attributes defined in the server (mapped between the
- // lowercase names and the virtual attributes).
- private ConcurrentHashMap<String,VirtualAttribute> virtualAttributes;
-
// The connection handler configuration manager for the Directory Server.
private ConnectionHandlerConfigManager connectionHandlerConfigManager;
@@ -341,6 +337,9 @@
private CopyOnWriteArrayList<SynchronizationProvider>
synchronizationProviders;
+ // The set of virtual attributes defined in the server.
+ private CopyOnWriteArrayList<VirtualAttributeRule> virtualAttributes;
+
// The set of backend initialization listeners registered with the Directory
// Server.
private CopyOnWriteArraySet<BackendInitializationListener>
@@ -511,6 +510,10 @@
// The trust manager provider configuration manager for the Directory Server.
private TrustManagerProviderConfigManager trustManagerProviderConfigManager;
+ // The virtual attribute provider configuration manager for the Directory
+ // Server.
+ private VirtualAttributeConfigManager virtualAttributeConfigManager;
+
// The work queue that will be used to service client requests.
private WorkQueue workQueue;
@@ -631,7 +634,7 @@
directoryServer.supportedControls = new TreeSet<String>();
directoryServer.supportedFeatures = new TreeSet<String>();
directoryServer.virtualAttributes =
- new ConcurrentHashMap<String,VirtualAttribute>();
+ new CopyOnWriteArrayList<VirtualAttributeRule>();
directoryServer.connectionHandlers =
new CopyOnWriteArrayList<ConnectionHandler>();
directoryServer.identityMappers =
@@ -2218,6 +2221,8 @@
supportedControls.add(OID_MATCHED_VALUES);
supportedControls.add(OID_LDAP_SUBENTRIES);
supportedControls.add(OID_PASSWORD_POLICY_CONTROL);
+ supportedControls.add(OID_REAL_ATTRS_ONLY);
+ supportedControls.add(OID_VIRTUAL_ATTRS_ONLY);
}
@@ -2314,7 +2319,8 @@
private void initializeVirtualAttributes()
throws ConfigException, InitializationException
{
- // NYI
+ virtualAttributeConfigManager = new VirtualAttributeConfigManager();
+ virtualAttributeConfigManager.initializeVirtualAttributes();
}
@@ -3926,6 +3932,113 @@
/**
+ * Retrieves the set of virtual attribute rules registered with the Directory
+ * Server.
+ *
+ * @return The set of virtual attribute rules registered with the Directory
+ * Server.
+ */
+ public static List<VirtualAttributeRule> getVirtualAttributes()
+ {
+ return directoryServer.virtualAttributes;
+ }
+
+
+
+ /**
+ * Retrieves the set of virtual attribute rules registered with the Directory
+ * Server that are applicable to the provided entry.
+ *
+ * @param entry The entry for which to retrieve the applicable virtual
+ * attribute rules.
+ *
+ * @return The set of virtual attribute rules registered with the Directory
+ * Server that apply to the given entry. It may be an empty list if
+ * there are no applicable virtual attribute rules.
+ */
+ public static List<VirtualAttributeRule> getVirtualAttributes(Entry entry)
+ {
+ LinkedList<VirtualAttributeRule> ruleList =
+ new LinkedList<VirtualAttributeRule>();
+
+ for (VirtualAttributeRule rule : directoryServer.virtualAttributes)
+ {
+ if (rule.appliesToEntry(entry))
+ {
+ ruleList.add(rule);
+ }
+ }
+
+ return ruleList;
+ }
+
+
+
+ /**
+ * Registers the provided virtual attribute rule with the Directory Server.
+ *
+ * @param rule The virtual attribute rule to be registered.
+ */
+ public static void registerVirtualAttribute(VirtualAttributeRule rule)
+ {
+ synchronized (directoryServer.virtualAttributes)
+ {
+ directoryServer.virtualAttributes.add(rule);
+ }
+ }
+
+
+
+ /**
+ * Deregisters the provided virtual attribute rule with the Directory Server.
+ *
+ * @param rule The virutal attribute rule to be deregistered.
+ */
+ public static void deregisterVirtualAttribute(VirtualAttributeRule rule)
+ {
+ synchronized (directoryServer.virtualAttributes)
+ {
+ directoryServer.virtualAttributes.remove(rule);
+ }
+ }
+
+
+
+ /**
+ * Replaces the specified virtual attribute rule in the set of virtual
+ * attributes registered with the Directory Server. If the old rule cannot
+ * be found in the list, then the set of registered virtual attributes is not
+ * updated.
+ *
+ * @param oldRule The existing rule that should be replaced with the new
+ * rule.
+ * @param newRule The new rule that should be used in place of the existing
+ * rule.
+ *
+ * @return {@code true} if the old rule was found and replaced with the new
+ * version, or {@code false} if it was not.
+ */
+ public static boolean replaceVirtualAttribute(VirtualAttributeRule oldRule,
+ VirtualAttributeRule newRule)
+ {
+ synchronized (directoryServer.virtualAttributes)
+ {
+ int pos = directoryServer.virtualAttributes.indexOf(oldRule);
+ if (pos >= 0)
+ {
+ directoryServer.virtualAttributes.set(pos, newRule);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+
+
+ /**
* Retrieves a reference to the JMX MBean server that is associated with the
* Directory Server.
*
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java
index 6766475..3b58b7d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java
@@ -1383,7 +1383,7 @@
// Duplicate the entry and set its new DN. Also, create an empty list
// to hold the attribute-level modifications.
- newEntry = currentEntry.duplicate();
+ newEntry = currentEntry.duplicate(false);
newEntry.setDN(newDN);
modifications = new ArrayList<Modification>();
@@ -1818,7 +1818,7 @@
if (preReadRequest != null)
{
- Entry entry = currentEntry.duplicate();
+ Entry entry = currentEntry.duplicate(true);
if (! preReadRequest.allowsAttribute(
DirectoryServer.getObjectClassAttributeType()))
@@ -1869,7 +1869,7 @@
if (postReadRequest != null)
{
- Entry entry = newEntry.duplicate();
+ Entry entry = newEntry.duplicate(true);
if (! postReadRequest.allowsAttribute(
DirectoryServer.getObjectClassAttributeType()))
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
index d424bdd..1c9ea1d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -1189,7 +1189,7 @@
// Create a duplicate of the entry and apply the changes to it.
- modifiedEntry = currentEntry.duplicate();
+ modifiedEntry = currentEntry.duplicate(false);
if (! noOp)
{
@@ -2605,7 +2605,7 @@
if (preReadRequest != null)
{
- Entry entry = currentEntry.duplicate();
+ Entry entry = currentEntry.duplicate(true);
if (! preReadRequest.allowsAttribute(
DirectoryServer.getObjectClassAttributeType()))
@@ -2656,7 +2656,7 @@
if (postReadRequest != null)
{
- Entry entry = modifiedEntry.duplicate();
+ Entry entry = modifiedEntry.duplicate(true);
if (! postReadRequest.allowsAttribute(
DirectoryServer.getObjectClassAttributeType()))
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
index 45f54d3..7d4f2f7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
@@ -102,9 +102,6 @@
PostOperationSearchOperation, PostResponseSearchOperation,
SearchEntrySearchOperation, SearchReferenceSearchOperation
{
-
-
-
// Indicates whether a search result done response has been sent to the
// client.
private AtomicBoolean responseSent;
@@ -116,12 +113,18 @@
// entries.
private boolean includeUsableControl;
+ // Indicates whether to only real attributes should be returned.
+ private boolean realAttributesOnly;
+
// Indicates whether LDAP subentries should be returned.
private boolean returnLDAPSubentries;
// Indicates whether to include attribute types only or both types and values.
private boolean typesOnly;
+ // Indicates whether to only virtual attributes should be returned.
+ private boolean virtualAttributesOnly;
+
// The raw, unprocessed base DN as included in the request from the client.
private ByteString rawBaseDN;
@@ -274,6 +277,8 @@
persistentSearch = null;
returnLDAPSubentries = false;
matchedValuesControl = null;
+ realAttributesOnly = false;
+ virtualAttributesOnly = false;
}
@@ -865,7 +870,8 @@
Entry entryToReturn;
if ((attributes == null) || attributes.isEmpty())
{
- entryToReturn = entry.duplicateWithoutOperationalAttributes(typesOnly);
+ entryToReturn = entry.duplicateWithoutOperationalAttributes(typesOnly,
+ true);
}
else
{
@@ -1028,6 +1034,16 @@
}
+ if (realAttributesOnly)
+ {
+ entryToReturn.stripVirtualAttributes();
+ }
+ else if (virtualAttributesOnly)
+ {
+ entryToReturn.stripRealAttributes();
+ }
+
+
// If there is a matched values control, then further pare down the entry
// based on the filters that it contains.
if ((matchedValuesControl != null) && (! typesOnly))
@@ -1877,6 +1893,14 @@
{
includeUsableControl = true;
}
+ else if (oid.equals(OID_REAL_ATTRS_ONLY))
+ {
+ realAttributesOnly = true;
+ }
+ else if (oid.equals(OID_VIRTUAL_ATTRS_ONLY))
+ {
+ virtualAttributesOnly = true;
+ }
// NYI -- Add support for additional controls.
else if (c.isCritical())
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/VirtualAttributeConfigManager.java b/opendj-sdk/opends/src/server/org/opends/server/core/VirtualAttributeConfigManager.java
new file mode 100644
index 0000000..fc4ce4f
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/VirtualAttributeConfigManager.java
@@ -0,0 +1,578 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.opends.server.admin.ClassPropertyDefinition;
+import org.opends.server.admin.server.ConfigurationAddListener;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.server.ConfigurationDeleteListener;
+import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.admin.server.ServerManagementContext;
+import org.opends.server.api.VirtualAttributeProvider;
+import org.opends.server.config.ConfigException;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.ErrorLogCategory;
+import org.opends.server.types.ErrorLogSeverity;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.opends.server.loggers.Error.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.messages.ConfigMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class defines a utility that will be used to manage the set of
+ * virtual attribute providers defined in the Directory Server. It will
+ * initialize the providers when the server starts, and then will manage any
+ * additions, removals, or modifications to any virtual attribute providers
+ * while the server is running.
+ */
+public class VirtualAttributeConfigManager
+ implements ConfigurationChangeListener<VirtualAttributeCfg>,
+ ConfigurationAddListener<VirtualAttributeCfg>,
+ ConfigurationDeleteListener<VirtualAttributeCfg>
+{
+ // A mapping between the DNs of the config entries and the associated
+ // virtual attribute rules.
+ private ConcurrentHashMap<DN,VirtualAttributeRule> rules;
+
+
+
+ /**
+ * Creates a new instance of this virtual attribute config manager.
+ */
+ public VirtualAttributeConfigManager()
+ {
+ rules = new ConcurrentHashMap<DN,VirtualAttributeRule>();
+ }
+
+
+
+ /**
+ * Initializes all virtual attribute providers currently defined in the
+ * Directory Server configuration. This should only be called at Directory
+ * Server startup.
+ *
+ * @throws ConfigException If a configuration problem causes the virtual
+ * attribute provider initialization process to
+ * fail.
+ *
+ * @throws InitializationException If a problem occurs while initializing
+ * the virtual attribute providers that is
+ * not related to the server configuration.
+ */
+ public void initializeVirtualAttributes()
+ throws ConfigException, InitializationException
+ {
+ // Get the root configuration object.
+ ServerManagementContext managementContext =
+ ServerManagementContext.getInstance();
+ RootCfg rootConfiguration =
+ managementContext.getRootConfiguration();
+
+
+ // Register as an add and delete listener with the root configuration so we
+ // can be notified if any virtual attribute provider entries are added or
+ // removed.
+ rootConfiguration.addVirtualAttributeAddListener(this);
+ rootConfiguration.addVirtualAttributeDeleteListener(this);
+
+
+ //Initialize the existing virtual attribute providers.
+ for (String providerName : rootConfiguration.listVirtualAttributes())
+ {
+ VirtualAttributeCfg cfg =
+ rootConfiguration.getVirtualAttribute(providerName);
+ cfg.addChangeListener(this);
+
+ if (cfg.isEnabled())
+ {
+ String className = cfg.getProviderClass();
+ try
+ {
+ VirtualAttributeProvider<? extends VirtualAttributeCfg> provider =
+ loadProvider(className, cfg);
+
+ LinkedHashSet<SearchFilter> filters =
+ new LinkedHashSet<SearchFilter>();
+ for (String filterString : cfg.getFilter())
+ {
+ try
+ {
+ filters.add(SearchFilter.createFilterFromString(filterString));
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ int msgID = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
+ String message = getMessage(msgID, filterString,
+ String.valueOf(cfg.dn()),
+ de.getErrorMessage());
+ throw new ConfigException(msgID, message, de);
+ }
+ }
+
+ if (cfg.getAttributeType().isSingleValue())
+ {
+ if (provider.isMultiValued())
+ {
+ int msgID = MSGID_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER;
+ String message = getMessage(msgID, String.valueOf(cfg.dn()),
+ cfg.getAttributeType().getNameOrOID(),
+ className);
+ throw new ConfigException(msgID, message);
+ }
+ else if (cfg.getConflictBehavior() ==
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ MERGE_REAL_AND_VIRTUAL)
+ {
+ int msgID = MSGID_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES;
+ String message = getMessage(msgID, String.valueOf(cfg.dn()),
+ cfg.getAttributeType().getNameOrOID());
+ throw new ConfigException(msgID, message);
+ }
+ }
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(cfg.getAttributeType(), provider,
+ cfg.getBaseDN(), cfg.getGroupDN(),
+ filters, cfg.getConflictBehavior());
+ rules.put(cfg.dn(), rule);
+ DirectoryServer.registerVirtualAttribute(rule);
+ }
+ catch (InitializationException ie)
+ {
+ logError(ErrorLogCategory.CONFIGURATION,
+ ErrorLogSeverity.SEVERE_ERROR,
+ ie.getMessage(), ie.getMessageID());
+ continue;
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationAddAcceptable(
+ VirtualAttributeCfg configuration,
+ List<String> unacceptableReasons)
+ {
+ if (configuration.isEnabled())
+ {
+ // Get the name of the class and make sure we can instantiate it as a
+ // virtual attribute provider.
+ String className = configuration.getProviderClass();
+ try
+ {
+ loadProvider(className, null);
+ }
+ catch (InitializationException ie)
+ {
+ unacceptableReasons.add(ie.getMessage());
+ return false;
+ }
+ }
+
+ // If there were any search filters provided, then make sure they are all
+ // valid.
+ for (String filterString : configuration.getFilter())
+ {
+ try
+ {
+ SearchFilter.createFilterFromString(filterString);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ int msgID = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
+ String message = getMessage(msgID, filterString,
+ String.valueOf(configuration.dn()),
+ de.getErrorMessage());
+ unacceptableReasons.add(message);
+ return false;
+ }
+ }
+
+ // If we've gotten here, then it's fine.
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationAdd(
+ VirtualAttributeCfg configuration)
+ {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<String> messages = new ArrayList<String>();
+
+ configuration.addChangeListener(this);
+
+ if (! configuration.isEnabled())
+ {
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+
+ // Make sure that we can parse all of the search filters.
+ LinkedHashSet<SearchFilter> filters =
+ new LinkedHashSet<SearchFilter>();
+ for (String filterString : configuration.getFilter())
+ {
+ try
+ {
+ filters.add(SearchFilter.createFilterFromString(filterString));
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ int msgID = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
+ String message = getMessage(msgID, filterString,
+ String.valueOf(configuration.dn()),
+ de.getErrorMessage());
+ messages.add(message);
+ }
+ }
+
+ // Get the name of the class and make sure we can instantiate it as a
+ // certificate mapper.
+ VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null;
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ String className = configuration.getProviderClass();
+ try
+ {
+ provider = loadProvider(className, configuration);
+ }
+ catch (InitializationException ie)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ messages.add(ie.getMessage());
+ }
+ }
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(configuration.getAttributeType(), provider,
+ configuration.getBaseDN(),
+ configuration.getGroupDN(),
+ filters,
+ configuration.getConflictBehavior());
+
+ rules.put(configuration.dn(), rule);
+ DirectoryServer.registerVirtualAttribute(rule);
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationDeleteAcceptable(
+ VirtualAttributeCfg configuration,
+ List<String> unacceptableReasons)
+ {
+ // We will always allow getting rid of a virtual attribute rule.
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationDelete(
+ VirtualAttributeCfg configuration)
+ {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<String> messages = new ArrayList<String>();
+
+ VirtualAttributeRule rule = rules.remove(configuration.dn());
+ if (rule != null)
+ {
+ DirectoryServer.deregisterVirtualAttribute(rule);
+ rule.getProvider().finalizeVirtualAttributeProvider();
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(
+ VirtualAttributeCfg configuration,
+ List<String> unacceptableReasons)
+ {
+ if (configuration.isEnabled())
+ {
+ // Get the name of the class and make sure we can instantiate it as a
+ // virtual attribute provider.
+ String className = configuration.getProviderClass();
+ try
+ {
+ loadProvider(className, null);
+ }
+ catch (InitializationException ie)
+ {
+ unacceptableReasons.add(ie.getMessage());
+ return false;
+ }
+ }
+
+ // If there were any search filters provided, then make sure they are all
+ // valid.
+ for (String filterString : configuration.getFilter())
+ {
+ try
+ {
+ SearchFilter.createFilterFromString(filterString);
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ int msgID = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
+ String message = getMessage(msgID, filterString,
+ String.valueOf(configuration.dn()),
+ de.getErrorMessage());
+ unacceptableReasons.add(message);
+ return false;
+ }
+ }
+
+ // If we've gotten here, then it's fine.
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationChange(
+ VirtualAttributeCfg configuration)
+ {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<String> messages = new ArrayList<String>();
+
+
+ // Get the existing rule if it's already enabled.
+ VirtualAttributeRule existingRule = rules.get(configuration.dn());
+
+
+ // If the new configuration has the rule disabled, then disable it if it
+ // is enabled, or do nothing if it's already disabled.
+ if (! configuration.isEnabled())
+ {
+ if (existingRule != null)
+ {
+ DirectoryServer.deregisterVirtualAttribute(existingRule);
+ existingRule.getProvider().finalizeVirtualAttributeProvider();
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+
+
+ // Make sure that we can parse all of the search filters.
+ LinkedHashSet<SearchFilter> filters =
+ new LinkedHashSet<SearchFilter>();
+ for (String filterString : configuration.getFilter())
+ {
+ try
+ {
+ filters.add(SearchFilter.createFilterFromString(filterString));
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
+ }
+
+ int msgID = MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER;
+ String message = getMessage(msgID, filterString,
+ String.valueOf(configuration.dn()),
+ de.getErrorMessage());
+ messages.add(message);
+ }
+ }
+
+ // Get the name of the class and make sure we can instantiate it as a
+ // certificate mapper.
+ VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null;
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ String className = configuration.getProviderClass();
+ try
+ {
+ provider = loadProvider(className, configuration);
+ }
+ catch (InitializationException ie)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ messages.add(ie.getMessage());
+ }
+ }
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(configuration.getAttributeType(), provider,
+ configuration.getBaseDN(),
+ configuration.getGroupDN(),
+ filters,
+ configuration.getConflictBehavior());
+
+ rules.put(configuration.dn(), rule);
+ if (existingRule == null)
+ {
+ DirectoryServer.registerVirtualAttribute(rule);
+ }
+ else
+ {
+ DirectoryServer.replaceVirtualAttribute(existingRule, rule);
+ existingRule.getProvider().finalizeVirtualAttributeProvider();
+ }
+ }
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+
+
+
+ /**
+ * Loads the specified class, instantiates it as a certificate mapper, and
+ * optionally initializes that instance.
+ *
+ * @param className The fully-qualified name of the certificate mapper
+ * class to load, instantiate, and initialize.
+ * @param configuration The configuration to use to initialize the
+ * certificate mapper, or {@code null} if the
+ * certificate mapper should not be initialized.
+ *
+ * @return The possibly initialized certificate mapper.
+ *
+ * @throws InitializationException If a problem occurred while attempting to
+ * initialize the certificate mapper.
+ */
+ private VirtualAttributeProvider<? extends VirtualAttributeCfg>
+ loadProvider(String className, VirtualAttributeCfg configuration)
+ throws InitializationException
+ {
+ try
+ {
+ VirtualAttributeCfgDefn definition =
+ VirtualAttributeCfgDefn.getInstance();
+ ClassPropertyDefinition propertyDefinition =
+ definition.getProviderClassPropertyDefinition();
+ Class<? extends VirtualAttributeProvider> providerClass =
+ propertyDefinition.loadClass(className,
+ VirtualAttributeProvider.class);
+ VirtualAttributeProvider<? extends VirtualAttributeCfg> provider =
+ (VirtualAttributeProvider<? extends VirtualAttributeCfg>)
+ providerClass.newInstance();
+
+ if (configuration != null)
+ {
+ Method method =
+ provider.getClass().getMethod("initializeVirtualAttributeProvider",
+ configuration.definition().getServerConfigurationClass());
+ method.invoke(provider, configuration);
+ }
+
+ return provider;
+ }
+ catch (Exception e)
+ {
+ int msgID = MSGID_CONFIG_VATTR_INITIALIZATION_FAILED;
+ String message = getMessage(msgID, className,
+ String.valueOf(configuration.dn()),
+ stackTraceToSingleLineString(e));
+ throw new InitializationException(msgID, message, e);
+ }
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
index e0b0de5..4a159c6 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -983,7 +983,7 @@
return null;
}
- return configEntry.getEntry();
+ return configEntry.getEntry().duplicate(true);
}
@@ -1027,6 +1027,8 @@
public void addEntry(Entry entry, AddOperation addOperation)
throws DirectoryException
{
+ Entry e = entry.duplicate(false);
+
// If there is an add operation, then make sure that the associated user has
// both the CONFIG_READ and CONFIG_WRITE privileges.
if (addOperation != null)
@@ -1051,7 +1053,7 @@
{
// Make sure that the target DN does not already exist. If it does, then
// fail.
- DN entryDN = entry.getDN();
+ DN entryDN = e.getDN();
if (configEntries.containsKey(entryDN))
{
int msgID = MSGID_CONFIG_FILE_ADD_ALREADY_EXISTS;
@@ -1099,7 +1101,7 @@
// Encapsulate the provided entry in a config entry.
- ConfigEntry newEntry = new ConfigEntry(entry, parentEntry);
+ ConfigEntry newEntry = new ConfigEntry(e, parentEntry);
// See if the parent entry has any add listeners. If so, then iterate
@@ -1327,6 +1329,8 @@
public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
throws DirectoryException
{
+ Entry e = entry.duplicate(false);
+
// If there is a modify operation, then make sure that the associated user
// has both the CONFIG_READ and CONFIG_WRITE privileges. Also, if the
// operation targets the set of root privileges then make sure the user has
@@ -1373,7 +1377,7 @@
try
{
// Get the DN of the target entry for future reference.
- DN entryDN = entry.getDN();
+ DN entryDN = e.getDN();
// Get the target entry. If it does not exist, then fail.
@@ -1405,7 +1409,7 @@
// Create a new config entry to use for the validation testing.
- ConfigEntry newEntry = new ConfigEntry(entry, currentEntry.getParent());
+ ConfigEntry newEntry = new ConfigEntry(e, currentEntry.getParent());
// See if there are any config change listeners registered for this entry.
@@ -1465,7 +1469,7 @@
// We'll just overwrite the core entry in the current config entry so that
// we keep all the registered listeners, references to the parent and
// children, and other metadata.
- currentEntry.setEntry(entry);
+ currentEntry.setEntry(e);
writeUpdatedConfig();
@@ -1606,7 +1610,7 @@
case BASE_OBJECT:
// We are only interested in the base entry itself. See if it matches
// and if so then return the entry.
- Entry e = baseEntry.getEntry();
+ Entry e = baseEntry.getEntry().duplicate(true);
if (filter.matchesEntry(e))
{
searchOperation.returnEntry(e, null);
@@ -1619,7 +1623,7 @@
// Iterate through them and return the ones that match the filter.
for (ConfigEntry child : baseEntry.getChildren().values())
{
- e = child.getEntry();
+ e = child.getEntry().duplicate(true);
if (filter.matchesEntry(e))
{
if (! searchOperation.returnEntry(e, null))
@@ -1680,7 +1684,7 @@
SearchOperation searchOperation)
throws DirectoryException
{
- Entry e = baseEntry.getEntry();
+ Entry e = baseEntry.getEntry().duplicate(true);
if (filter.matchesEntry(e))
{
if (! searchOperation.returnEntry(e, null))
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
new file mode 100644
index 0000000..92f9af0
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProvider.java
@@ -0,0 +1,399 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.api.VirtualAttributeProvider;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringFactory;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * This class implements a virtual attribute provider that is meant to serve the
+ * entryDN operational attribute as described in draft-zeilenga-ldap-entrydn.
+ */
+public class EntryDNVirtualAttributeProvider
+ extends VirtualAttributeProvider<VirtualAttributeCfg>
+{
+ /**
+ * Creates a new instance of this entryDN virtual attribute provider.
+ */
+ public EntryDNVirtualAttributeProvider()
+ {
+ super();
+
+ // All initialization should be performed in the
+ // initializeVirtualAttributeProvider method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeVirtualAttributeProvider(
+ VirtualAttributeCfg configuration)
+ throws ConfigException, InitializationException
+ {
+ // No initialization is required.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isMultiValued()
+ {
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public LinkedHashSet<AttributeValue> getValues(Entry entry,
+ VirtualAttributeRule rule)
+ {
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
+
+ String normDNString = entry.getDN().toNormalizedString();
+ values.add(new AttributeValue(ByteStringFactory.create(normDNString),
+ ByteStringFactory.create(normDNString)));
+
+ return values;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule)
+ {
+ // This virtual attribute provider will always generate a value.
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ try
+ {
+ String normalizedDN = entry.getDN().toNormalizedString();
+ String normalizedValue = value.getNormalizedStringValue();
+ return normalizedDN.equals(normalizedValue);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ return false;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
+ Collection<AttributeValue> values)
+ {
+ String ndnString = entry.getDN().toNormalizedString();
+
+ AttributeValue v = new AttributeValue(ByteStringFactory.create(ndnString),
+ ByteStringFactory.create(ndnString));
+ return values.contains(v);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult matchesSubstring(Entry entry,
+ VirtualAttributeRule rule,
+ ByteString subInitial,
+ List<ByteString> subAny,
+ ByteString subFinal)
+ {
+ // DNs cannot be used in substring matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult greaterThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult lessThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult approximatelyEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in approximate matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}. This virtual attribute will support search operations only
+ * if one of the following is true about the search filter:
+ * <UL>
+ * <LI>It is an equality filter targeting the associated attribute
+ * type.</LI>
+ * <LI>It is an AND filter in which at least one of the components is an
+ * equality filter targeting the associated attribute type.</LI>
+ * <LI>It is an OR filter in which all of the components are equality
+ * filters targeting the associated attribute type.</LI>
+ * </UL>
+ */
+ @Override()
+ public boolean isSearchable(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ return isSearchable(rule.getAttributeType(), searchOperation.getFilter(),
+ 0);
+ }
+
+
+
+
+ /**
+ * Indicates whether the provided search filter is one that may be used with
+ * this virtual attribute provider, optionally operating in a recursive manner
+ * to make the determination.
+ *
+ * @param attributeType The attribute type used to hold the entryDN value.
+ * @param searchFilter The search filter for which to make the
+ * determination.
+ * @param depth The current recursion depth for this processing.
+ *
+ * @return {@code true} if the provided filter may be used with this virtual
+ * attribute provider, or {@code false} if not.
+ */
+ private boolean isSearchable(AttributeType attributeType, SearchFilter filter,
+ int depth)
+ {
+ switch (filter.getFilterType())
+ {
+ case AND:
+ if (depth >= MAX_NESTED_FILTER_DEPTH)
+ {
+ return false;
+ }
+
+ for (SearchFilter f : filter.getFilterComponents())
+ {
+ if (isSearchable(attributeType, f, depth+1))
+ {
+ return true;
+ }
+ }
+ return false;
+
+ case OR:
+ if (depth >= MAX_NESTED_FILTER_DEPTH)
+ {
+ return false;
+ }
+
+ for (SearchFilter f : filter.getFilterComponents())
+ {
+ if (! isSearchable(attributeType, f, depth+1))
+ {
+ return false;
+ }
+ }
+ return true;
+
+ case EQUALITY:
+ return filter.getAttributeType().equals(attributeType);
+
+ default:
+ return false;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void processSearch(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ SearchFilter filter = searchOperation.getFilter();
+ LinkedHashSet<DN> dnSet = new LinkedHashSet<DN>();
+ extractDNs(rule.getAttributeType(), filter, dnSet);
+
+ if (dnSet.isEmpty())
+ {
+ return;
+ }
+
+ DN baseDN = searchOperation.getBaseDN();
+ SearchScope scope = searchOperation.getScope();
+ for (DN dn : dnSet)
+ {
+ if (! dn.matchesBaseAndScope(baseDN, scope))
+ {
+ continue;
+ }
+
+ try
+ {
+ Entry entry = DirectoryServer.getEntry(dn);
+ if ((entry != null) && filter.matchesEntry(entry))
+ {
+ searchOperation.returnEntry(entry, null);
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Extracts the user DNs from the provided filter, operating recursively as
+ * necessary, and adds them to the provided set.
+ *
+ * @param attributeType The attribute type holding the entryDN value.
+ * @param filter The search filter to be processed.
+ * @param dnSet The set into which the identified DNs should be
+ * placed.
+ */
+ private void extractDNs(AttributeType attributeType, SearchFilter filter,
+ LinkedHashSet<DN> dnSet)
+ {
+ switch (filter.getFilterType())
+ {
+ case AND:
+ case OR:
+ for (SearchFilter f : filter.getFilterComponents())
+ {
+ extractDNs(attributeType, f, dnSet);
+ }
+ break;
+
+ case EQUALITY:
+ if (filter.getAttributeType().equals(attributeType))
+ {
+ try
+ {
+ dnSet.add(DN.decode(filter.getAssertionValue().getValue()));
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
new file mode 100644
index 0000000..b4ced1b
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProvider.java
@@ -0,0 +1,437 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.api.Group;
+import org.opends.server.api.VirtualAttributeProvider;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.MemberList;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * This class implements a virtual attribute provider that is meant to serve the
+ * isMemberOf operational attribute. This attribute will be used to provide a
+ * list of all groups in which the specified user is a member.
+ */
+public class IsMemberOfVirtualAttributeProvider
+ extends VirtualAttributeProvider<VirtualAttributeCfg>
+{
+ /**
+ * Creates a new instance of this entryDN virtual attribute provider.
+ */
+ public IsMemberOfVirtualAttributeProvider()
+ {
+ super();
+
+ // All initialization should be performed in the
+ // initializeVirtualAttributeProvider method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeVirtualAttributeProvider(
+ VirtualAttributeCfg configuration)
+ throws ConfigException, InitializationException
+ {
+ // No initialization is required.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isMultiValued()
+ {
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public LinkedHashSet<AttributeValue> getValues(Entry entry,
+ VirtualAttributeRule rule)
+ {
+ // FIXME -- This probably isn't the most efficient implementation.
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ for (Group g : DirectoryServer.getGroupManager().getGroupInstances())
+ {
+ try
+ {
+ if (g.isMember(entry))
+ {
+ values.add(new AttributeValue(rule.getAttributeType(),
+ g.getGroupDN().toString()));
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+ return values;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule)
+ {
+ // FIXME -- This probably isn't the most efficient implementation.
+ for (Group g : DirectoryServer.getGroupManager().getGroupInstances())
+ {
+ try
+ {
+ if (g.isMember(entry))
+ {
+ return true;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ try
+ {
+ DN groupDN = DN.decode(value.getValue());
+ Group g = DirectoryServer.getGroupManager().getGroupInstance(groupDN);
+ if (g == null)
+ {
+ return false;
+ }
+ else
+ {
+ return g.isMember(entry);
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ return false;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
+ Collection<AttributeValue> values)
+ {
+ for (AttributeValue value : values)
+ {
+ if (hasValue(entry, rule, value))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult matchesSubstring(Entry entry,
+ VirtualAttributeRule rule,
+ ByteString subInitial,
+ List<ByteString> subAny,
+ ByteString subFinal)
+ {
+ // DNs cannot be used in substring matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult greaterThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult lessThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult approximatelyEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in approximate matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}. This virtual attribute will support search operations only
+ * if one of the following is true about the search filter:
+ * <UL>
+ * <LI>It is an equality filter targeting the associated attribute
+ * type.</LI>
+ * <LI>It is an AND filter in which at least one of the components is an
+ * equality filter targeting the associated attribute type.</LI>
+ * </UL>
+ */
+ @Override()
+ public boolean isSearchable(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ return isSearchable(rule.getAttributeType(), searchOperation.getFilter(),
+ 0);
+ }
+
+
+
+
+ /**
+ * Indicates whether the provided search filter is one that may be used with
+ * this virtual attribute provider, optionally operating in a recursive manner
+ * to make the determination.
+ *
+ * @param attributeType The attribute type used to hold the entryDN value.
+ * @param searchFilter The search filter for which to make the
+ * determination.
+ * @param depth The current recursion depth for this processing.
+ *
+ * @return {@code true} if the provided filter may be used with this virtual
+ * attribute provider, or {@code false} if not.
+ */
+ private boolean isSearchable(AttributeType attributeType, SearchFilter filter,
+ int depth)
+ {
+ switch (filter.getFilterType())
+ {
+ case AND:
+ if (depth >= MAX_NESTED_FILTER_DEPTH)
+ {
+ return false;
+ }
+
+ for (SearchFilter f : filter.getFilterComponents())
+ {
+ if (isSearchable(attributeType, f, depth+1))
+ {
+ return true;
+ }
+ }
+ return false;
+
+ case EQUALITY:
+ return filter.getAttributeType().equals(attributeType);
+
+ default:
+ return false;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void processSearch(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ SearchFilter filter = searchOperation.getFilter();
+ Group group = extractGroup(rule.getAttributeType(), filter);
+ if (group == null)
+ {
+ return;
+ }
+
+ DN baseDN = searchOperation.getBaseDN();
+ SearchScope scope = searchOperation.getScope();
+ try
+ {
+ MemberList memberList = group.getMembers();
+ while (memberList.hasMoreMembers())
+ {
+ try
+ {
+ Entry e = memberList.nextMemberEntry();
+ if (e.matchesBaseAndScope(baseDN, scope) &&
+ filter.matchesEntry(e))
+ {
+ searchOperation.returnEntry(e, null);
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ }
+ catch (DirectoryException de)
+ {
+ searchOperation.setResponseData(de);
+ }
+ }
+
+
+
+ /**
+ * Extracts the first group DN encountered in the provided filter, operating
+ * recursively as necessary.
+ *
+ * @param attributeType The attribute type holding the entryDN value.
+ * @param filter The search filter to be processed.
+ *
+ * @return The first group encountered in the provided filter, or
+ * {@code null} if there is no match.
+ */
+ private Group extractGroup(AttributeType attributeType, SearchFilter filter)
+ {
+ switch (filter.getFilterType())
+ {
+ case AND:
+ for (SearchFilter f : filter.getFilterComponents())
+ {
+ Group g = extractGroup(attributeType, f);
+ if (g != null)
+ {
+ return g;
+ }
+ }
+ break;
+
+ case EQUALITY:
+ if (filter.getAttributeType().equals(attributeType))
+ {
+ try
+ {
+ DN dn = DN.decode(filter.getAssertionValue().getValue());
+ return DirectoryServer.getGroupManager().getGroupInstance(dn);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+ break;
+ }
+
+ return null;
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java
new file mode 100644
index 0000000..4472529
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProvider.java
@@ -0,0 +1,210 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.api.VirtualAttributeProvider;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * This class implements a virtual attribute provider that is meant to serve the
+ * subschemaSubentry operational attribute as described in RFC 4512.
+ */
+public class SubschemaSubentryVirtualAttributeProvider
+ extends VirtualAttributeProvider<VirtualAttributeCfg>
+{
+ /**
+ * Creates a new instance of this subschemaSubentry virtual attribute
+ * provider.
+ */
+ public SubschemaSubentryVirtualAttributeProvider()
+ {
+ super();
+
+ // All initialization should be performed in the
+ // initializeVirtualAttributeProvider method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeVirtualAttributeProvider(
+ VirtualAttributeCfg configuration)
+ throws ConfigException, InitializationException
+ {
+ // No initialization is required.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isMultiValued()
+ {
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public LinkedHashSet<AttributeValue> getValues(Entry entry,
+ VirtualAttributeRule rule)
+ {
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
+
+ values.add(new AttributeValue(rule.getAttributeType(),
+ DirectoryServer.getSchemaDN().toString()));
+
+ return values;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult matchesSubstring(Entry entry,
+ VirtualAttributeRule rule,
+ ByteString subInitial,
+ List<ByteString> subAny,
+ ByteString subFinal)
+ {
+ // DNs cannot be used in substring matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult greaterThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult lessThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult approximatelyEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in approximate matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}. This virtual attribute will support search operations only
+ * if one of the following is true about the search filter:
+ * <UL>
+ * <LI>It is an equality filter targeting the associated attribute
+ * type.</LI>
+ * <LI>It is an AND filter in which at least one of the components is an
+ * equality filter targeting the associated attribute type.</LI>
+ * <LI>It is an OR filter in which all of the components are equality
+ * filters targeting the associated attribute type.</LI>
+ * </UL>
+ */
+ @Override()
+ public boolean isSearchable(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ // This attribute is not searchable, since it will have the same value in
+ // tons of entries.
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void processSearch(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+
+ int msgID = MSGID_SUBSCHEMASUBENTRY_VATTR_NOT_SEARCHABLE;
+ String message = getMessage(msgID, rule.getAttributeType().getNameOrOID());
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/interop/LazyDN.java b/opendj-sdk/opends/src/server/org/opends/server/interop/LazyDN.java
index 173a9a9..774eafc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/interop/LazyDN.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/interop/LazyDN.java
@@ -29,11 +29,11 @@
import org.opends.server.types.DN;
-import org.opends.server.types.RDN;
-
-import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.RDN;
+import org.opends.server.types.SearchScope;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
@@ -57,9 +57,6 @@
public class LazyDN
extends DN
{
-
-
-
/**
* The serial version identifier required to satisfy the compiler because this
* class implements the {@code java.io.Serializable} interface. This value
@@ -229,6 +226,18 @@
* {@inheritDoc}
*/
@Override()
+ public boolean matchesBaseAndScope(DN baseDN, SearchScope scope)
+ throws RuntimeException
+ {
+ return getDecodedDN().matchesBaseAndScope(baseDN, scope);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
public boolean equals(Object o)
throws RuntimeException
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
index 7eef8e8..751e211 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -6528,6 +6528,51 @@
/**
+ * The message ID for the message that will be used if a virtual attribute
+ * definition has an invalid search filter. This takes three arguments, which
+ * are the filter string, the configuration entry DN, and a message explaining
+ * the problem that occurred.
+ */
+ public static final int MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 649;
+
+
+
+ /**
+ * The message ID for the message that will be used if an error occurs while
+ * trying to load and/or initialize a class as a virtual attribute provider.
+ * This takes three arguments, which are the class name, the configuration
+ * entry DN, and string representation of the exception that was caught.
+ */
+ public static final int MSGID_CONFIG_VATTR_INITIALIZATION_FAILED =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 650;
+
+
+
+ /**
+ * The message ID for the message that will be used if the configured
+ * attribute type is single-valued, but the virtual attribute provider may
+ * generate multiple values. This takes three arguments, which are the DN of
+ * the configuration entry, the name or OID of the attribute type, and the
+ * name of the virtual attribute provider class.
+ */
+ public static final int MSGID_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 651;
+
+
+
+ /**
+ * The message ID for the message that will be used if the configured
+ * attribute type is single-valued, but the conflict behavior is to merge the
+ * real and virtual values. This takes two arguments, which are the DN of
+ * the configuration entry and the name or OID of the attribute type.
+ */
+ public static final int MSGID_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES =
+ CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 652;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -9383,6 +9428,24 @@
registerMessage(MSGID_CONFIG_CHANGE_RESULT_MESSAGES,
"%s.%s succeeded but generated the following messages " +
"for entry %s: %s.");
+
+
+ registerMessage(MSGID_CONFIG_VATTR_INVALID_SEARCH_FILTER,
+ "Unable to parse value \"%s\" from config entry \"%s\" " +
+ "as a valid search filter: %s.");
+ registerMessage(MSGID_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER,
+ "The virtual attribute configuration in entry \"%s\" is " +
+ "not valid because attribute type %s is single-valued " +
+ "but provider %s may generate multiple values.");
+ registerMessage(MSGID_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES,
+ "The virtual attribute configuration in entry \"%s\" is " +
+ "not valid because attribute type %s is single-valued " +
+ "but the conflict behavior is configured to merge real " +
+ "and virtual values.");
+ registerMessage(MSGID_CONFIG_VATTR_INITIALIZATION_FAILED,
+ "An error occurred while trying to load an instance " +
+ "of class %s referenced in configuration entry %s as a " +
+ "virtual attribute provider: %s.");
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
index bc856f8..7866cc2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -4826,6 +4826,16 @@
/**
+ * The message ID for the message that will be used if a search operation has
+ * a filter targeting the subschemaSubentry virtual attribute, which is not
+ * searchable. This takes a single argument, which is the name of the
+ * subschemaSubentry attribute type.
+ */
+ public static final int MSGID_SUBSCHEMASUBENTRY_VATTR_NOT_SEARCHABLE =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 459;
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -6962,6 +6972,11 @@
"The provided password does not contain enough unique " +
"characters. The minimum number of unique characters " +
"that may appear in a user password is %d.");
+
+
+ registerMessage(MSGID_SUBSCHEMASUBENTRY_VATTR_NOT_SEARCHABLE,
+ "The %s attribute is not searchable and should not be " +
+ "included in otherwise unindexed search filters.");
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
index cfdf825..6c3467b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
@@ -102,8 +102,6 @@
public class InternalClientConnection
extends ClientConnection
{
-
-
// The message ID counter to use for internal connections.
private static AtomicInteger nextMessageID;
@@ -207,9 +205,9 @@
this.authenticationInfo =
new AuthenticationInfo(internalUserEntry, true);
super.setAuthenticationInfo(authenticationInfo);
- setSizeLimit(0);
- setTimeLimit(0);
- setLookthroughLimit(0);
+ super.setSizeLimit(0);
+ super.setTimeLimit(0);
+ super.setLookthroughLimit(0);
}
catch (DirectoryException de)
{
@@ -257,9 +255,9 @@
this.authenticationInfo = authInfo;
super.setAuthenticationInfo(authInfo);
- setSizeLimit(0);
- setTimeLimit(0);
- setLookthroughLimit(0);
+ super.setSizeLimit(0);
+ super.setTimeLimit(0);
+ super.setLookthroughLimit(0);
connectionID = nextConnectionID.getAndDecrement();
operationList = new LinkedList<Operation>();
@@ -457,6 +455,42 @@
/**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void setSizeLimit(int sizeLimit)
+ {
+ // No implementation required. We never want to set a nonzero
+ // size limit for internal client connections.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void setLookthroughLimit(int lookthroughLimit)
+ {
+ // No implementation required. We never want to set a nonzero
+ // lookthrough limit for internal client connections.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void setTimeLimit(int timeLimit)
+ {
+ // No implementation required. We never want to set a nonzero
+ // time limit for internal client connections.
+ }
+
+
+
+ /**
* Indicates whether this client connection is currently using a
* secure mechanism to communicate with the server. Note that this
* may change over time based on operations performed by the client
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
index 81b8c43..ca40b69 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPToolUtils.java
@@ -111,6 +111,16 @@
{
controlOID = OID_SUBTREE_DELETE_CONTROL;
}
+ else if (lowerOID.equals("realattrsonly") ||
+ lowerOID.equals("realattributesonly"))
+ {
+ controlOID = OID_REAL_ATTRS_ONLY;
+ }
+ else if (lowerOID.equals("virtualattrsonly") ||
+ lowerOID.equals("virtualattributesonly"))
+ {
+ controlOID = OID_VIRTUAL_ATTRS_ONLY;
+ }
if (idx < 0)
{
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Attribute.java b/opendj-sdk/opends/src/server/org/opends/server/types/Attribute.java
index d649300..029f43a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Attribute.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Attribute.java
@@ -40,10 +40,7 @@
import org.opends.server.core.DirectoryServer;
import org.opends.server.util.Base64;
-import static
- org.opends.server.loggers.debug.DebugLogger.debugCaught;
-import static
- org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -55,9 +52,6 @@
*/
public class Attribute
{
-
-
-
// The attribute type for this attribute.
private final AttributeType attributeType;
@@ -385,7 +379,7 @@
*/
public boolean hasValue()
{
- return (! values.isEmpty());
+ return (! getValues().isEmpty());
}
@@ -400,7 +394,7 @@
*/
public boolean hasValue(AttributeValue value)
{
- return values.contains(value);
+ return getValues().contains(value);
}
@@ -420,7 +414,7 @@
{
for (AttributeValue value : values)
{
- if (! this.values.contains(value))
+ if (! getValues().contains(value))
{
return false;
}
@@ -447,7 +441,7 @@
{
for (AttributeValue value : values)
{
- if (this.values.contains(value))
+ if (getValues().contains(value))
{
return true;
}
@@ -570,7 +564,7 @@
ConditionResult result = ConditionResult.FALSE;
- for (AttributeValue value : values)
+ for (AttributeValue value : getValues())
{
try
{
@@ -639,7 +633,7 @@
}
ConditionResult result = ConditionResult.FALSE;
- for (AttributeValue v : values)
+ for (AttributeValue v : getValues())
{
try
{
@@ -708,7 +702,7 @@
}
ConditionResult result = ConditionResult.FALSE;
- for (AttributeValue v : values)
+ for (AttributeValue v : getValues())
{
try
{
@@ -777,7 +771,7 @@
}
ConditionResult result = ConditionResult.FALSE;
- for (AttributeValue v : values)
+ for (AttributeValue v : getValues())
{
try
{
@@ -807,6 +801,20 @@
/**
+ * Indicates whether this is a virtual attribute rather than a real
+ * attribute.
+ *
+ * @return {@code true} if this is a virtual attribute, or
+ * {@code false} if it is a real attribute.
+ */
+ public boolean isVirtual()
+ {
+ return false;
+ }
+
+
+
+ /**
* Creates a duplicate of this attribute that can be modified
* without impacting this attribute.
*
@@ -845,11 +853,7 @@
else
{
LinkedHashSet<AttributeValue> valuesCopy =
- new LinkedHashSet<AttributeValue>(values.size());
- for (AttributeValue v : values)
- {
- valuesCopy.add(v);
- }
+ new LinkedHashSet<AttributeValue>(getValues());
return new Attribute(attributeType, name, optionsCopy,
valuesCopy);
@@ -886,12 +890,12 @@
return false;
}
- if (values.size() != a.values.size())
+ if (getValues().size() != a.getValues().size())
{
return false;
}
- if (! hasAllValues(a.values))
+ if (! hasAllValues(a.getValues()))
{
return false;
}
@@ -911,7 +915,7 @@
public int hashCode()
{
int hashCode = attributeType.hashCode();
- for (AttributeValue value : values)
+ for (AttributeValue value : getValues())
{
hashCode += value.hashCode();
}
@@ -949,7 +953,7 @@
buffer.append(", {");
boolean firstValue = true;
- for (AttributeValue value : values)
+ for (AttributeValue value : getValues())
{
if (! firstValue)
{
@@ -988,7 +992,7 @@
*/
public void toLDIF(StringBuilder buffer)
{
- for (AttributeValue value : values)
+ for (AttributeValue value : getValues())
{
buffer.append(name);
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/DN.java b/opendj-sdk/opends/src/server/org/opends/server/types/DN.java
index ad60aff..fc6e7fd 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/DN.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/DN.java
@@ -29,11 +29,11 @@
/********************
- * NOTE: Any changes to the set of public methods defined in this
- * class or the arguments that they contain must also be made
- * in the org.opends.server.interop.LazyDN package to ensure
- * continued interoperability with third-party applications
- * that rely on that functionality.
+ * NOTE: Any changes to the set of non-static public methods defined
+ * in this class or the arguments that they contain must also
+ * be made in the org.opends.server.interop.LazyDN package to
+ * ensure continued interoperability with third-party
+ * applications that rely on that functionality.
********************/
@@ -45,10 +45,7 @@
import org.opends.server.protocols.asn1.ASN1OctetString;
import static org.opends.server.config.ConfigConstants.*;
-import static
- org.opends.server.loggers.debug.DebugLogger.debugCaught;
-import static
- org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.SchemaMessages.*;
import static org.opends.server.util.StaticUtils.*;
@@ -63,9 +60,6 @@
public class DN
implements Comparable<DN>, Serializable
{
-
-
-
/**
* A singleton instance of the null DN (a DN with no components).
*/
@@ -429,6 +423,46 @@
/**
+ * Indicates whether this entry falls within the range of the
+ * provided search base DN and scope.
+ *
+ * @param baseDN The base DN for which to make the determination.
+ * @param scope The search scope for which to make the
+ * determination.
+ *
+ * @return <CODE>true</CODE> if this entry is within the given
+ * base and scope, or <CODE>false</CODE> if it is not.
+ */
+ public boolean matchesBaseAndScope(DN baseDN, SearchScope scope)
+ {
+ switch (scope)
+ {
+ case BASE_OBJECT:
+ // The base DN must equal this DN.
+ return equals(baseDN);
+
+ case SINGLE_LEVEL:
+ // The parent DN must equal the base DN.
+ return baseDN.equals(getParent());
+
+ case WHOLE_SUBTREE:
+ // This DN must be a descendant of the provided base DN.
+ return isDescendantOf(baseDN);
+
+ case SUBORDINATE_SUBTREE:
+ // This DN must be a descendant of the provided base DN, but
+ // not equal to it.
+ return ((! equals(baseDN)) && isDescendantOf(baseDN));
+
+ default:
+ // This is a scope that we don't recognize.
+ return false;
+ }
+ }
+
+
+
+ /**
* Decodes the provided ASN.1 octet string as a DN.
*
* @param dnString The ASN.1 octet string to decode as a DN.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
index 9dcf168..214405a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/Entry.java
@@ -51,14 +51,7 @@
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.util.LDIFException;
-import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
-import static
- org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static
- org.opends.server.loggers.debug.DebugLogger.debugVerbose;
-import static org.opends.server.loggers.debug.DebugLogger.debugInfo;
-import static
- org.opends.server.loggers.debug.DebugLogger.debugWarning;
+import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.*;
@@ -90,8 +83,9 @@
public class Entry
implements ProtocolElement
{
-
-
+ // Indicates whether virtual attribute processing has been performed
+ // for this entry.
+ private boolean virtualAttributeProcessingPerformed;
// The set of operational attributes for this entry.
private Map<AttributeType,List<Attribute>> operationalAttributes;
@@ -99,6 +93,9 @@
// The set of user attributes for this entry.
private Map<AttributeType,List<Attribute>> userAttributes;
+ // The set of suppressed real attributes for this entry.
+ private Map<AttributeType,List<Attribute>> suppressedAttributes;
+
// The set of objectclasses for this entry.
private Map<ObjectClass,String> objectClasses;
@@ -137,8 +134,13 @@
Map<AttributeType,List<Attribute>>
operationalAttributes)
{
- attachment = null;
- schema = DirectoryServer.getSchema();
+ attachment = null;
+ schema = DirectoryServer.getSchema();
+ virtualAttributeProcessingPerformed = false;
+
+
+ suppressedAttributes =
+ new LinkedHashMap<AttributeType,List<Attribute>>();
if (dn == null)
@@ -3094,10 +3096,14 @@
* Creates a duplicate of this entry that may be altered without
* impacting the information in this entry.
*
+ * @param processVirtual Indicates whether virtual attribute
+ * processing should be performed for the
+ * entry.
+ *
* @return A duplicate of this entry that may be altered without
* impacting the information in this entry.
*/
- public Entry duplicate()
+ public Entry duplicate(boolean processVirtual)
{
HashMap<ObjectClass,String> objectClassesCopy =
new HashMap<ObjectClass,String>(objectClasses);
@@ -3112,8 +3118,26 @@
operationalAttributes.size());
deepCopy(operationalAttributes, operationalAttrsCopy, false);
- return new Entry(dn, objectClassesCopy, userAttrsCopy,
- operationalAttrsCopy);
+ for (AttributeType t : suppressedAttributes.keySet())
+ {
+ List<Attribute> attrList = suppressedAttributes.get(t);
+ if (t.isOperational())
+ {
+ operationalAttributes.put(t, attrList);
+ }
+ else
+ {
+ userAttributes.put(t, attrList);
+ }
+ }
+
+ Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy,
+ operationalAttrsCopy);
+ if (processVirtual)
+ {
+ e.processVirtualAttributes();
+ }
+ return e;
}
@@ -3123,15 +3147,18 @@
* attributes that may be altered without impacting the information
* in this entry.
*
- * @param typesOnly Indicates whether to include attribute types
- * only without values.
+ * @param typesOnly Indicates whether to include attribute
+ * types only without values.
+ * @param processVirtual Indicates whether virtual attribute
+ * processing should be performed for the
+ * entry.
*
* @return A duplicate of this entry that may be altered without
* impacting the information in this entry and that does
* not contain any operational attributes.
*/
public Entry duplicateWithoutOperationalAttributes(
- boolean typesOnly)
+ boolean typesOnly, boolean processVirtual)
{
HashMap<ObjectClass,String> objectClassesCopy;
if (typesOnly)
@@ -3163,8 +3190,24 @@
HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
new HashMap<AttributeType,List<Attribute>>(0);
- return new Entry(dn, objectClassesCopy, userAttrsCopy,
- operationalAttrsCopy);
+ for (AttributeType t : suppressedAttributes.keySet())
+ {
+ List<Attribute> attrList = suppressedAttributes.get(t);
+ if (! t.isOperational())
+ {
+ userAttributes.put(t, attrList);
+ }
+ }
+
+ Entry e = new Entry(dn, objectClassesCopy, userAttrsCopy,
+ operationalAttrsCopy);
+
+ if (processVirtual)
+ {
+ e.processVirtualAttributes(false);
+ }
+
+ return e;
}
@@ -3172,14 +3215,15 @@
/**
* Performs a deep copy from the source map to the target map. In
* this case, the attributes in the list will be duplicates rather
- * than re-using the same reference.
+ * than re-using the same reference. Virtual attributes will not be
+ * included when making the copy.
*
- * @param source The source map from which to obtain the
- * information.
- * @param target The target map into which to place the copied
- * information.
- * @param omitValues <CODE>true</CODE> if the values should be
- * omitted.
+ * @param source The source map from which to obtain the
+ * information.
+ * @param target The target map into which to place the
+ * copied information.
+ * @param omitValues Indicates whether to omit attribute values
+ * when processing.
*/
private void deepCopy(Map<AttributeType,List<Attribute>> source,
Map<AttributeType,List<Attribute>> target,
@@ -3193,10 +3237,18 @@
for (Attribute a : sourceList)
{
+ if (a.isVirtual())
+ {
+ continue;
+ }
+
targetList.add(a.duplicate(omitValues));
}
- target.put(t, targetList);
+ if (! targetList.isEmpty())
+ {
+ target.put(t, targetList);
+ }
}
}
@@ -3522,28 +3574,267 @@
*/
public boolean matchesBaseAndScope(DN baseDN, SearchScope scope)
{
- switch (scope)
+ return dn.matchesBaseAndScope(baseDN, scope);
+ }
+
+
+
+ /**
+ * 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()
+ {
+ processVirtualAttributes(true);
+ }
+
+
+
+ /**
+ * 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.
+ *
+ * @param includeOperational Indicates whether to include
+ * operational attributes.
+ */
+ public void processVirtualAttributes(boolean includeOperational)
+ {
+ for (VirtualAttributeRule rule :
+ DirectoryServer.getVirtualAttributes(this))
{
- case BASE_OBJECT:
- // The entry DN must equal the base DN.
- return baseDN.equals(dn);
+ AttributeType attributeType = rule.getAttributeType();
+ if (attributeType.isOperational() && (! includeOperational))
+ {
+ continue;
+ }
- case SINGLE_LEVEL:
- // The parent DN for this entry must equal the base DN.
- return baseDN.equals(dn.getParentDNInSuffix());
+ 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(new VirtualAttribute(attributeType, this,
+ rule));
+ 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;
+ }
- case WHOLE_SUBTREE:
- // The base DN must be an ancestor of the entry DN.
- return baseDN.isAncestorOf(dn);
+ // The conflict is with a real attribute. See what the
+ // conflict behavior is and figure out how to handle it.
+ switch (rule.getConflictBehavior())
+ {
+ case REAL_OVERRIDES_VIRTUAL:
+ // We don't need to update the entry because the real
+ // attribute will take precedence.
+ break;
- case SUBORDINATE_SUBTREE:
- // The base DN must be an ancstor of the entry DN, but it
- // must not equal the entry DN.
- return ((! baseDN.equals(dn)) && baseDN.isAncestorOf(dn));
+ 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(new VirtualAttribute(attributeType, this,
+ rule));
+ operationalAttributes.put(attributeType, attrList);
+ break;
- default:
- // This is a scope that we don't recognize.
- return false;
+ case MERGE_REAL_AND_VIRTUAL:
+ // We need to add the virtual attribute to the list and
+ // keep the existing real attribute(s).
+ attrList.add(new VirtualAttribute(attributeType, this,
+ rule));
+ 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 (rule.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(new VirtualAttribute(attributeType, this,
+ rule));
+ 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(new VirtualAttribute(attributeType, this,
+ rule));
+ break;
+ }
+ }
+ }
+
+ virtualAttributeProcessingPerformed = true;
+ }
+
+
+
+ /**
+ * Indicates whether virtual attribute processing has been performed
+ * for this entry.
+ *
+ * @return {@code true} if virtual attribute processing has been
+ * performed for this entry, or {@code false} if not.
+ */
+ public boolean virtualAttributeProcessingPerformed()
+ {
+ return virtualAttributeProcessingPerformed;
+ }
+
+
+
+ /**
+ * Strips out all real attributes from this entry so that it only
+ * contains virtual attributes.
+ */
+ public void stripRealAttributes()
+ {
+ // The objectClass attribute will always be a real attribute.
+ objectClasses.clear();
+
+ Iterator<Map.Entry<AttributeType,List<Attribute>>>
+ attrListIterator = userAttributes.entrySet().iterator();
+ while (attrListIterator.hasNext())
+ {
+ Map.Entry<AttributeType,List<Attribute>> mapEntry =
+ attrListIterator.next();
+ Iterator<Attribute> attrIterator =
+ mapEntry.getValue().iterator();
+ while (attrIterator.hasNext())
+ {
+ Attribute a = attrIterator.next();
+ if (! a.isVirtual())
+ {
+ attrIterator.remove();
+ }
+ }
+
+ if (mapEntry.getValue().isEmpty())
+ {
+ attrListIterator.remove();
+ }
+ }
+
+ attrListIterator = operationalAttributes.entrySet().iterator();
+ while (attrListIterator.hasNext())
+ {
+ Map.Entry<AttributeType,List<Attribute>> mapEntry =
+ attrListIterator.next();
+ Iterator<Attribute> attrIterator =
+ mapEntry.getValue().iterator();
+ while (attrIterator.hasNext())
+ {
+ Attribute a = attrIterator.next();
+ if (! a.isVirtual())
+ {
+ attrIterator.remove();
+ }
+ }
+
+ if (mapEntry.getValue().isEmpty())
+ {
+ attrListIterator.remove();
+ }
+ }
+ }
+
+
+
+ /**
+ * Strips out all virtual attributes from this entry so that it only
+ * contains real attributes.
+ */
+ public void stripVirtualAttributes()
+ {
+ Iterator<Map.Entry<AttributeType,List<Attribute>>>
+ attrListIterator = userAttributes.entrySet().iterator();
+ while (attrListIterator.hasNext())
+ {
+ Map.Entry<AttributeType,List<Attribute>> mapEntry =
+ attrListIterator.next();
+ Iterator<Attribute> attrIterator =
+ mapEntry.getValue().iterator();
+ while (attrIterator.hasNext())
+ {
+ Attribute a = attrIterator.next();
+ if (a.isVirtual())
+ {
+ attrIterator.remove();
+ }
+ }
+
+ if (mapEntry.getValue().isEmpty())
+ {
+ attrListIterator.remove();
+ }
+ }
+
+ attrListIterator = operationalAttributes.entrySet().iterator();
+ while (attrListIterator.hasNext())
+ {
+ Map.Entry<AttributeType,List<Attribute>> mapEntry =
+ attrListIterator.next();
+ Iterator<Attribute> attrIterator =
+ mapEntry.getValue().iterator();
+ while (attrIterator.hasNext())
+ {
+ Attribute a = attrIterator.next();
+ if (a.isVirtual())
+ {
+ attrIterator.remove();
+ }
+ }
+
+ if (mapEntry.getValue().isEmpty())
+ {
+ attrListIterator.remove();
+ }
}
}
@@ -3742,6 +4033,12 @@
List<Attribute> attrList = userAttributes.get(attrType);
for (Attribute a : attrList)
{
+ if (a.isVirtual() &&
+ (! exportConfig.includeVirtualAttributes()))
+ {
+ continue;
+ }
+
if (exportConfig.typesOnly())
{
StringBuilder attrName = new StringBuilder(a.getName());
@@ -3787,7 +4084,7 @@
}
- // Finally, the set of operational attributes.
+ // Next, the set of operational attributes.
if (exportConfig.includeOperationalAttributes())
{
for (AttributeType attrType : operationalAttributes.keySet())
@@ -3798,6 +4095,12 @@
operationalAttributes.get(attrType);
for (Attribute a : attrList)
{
+ if (a.isVirtual() &&
+ (! exportConfig.includeVirtualAttributes()))
+ {
+ continue;
+ }
+
if (exportConfig.typesOnly())
{
StringBuilder attrName = new StringBuilder(a.getName());
@@ -3854,6 +4157,55 @@
}
}
+
+ // If we are not supposed to include virtual attributes, then
+ // write any attributes that may normally be suppressed by a
+ // virtual attribute.
+ if (! exportConfig.includeVirtualAttributes())
+ {
+ for (AttributeType t : suppressedAttributes.keySet())
+ {
+ if (exportConfig.includeAttribute(t))
+ {
+ for (Attribute a : suppressedAttributes.get(t))
+ {
+ if (exportConfig.typesOnly())
+ {
+ StringBuilder attrName = new StringBuilder(a.getName());
+ for (String o : a.getOptions())
+ {
+ attrName.append(";");
+ attrName.append(o);
+ }
+ attrName.append(":");
+
+ writeLDIFLine(attrName, writer, wrapLines, wrapColumn);
+ }
+ else
+ {
+ StringBuilder attrName = new StringBuilder(a.getName());
+ for (String o : a.getOptions())
+ {
+ attrName.append(";");
+ attrName.append(o);
+ }
+
+ for (AttributeValue v : a.getValues())
+ {
+ StringBuilder attrLine = new StringBuilder();
+ attrLine.append(attrName);
+ appendLDIFSeparatorAndValue(attrLine,
+ v.getValueBytes());
+ writeLDIFLine(attrLine, writer, wrapLines,
+ wrapColumn);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
// Make sure there is a blank line after the entry.
writer.newLine();
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/LDIFExportConfig.java b/opendj-sdk/opends/src/server/org/opends/server/types/LDIFExportConfig.java
index a5b6938..793d175 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/LDIFExportConfig.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/LDIFExportConfig.java
@@ -54,9 +54,6 @@
*/
public class LDIFExportConfig
{
-
-
-
// Indicates whether the data should be compressed as it is written.
private boolean compressData;
@@ -75,6 +72,9 @@
// export.
private boolean includeOperationalAttributes;
+ // Indicates whether to include virutal attributes in the export.
+ private boolean includeVirtualAttributes;
+
// Indicates whether to invoke LDIF export plugins on entries being
// exported.
private boolean invokeExportPlugins;
@@ -149,6 +149,7 @@
hashData = false;
includeObjectClasses = true;
includeOperationalAttributes = true;
+ includeVirtualAttributes = false;
invokeExportPlugins = false;
signHash = false;
typesOnly = false;
@@ -182,6 +183,7 @@
hashData = false;
includeObjectClasses = true;
includeOperationalAttributes = true;
+ includeVirtualAttributes = false;
invokeExportPlugins = false;
signHash = false;
typesOnly = false;
@@ -593,6 +595,36 @@
/**
+ * Indicates whether virtual attributes should be included in the
+ * export.
+ *
+ * @return {@code true} if virtual attributes should be included in
+ * the export, or {@code false} if not.
+ */
+ public boolean includeVirtualAttributes()
+ {
+ return includeVirtualAttributes;
+ }
+
+
+
+ /**
+ * Specifies whether virtual attributes should be included in the
+ * export.
+ *
+ * @param includeVirtualAttributes Specifies whether virtual
+ * attributes should be included
+ * in the export.
+ */
+ public void setIncludeVirtualAttributes(
+ boolean includeVirtualAttributes)
+ {
+ this.includeVirtualAttributes = includeVirtualAttributes;
+ }
+
+
+
+ /**
* Retrieves the set of attributes that should be excluded from the
* entries written to LDIF. The set that is returned may be altered
* by the caller.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttribute.java b/opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttribute.java
new file mode 100644
index 0000000..0fec185
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttribute.java
@@ -0,0 +1,258 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.api.VirtualAttributeProvider;
+
+
+
+/**
+ * This class defines a virtual attribute, which is a special kind of
+ * attribute whose values do not actually exist in persistent storage
+ * but rather are computed or otherwise obtained dynamically.
+ */
+public class VirtualAttribute
+ extends Attribute
+{
+ // The entry with which this virtual attribute is associated.
+ private final Entry entry;
+
+ // The virtual attribute provider for this virtual attribute.
+ private final VirtualAttributeProvider<
+ ? extends VirtualAttributeCfg> provider;
+
+ // The virtual attribute rule for this virtual attribute.
+ private final VirtualAttributeRule rule;
+
+
+
+ /**
+ * Creates a new virtual attribute with the provided information.
+ *
+ * @param attributeType The attribute type for this virtual
+ * attribute.
+ * @param entry The entry in which this virtual attribute
+ * exists.
+* @param rule The virutal attribute rule that governs
+ * the behavior of this virtual attribute.
+ */
+ public VirtualAttribute(AttributeType attributeType, Entry entry,
+ VirtualAttributeRule rule)
+ {
+ super(attributeType);
+
+ this.entry = entry;
+ this.rule = rule;
+
+ provider = rule.getProvider();
+ }
+
+
+
+ /**
+ * Retrieves the entry in which this virtual attribute exists.
+ *
+ * @return The entry in which this virtual attribute exists.
+ */
+ public Entry getEntry()
+ {
+ return entry;
+ }
+
+
+
+ /**
+ * Retrieves the virtual attribute rule that governs the behavior of
+ * this virtual attribute.
+ *
+ * @return The virtual attribute rule that governs the behavior of
+ * this virtual attribute.
+ */
+ public VirtualAttributeRule getVirtualAttributeRule()
+ {
+ return rule;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public LinkedHashSet<AttributeValue> getValues()
+ {
+ return provider.getValues(entry, rule);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue()
+ {
+ return provider.hasValue(entry, rule);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue(AttributeValue value)
+ {
+ return provider.hasValue(entry, rule, value);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasAllValues(Collection<AttributeValue> values)
+ {
+ return provider.hasAllValues(entry, rule, values);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasAnyValue(Collection<AttributeValue> values)
+ {
+ return provider.hasAnyValue(entry, rule, values);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult matchesSubstring(ByteString subInitial,
+ List<ByteString> subAny,
+ ByteString subFinal)
+ {
+ return provider.matchesSubstring(entry, rule, subInitial, subAny,
+ subFinal);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult greaterThanOrEqualTo(AttributeValue value)
+ {
+ return provider.greaterThanOrEqualTo(entry, rule, value);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult lessThanOrEqualTo(AttributeValue value)
+ {
+ return provider.lessThanOrEqualTo(entry, rule, value);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult approximatelyEqualTo(AttributeValue value)
+ {
+ return provider.approximatelyEqualTo(entry, rule, value);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isVirtual()
+ {
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public Attribute duplicate(boolean omitValues)
+ {
+ return new VirtualAttribute(getAttributeType(), entry, rule);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void toString(StringBuilder buffer)
+ {
+ buffer.append("VirtualAttribute(");
+ buffer.append(getAttributeType().getNameOrOID());
+ buffer.append(", {");
+
+ boolean firstValue = true;
+ for (AttributeValue value : getValues())
+ {
+ if (! firstValue)
+ {
+ buffer.append(", ");
+ }
+
+ value.toString(buffer);
+ firstValue = false;
+ }
+
+ buffer.append("})");
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttributeRule.java b/opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttributeRule.java
new file mode 100644
index 0000000..1344853
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/VirtualAttributeRule.java
@@ -0,0 +1,408 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.api.Group;
+import org.opends.server.api.VirtualAttributeProvider;
+import org.opends.server.core.DirectoryServer;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.Validator.*;
+
+
+
+/**
+ * This class defines a virtual attribute rule, which associates a
+ * virtual attribute provider with its associated configuration,
+ * including the attribute type for which the values should be
+ * generated; the base DN(s), group DN(s), and search filter(s) that
+ * should be used to identify which entries should have the virtual
+ * attribute, and how conflicts between real and virtual values should
+ * be handled.
+ */
+public class VirtualAttributeRule
+{
+ // The attribute type for which the values should be generated.
+ private final AttributeType attributeType;
+
+ // The set of base DNs for branches that are eligible to have this
+ // virtual attribute.
+ private final Set<DN> baseDNs;
+
+ // The set of DNs for groups whose members are eligible to have this
+ // virtual attribute.
+ private final Set<DN> groupDNs;
+
+ // The set of search filters for entries that are eligible to have
+ // this virtual attribute.
+ private final Set<SearchFilter> filters;
+
+ // The virtual attribute provider used to generate the values.
+ private final VirtualAttributeProvider<
+ ? extends VirtualAttributeCfg> provider;
+
+ // The behavior that should be exhibited for entries that already
+ // have real values for the target attribute.
+ private final VirtualAttributeCfgDefn.ConflictBehavior
+ conflictBehavior;
+
+
+
+ /**
+ * Creates a new virtual attribute rule with the provided
+ * information.
+ *
+ * @param attributeType The attribute type for which the values
+ * should be generated.
+ * @param provider The virtual attribute provider to use
+ * to generate the values.
+ * @param baseDNs The set of base DNs for branches that
+ * are eligible to have this virtual
+ * attribute.
+ * @param groupDNs The set of DNs for groups whose members
+ * are eligible to have this virtual
+ * attribute.
+ * @param filters The set of search filters for entries
+ * that are eligible to have this virtual
+ * attribute.
+ * @param conflictBehavior The behavior that the server should
+ * exhibit for entries that already have
+ * one or more real values for the target
+ * attribute.
+ */
+ public VirtualAttributeRule(AttributeType attributeType,
+ VirtualAttributeProvider<? extends VirtualAttributeCfg>
+ provider,
+ Set<DN> baseDNs, Set<DN> groupDNs,
+ Set<SearchFilter> filters,
+ VirtualAttributeCfgDefn.ConflictBehavior
+ conflictBehavior)
+ {
+ ensureNotNull(attributeType, provider, baseDNs, groupDNs);
+ ensureNotNull(filters, conflictBehavior);
+
+ this.attributeType = attributeType;
+ this.provider = provider;
+ this.baseDNs = baseDNs;
+ this.groupDNs = groupDNs;
+ this.filters = filters;
+ this.conflictBehavior = conflictBehavior;
+ }
+
+
+
+ /**
+ * Retrieves the attribute type for which the values should be
+ * generated.
+ *
+ * @return The attribute type for which the values should be
+ * generated.
+ */
+ public AttributeType getAttributeType()
+ {
+ return attributeType;
+ }
+
+
+
+ /**
+ *
+ * Retrieves the virtual attribute provider used to generate the
+ * values.
+ *
+ * @return The virtual attribute provider to use to generate the
+ * values.
+ */
+ public VirtualAttributeProvider<? extends VirtualAttributeCfg>
+ getProvider()
+ {
+ return provider;
+ }
+
+
+
+ /**
+ * Retrieves the set of base DNs for branches that are eligible to
+ * have this virtual attribute.
+ *
+ * @return The set of base DNs for branches that are eligible to
+ * have this virtual attribute.
+ */
+ public Set<DN> getBaseDNs()
+ {
+ return baseDNs;
+ }
+
+
+
+ /**
+ * Retrieves the set of DNs for groups whose members are eligible to
+ * have this virtual attribute.
+ *
+ * @return The set of DNs for groups whose members are eligible to
+ * have this virtual attribute.
+ */
+ public Set<DN> getGroupDNs()
+ {
+ return groupDNs;
+ }
+
+
+
+ /**
+ * Retrieves the set of search filters for entries that are eligible
+ * to have this virtual attribute.
+ *
+ * @return The set of search filters for entries that are eligible
+ * to have this virtual attribute.
+ */
+ public Set<SearchFilter> getFilters()
+ {
+ return filters;
+ }
+
+
+
+ /**
+ * Retrieves the behavior that the server should exhibit for entries
+ * that already have one or more real values for the target
+ * attribute.
+ *
+ * @return The behavior that the server should exhibit for entries
+ * that already have one or more real values for the target
+ * attribute.
+ */
+ public VirtualAttributeCfgDefn.ConflictBehavior
+ getConflictBehavior()
+ {
+ return conflictBehavior;
+ }
+
+
+
+ /**
+ * Indicates whether this virtual attribute rule applies to the
+ * provided entry, taking into account the eligibility requirements
+ * defined in the rule.
+ *
+ * @param entry The entry for which to make the determination.
+ *
+ * @return {@code true} if this virtual attribute rule may be used
+ * to generate values for the entry, or {@code false} if
+ * not.
+ */
+ public boolean appliesToEntry(Entry entry)
+ {
+ // We'll do this in order of expense so that the checks which are
+ // potentially most expensive are done last. First, check to see
+ // if real values should override virtual ones and if so whether
+ // the entry already has virtual values.
+ if ((conflictBehavior == VirtualAttributeCfgDefn.ConflictBehavior.
+ REAL_OVERRIDES_VIRTUAL) &&
+ entry.hasAttribute(attributeType))
+ {
+ return false;
+ }
+
+ // If there are any base DNs defined, then the entry must be below
+ // one of them.
+ DN entryDN = entry.getDN();
+ if (! baseDNs.isEmpty())
+ {
+ boolean found = false;
+ for (DN dn : baseDNs)
+ {
+ if (entryDN.isDescendantOf(dn))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (! found)
+ {
+ return false;
+ }
+ }
+
+ // If there are any search filters defined, then the entry must
+ // match one of them.
+ if (! filters.isEmpty())
+ {
+ boolean found = false;
+ for (SearchFilter filter : filters)
+ {
+ try
+ {
+ if (filter.matchesEntry(entry))
+ {
+ found = true;
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+ if (! found)
+ {
+ return false;
+ }
+ }
+
+ // If there are any group memberships defined, then the entry must
+ // be a member of one of them.
+ if (! groupDNs.isEmpty())
+ {
+ boolean found = false;
+ for (DN dn : groupDNs)
+ {
+ try
+ {
+ Group group =
+ DirectoryServer.getGroupManager().getGroupInstance(dn);
+ if ((group != null) && group.isMember(entry))
+ {
+ found = true;
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+ if (! found)
+ {
+ return false;
+ }
+ }
+
+ // If we've gotten here, then the rule is applicable.
+ return true;
+ }
+
+
+
+ /**
+ * Retrieves a string representation of this virtual attribute rule.
+ *
+ * @return A string representation of this virutal attribute rule.
+ */
+ public String toString()
+ {
+ StringBuilder buffer = new StringBuilder();
+ toString(buffer);
+ return buffer.toString();
+ }
+
+
+
+ /**
+ * Appends a string representation of this virtual attribute rule to
+ * the provided buffer.
+ *
+ * @param buffer The buffer to which the information should be
+ * written.
+ */
+ public void toString(StringBuilder buffer)
+ {
+ buffer.append("VirtualAttributeRule(attrType=");
+ buffer.append(attributeType.getNameOrOID());
+ buffer.append(", providerDN=\"");
+ buffer.append(provider.getClass().getName());
+
+ buffer.append("\", baseDNs={");
+ if (! baseDNs.isEmpty())
+ {
+ buffer.append("\"");
+ Iterator<DN> iterator = baseDNs.iterator();
+ buffer.append(iterator.next());
+
+ while (iterator.hasNext())
+ {
+ buffer.append("\", \"");
+ buffer.append(iterator.next());
+ }
+
+ buffer.append("\"");
+ }
+
+ buffer.append("}, groupDNs={");
+ if (! groupDNs.isEmpty())
+ {
+ buffer.append("\"");
+ Iterator<DN> iterator = groupDNs.iterator();
+ buffer.append(iterator.next());
+
+ while (iterator.hasNext())
+ {
+ buffer.append("\", \"");
+ buffer.append(iterator.next());
+ }
+
+ buffer.append("\"");
+ }
+
+ buffer.append("}, filters={");
+ if (! filters.isEmpty())
+ {
+ buffer.append("\"");
+ Iterator<SearchFilter> iterator = filters.iterator();
+ buffer.append(iterator.next());
+
+ while (iterator.hasNext())
+ {
+ buffer.append("\", \"");
+ buffer.append(iterator.next());
+ }
+
+ buffer.append("\"");
+ }
+
+ buffer.append("}, conflictBehavior=");
+ buffer.append(conflictBehavior);
+ buffer.append(")");
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/LDIFWriter.java b/opendj-sdk/opends/src/server/org/opends/server/util/LDIFWriter.java
index 05cc73c..f504e5a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/LDIFWriter.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/LDIFWriter.java
@@ -58,8 +58,6 @@
*/
public final class LDIFWriter
{
-
-
// FIXME -- Add support for generating a hash when writing the data.
// FIXME -- Add support for signing the hash that is generated.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
index e5c3468..990b2b6 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -1771,6 +1771,13 @@
/**
+ * The OID for the real attributes only control.
+ */
+ public static final String OID_REAL_ATTRS_ONLY = "2.16.840.1.113730.3.4.17";
+
+
+
+ /**
* The OID for the subtree delete control.
*/
public static final String OID_SUBTREE_DELETE_CONTROL =
@@ -1803,6 +1810,14 @@
+ /**
+ * The OID for the virtual attributes only control.
+ */
+ public static final String OID_VIRTUAL_ATTRS_ONLY =
+ "2.16.840.1.113730.3.4.19";
+
+
+
/**
* The block length in bytes used when generating an HMAC-MD5 digest.
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
index ce63a78..6b9a6f0 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SearchOperationTestCase.java
@@ -136,7 +136,7 @@
}
// The add operation changes the attributes, so let's duplicate the entry.
- Entry duplicateEntry = testEntry.duplicate();
+ Entry duplicateEntry = testEntry.duplicate(false);
AddOperation addOperation =
connection.processAdd(duplicateEntry.getDN(),
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
new file mode 100644
index 0000000..dd6ff2f
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntryDNVirtualAttributeProviderTestCase.java
@@ -0,0 +1,1116 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringFactory;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * A set of test cases for the entryDN virtual attribute provider.
+ */
+public class EntryDNVirtualAttributeProviderTestCase
+ extends ExtensionsTestCase
+{
+ // The attribute type for the entryDN attribute.
+ private AttributeType entryDNType;
+
+
+
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ entryDNType = DirectoryServer.getAttributeType("entrydn", false);
+ assertNotNull(entryDNType);
+ }
+
+
+
+ /**
+ * Retrieves a set of entry DNs for use in testing the entryDN virtual
+ * attribute.
+ *
+ * @return A set of entry DNs for use in testing the entryDN virtual
+ * attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "testEntryDNs")
+ public Object[][] getTestEntryDNs()
+ throws Exception
+ {
+ return new Object[][]
+ {
+ new Object[] { DN.decode("") },
+ new Object[] { DN.decode("o=test") },
+ new Object[] { DN.decode("dc=example,dc=com") },
+ new Object[] { DN.decode("cn=config") },
+ new Object[] { DN.decode("cn=schema") },
+ new Object[] { DN.decode("cn=tasks") },
+ new Object[] { DN.decode("cn=monitor") },
+ new Object[] { DN.decode("cn=backups") }
+ };
+ }
+
+
+
+ /**
+ * Tests the {@code getEntry} method for the specified entry to ensure that
+ * the entry returned includes the entryDN operational attribute with the
+ * correct value.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testGetEntry(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ Entry e = DirectoryServer.getEntry(entryDN);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(entryDNType));
+
+ List<Attribute> attrList = e.getAttribute(entryDNType);
+ assertNotNull(attrList);
+ assertFalse(attrList.isEmpty());
+ for (Attribute a : attrList)
+ {
+ assertTrue(a.hasValue());
+ assertEquals(a.getValues().size(), 1);
+ assertTrue(a.hasValue(new AttributeValue(entryDNType,
+ entryDN.toNormalizedString())));
+ }
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is not included when the list of attributes requested
+ * is empty (defaulting to all user attributes).
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchEmptyAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT, filter);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is not included when the list of requested attributes
+ * is "1.1", meaning no attributes.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchNoAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("1.1");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is not included when all user attributes are
+ * requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchAllUserAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("*");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is included when all operational attributes are
+ * requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchAllOperationalAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("+");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is included when the entryDN attribute is
+ * specifically requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchEntryDNAttr(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("entrydn");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is not included when it is not in the list of
+ * attributes that is explicitly requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchExcludeEntryDNAttr(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("objectClass");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is included when the entryDN attribute is
+ * specifically requested and the entryDN attribute is used in the search
+ * filter with a matching value.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchEntryDNAttrInMatchingFilter(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(entryDN=" + entryDN.toString() +
+ ")");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("entrydn");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * no entries are returned when the entryDN attribute is used in the search
+ * filter with a non-matching value.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchEntryDNAttrInNonMatchingFilter(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(entryDN=cn=Not A Match)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("entrydn");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 0);
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is not included when the entryDN attribute is
+ * specifically requested and the real attributes only control is included in
+ * the request.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchEntryDNAttrRealAttrsOnly(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("entrydn");
+
+ LinkedList<Control> requestControls = new LinkedList<Control>();
+ requestControls.add(new Control(OID_REAL_ATTRS_ONLY, true));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), requestControls,
+ entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, attrList, null);
+ searchOperation.run();
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the entryDN attribute is included when the entryDN attribute is
+ * specifically requested and the virtual attributes only control is included
+ * in the request.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchEntryDNAttrVirtualAttrsOnly(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("entrydn");
+
+ LinkedList<Control> requestControls = new LinkedList<Control>();
+ requestControls.add(new Control(OID_VIRTUAL_ATTRS_ONLY, true));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), requestControls,
+ entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, attrList, null);
+ searchOperation.run();
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(entryDNType));
+ }
+
+
+
+ /**
+ * Tests the {@code isMultiValued} method.
+ */
+ @Test()
+ public void testIsMultiValued()
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+ assertFalse(provider.isMultiValued());
+ }
+
+
+
+ /**
+ * Tests the {@code getValues} method for an entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGetValues()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+ assertNotNull(values);
+ assertEquals(values.size(), 1);
+ assertTrue(values.contains(new AttributeValue(entryDNType, "o=test")));
+ }
+
+
+
+ /**
+ * Tests the {@code hasValue} method variant that doesn't take a specific
+ * value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValue()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertTrue(provider.hasValue(entry, rule));
+ }
+
+
+
+ /**
+ * Tests the {@code hasValue} method variant that takes a specific value when
+ * the provided value is a match.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasMatchingValue()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertTrue(provider.hasValue(entry, rule,
+ new AttributeValue(entryDNType, "o=test")));
+ }
+
+
+
+ /**
+ * Tests the {@code hasValue} method variant that takes a specific value when
+ * the provided value is not a match.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasNonMatchingValue()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertFalse(provider.hasValue(entry, rule,
+ new AttributeValue(entryDNType, "o=not test")));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with an empty set of values.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueEmptySet()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertFalse(provider.hasAnyValue(entry, rule,
+ Collections.<AttributeValue>emptySet()));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing only
+ * the correct value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueOnlyCorrect()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
+ values.add(new AttributeValue(entryDNType, "o=test"));
+
+ assertTrue(provider.hasAnyValue(entry, rule, values));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing only
+ * an incorrect value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueOnlyIncorrect()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
+ values.add(new AttributeValue(entryDNType, "o=not test"));
+
+ assertFalse(provider.hasAnyValue(entry, rule, values));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing the
+ * correct value as well as multiple incorrect values.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueIncludesCorrect()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
+ values.add(new AttributeValue(entryDNType, "o=test"));
+ values.add(new AttributeValue(entryDNType, "o=not test"));
+ values.add(new AttributeValue(entryDNType, "o=not test either"));
+
+ assertTrue(provider.hasAnyValue(entry, rule, values));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of multiple values, none of
+ * which are correct.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueMissingCorrect()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
+ values.add(new AttributeValue(entryDNType, "o=not test"));
+ values.add(new AttributeValue(entryDNType, "o=not test either"));
+ values.add(new AttributeValue(entryDNType, "o=still not test"));
+
+ assertFalse(provider.hasAnyValue(entry, rule, values));
+ }
+
+
+
+ /**
+ * Tests the {@code matchesSubstring} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testMatchesSubstring()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedList<ByteString> subAny = new LinkedList<ByteString>();
+ subAny.add(ByteStringFactory.create("="));
+
+ assertEquals(provider.matchesSubstring(entry, rule, null, subAny, null),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Tests the {@code greaterThanOrEqualTo} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGreaterThanOrEqualTo()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ AttributeValue value = new AttributeValue(entryDNType, "o=test2");
+ assertEquals(provider.greaterThanOrEqualTo(entry, rule, value),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Tests the {@code lessThanOrEqualTo} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testLessThanOrEqualTo()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ AttributeValue value = new AttributeValue(entryDNType, "o=test2");
+ assertEquals(provider.lessThanOrEqualTo(entry, rule, value),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Tests the {@code approximatelyEqualTo} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testApproximatelyEqualTo()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ AttributeValue value = new AttributeValue(entryDNType, "o=test2");
+ assertEquals(provider.approximatelyEqualTo(entry, rule, value),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Retrieves a set of filters for use in testing searchability. The returned
+ * data will actually include three elements:
+ * <OL>
+ * <LI>The string representation of the search filter to use</LI>
+ * <LI>An indication of whether it should be searchable</LI>
+ * <LI>An indication of whether a minimal o=test entry should match</LI>
+ * </OL>
+ *
+ * @return A set of filters for use in testing searchability.
+ */
+ @DataProvider(name = "testFilters")
+ public Object[][] getTestFilters()
+ {
+ return new Object[][]
+ {
+ new Object[] { "(entryDN=o=test)", true, true },
+ new Object[] { "(entryDN=o=not test)", true, false },
+ new Object[] { "(o=test)", false, false },
+ new Object[] { "(entryDN=*)", false, false },
+ new Object[] { "(&(objectClass=*)(entryDN=o=test))", true, true },
+ new Object[] { "(&(entryDN=o=test)(entryDN=o=not test))", true, false },
+ new Object[] { "(|(objectClass=*)(entryDN=o=test))", false, false },
+ new Object[] { "(|(entryDN=o=test)(entryDN=o=not test))", true, true },
+ new Object[] { "(&(|(entryDN=o=test)(entryDN=o=not test))" +
+ "(&(objectClass=top)(|(objectClass=organization)" +
+ "(objectClass=domain)))" +
+ "(|(o=test)(o=not test)))", true, true }
+ };
+ }
+
+
+
+ /**
+ * Tests the {@code isSearchable} method with the provided information.
+ *
+ * @param filterString The string representation of the search filter to use
+ * for the test.
+ * @param isSearchable Indicates whether a search with the given filter
+ * should be considered searchable.
+ * @param shouldMatch Indicates whether the provided filter should match
+ * a minimal o=test entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testFilters")
+ public void testIsSearchable(String filterString, boolean isSearchable,
+ boolean shouldMatch)
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ SearchFilter filter = SearchFilter.createFilterFromString(filterString);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null,
+ DN.decode("o=test"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, null, null);
+
+ assertEquals(provider.isSearchable(rule, searchOperation), isSearchable);
+ }
+
+
+
+ /**
+ * Tests the {@code processSearch} method with the provided information.
+ *
+ * @param filterString The string representation of the search filter to use
+ * for the test.
+ * @param isSearchable Indicates whether a search with the given filter
+ * should be considered searchable.
+ * @param shouldMatch Indicates whether the provided filter should match
+ * a minimal o=test entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testFilters")
+ public void testProcessSearch(String filterString, boolean isSearchable,
+ boolean shouldMatch)
+ throws Exception
+ {
+ if (! isSearchable)
+ {
+ return;
+ }
+
+ TestCaseUtils.initializeTestBackend(true);
+
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ SearchFilter filter = SearchFilter.createFilterFromString(filterString);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null,
+ DN.decode("o=test"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, null, null);
+ provider.processSearch(rule, searchOperation);
+
+ if (shouldMatch)
+ {
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+ }
+ else
+ {
+ assertEquals(searchOperation.getSearchEntries().size(), 0);
+ }
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
new file mode 100644
index 0000000..5487dbb
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/IsMemberOfVirtualAttributeProviderTestCase.java
@@ -0,0 +1,1277 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
+import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringFactory;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * A set of test cases for the isMemberOf virtual attribute provider.
+ */
+public class IsMemberOfVirtualAttributeProviderTestCase
+ extends ExtensionsTestCase
+{
+ // The attribute type for the isMemberOf attribute.
+ private AttributeType isMemberOfType;
+
+
+
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ isMemberOfType = DirectoryServer.getAttributeType("ismemberof", false);
+ assertNotNull(isMemberOfType);
+ }
+
+
+
+ /**
+ * Tests that the isMemberOf virtual attribute is properly generated for an
+ * entry that is a member of a static group based on the member attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStaticGroupMembershipMember()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Static Group",
+ "member: uid=test.user,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(isMemberOfType));
+ for (Attribute a : e.getAttribute(isMemberOfType))
+ {
+ assertEquals(a.getValues().size(), 1);
+
+ assertTrue(a.hasValue());
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test static group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=not a group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
+ }
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests that the isMemberOf virtual attribute is properly generated for an
+ * entry that is a member of a static group based on the uniqueMember
+ * attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testStaticGroupMembershipUniqueMember()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "cn: Test Static Group",
+ "uniqueMember: uid=test.user,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(isMemberOfType));
+ for (Attribute a : e.getAttribute(isMemberOfType))
+ {
+ assertEquals(a.getValues().size(), 1);
+
+ assertTrue(a.hasValue());
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test static group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=not a group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
+ }
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests that the isMemberOf virtual attribute is properly generated for an
+ * entry that is a member of a dynamic group.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testDynamicGroupMembership()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Dynamic Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfURLs",
+ "cn: Test Dynamic Group",
+ "memberURL: ldap:///ou=People,o=test??sub?(sn=user)");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(isMemberOfType));
+ for (Attribute a : e.getAttribute(isMemberOfType))
+ {
+ assertEquals(a.getValues().size(), 1);
+
+ assertTrue(a.hasValue());
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test dynamic group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=not a group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
+ }
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(
+ DN.decode("cn=test dynamic group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests that the isMemberOf virtual attribute is properly generated for an
+ * entry that is a member of multiple static groups.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testMultipleStaticGroups()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: uid=test.user2,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user2",
+ "givenName: Test",
+ "sn: User2",
+ "cn: Test User2",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Group 1,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 1",
+ "member: uid=test.user,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 2,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 2",
+ "member: uid=test.user2,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 3,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 3",
+ "member: uid=test.user,ou=People,o=test",
+ "member: uid=test.user2,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(isMemberOfType));
+ for (Attribute a : e.getAttribute(isMemberOfType))
+ {
+ assertEquals(a.getValues().size(), 2);
+
+ assertTrue(a.hasValue());
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 1,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 2,ou=groups,o=test")));
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 3,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=not a group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
+ }
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 1,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 2,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 3,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests that the isMemberOf virtual attribute is properly generated for an
+ * entry that is a member of multiple static and dynamic groups.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testMultipleGroups()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: uid=test.user2,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user2",
+ "givenName: Test",
+ "sn: User2",
+ "cn: Test User2",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Group 1,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 1",
+ "member: uid=test.user,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 2,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 2",
+ "member: uid=test.user2,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 3,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 3",
+ "member: uid=test.user,ou=People,o=test",
+ "member: uid=test.user2,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 4,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfURLs",
+ "cn: Test Group 4",
+ "memberURL: ldap:///o=test??sub?(uid=test.user)",
+ "",
+ "dn: cn=Test Group 5,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfURLs",
+ "cn: Test Group 5",
+ "memberURL: ldap:///o=test??sub?(uid=test.user1)",
+ "",
+ "dn: cn=Test Group 6,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfURLs",
+ "cn: Test Group 6",
+ "memberURL: ldap:///o=test??sub?(givenName=test)");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(isMemberOfType));
+ for (Attribute a : e.getAttribute(isMemberOfType))
+ {
+ assertEquals(a.getValues().size(), 4);
+
+ assertTrue(a.hasValue());
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 1,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 2,ou=groups,o=test")));
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 3,ou=groups,o=test")));
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 4,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 5,ou=groups,o=test")));
+ assertTrue(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=test group 6,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType,
+ "cn=not a group,ou=groups,o=test")));
+ assertFalse(a.hasValue(new AttributeValue(isMemberOfType, "invalid")));
+ }
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 1,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 2,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 3,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 4,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 5,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 6,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the {@code isMultiValued} method.
+ */
+ @Test()
+ public void testIsMultiValued()
+ {
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+ assertTrue(provider.isMultiValued());
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with an empty set of values.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueEmptySet()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Static Group",
+ "member: uid=test.user,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertFalse(provider.hasAnyValue(e, rule,
+ Collections.<AttributeValue>emptySet()));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing only
+ * the correct value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueOnlyCorrect()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Static Group",
+ "member: uid=test.user,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(isMemberOfType,
+ "cn=test static group,ou=groups,o=test"));
+
+ assertTrue(provider.hasAnyValue(e, rule, values));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing only
+ * an incorrect value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueOnlyIncorrect()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Static Group",
+ "member: uid=test.user,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(isMemberOfType,
+ "cn=test dynamic group,ou=groups,o=test"));
+
+ assertFalse(provider.hasAnyValue(e, rule, values));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing the
+ * correct value as well as multiple incorrect values.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueIncludesCorrect()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Static Group",
+ "member: uid=test.user,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(isMemberOfType,
+ "cn=test static group,ou=groups,o=test"));
+ values.add(new AttributeValue(isMemberOfType,
+ "cn=test dynamic group,ou=groups,o=test"));
+
+ assertTrue(provider.hasAnyValue(e, rule, values));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of multiple values, none of
+ * which are correct.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueMissingCorrect()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Static Group",
+ "member: uid=test.user,ou=People,o=test");
+
+ Entry e =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ values.add(new AttributeValue(isMemberOfType,
+ "cn=test nonstatic group,ou=groups,o=test"));
+ values.add(new AttributeValue(isMemberOfType,
+ "cn=test dynamic group,ou=groups,o=test"));
+
+ assertFalse(provider.hasAnyValue(e, rule, values));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test static group,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+
+
+
+ /**
+ * Tests the {@code matchesSubstring} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testMatchesSubstring()
+ throws Exception
+ {
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedList<ByteString> subAny = new LinkedList<ByteString>();
+ subAny.add(ByteStringFactory.create("="));
+
+ assertEquals(provider.matchesSubstring(entry, rule, null, subAny, null),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Tests the {@code greaterThanOrEqualTo} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGreaterThanOrEqualTo()
+ throws Exception
+ {
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ AttributeValue value = new AttributeValue(isMemberOfType, "o=test2");
+ assertEquals(provider.greaterThanOrEqualTo(entry, rule, value),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Tests the {@code lessThanOrEqualTo} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testLessThanOrEqualTo()
+ throws Exception
+ {
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ AttributeValue value = new AttributeValue(isMemberOfType, "o=test2");
+ assertEquals(provider.lessThanOrEqualTo(entry, rule, value),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Tests the {@code approximatelyEqualTo} method to ensure that it returns a
+ * result of "undefined".
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testApproximatelyEqualTo()
+ throws Exception
+ {
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ AttributeValue value = new AttributeValue(isMemberOfType, "o=test2");
+ assertEquals(provider.approximatelyEqualTo(entry, rule, value),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Retrieves a set of filters for use in testing searchability. The returned
+ * data will actually include three elements:
+ * <OL>
+ * <LI>The string representation of the search filter to use</LI>
+ * <LI>An indication of whether it should be searchable</LI>
+ * <LI>An indication of whether the uid=test.user,ou=People,o=test entry
+ * should match</LI>
+ * </OL>
+ *
+ * @return A set of filters for use in testing searchability.
+ */
+ @DataProvider(name = "testFilters")
+ public Object[][] getTestFilters()
+ {
+ return new Object[][]
+ {
+ new Object[] { "(isMemberOf=*)", false, false },
+ new Object[] { "(isMemberOf=cn*)", false, false },
+ new Object[] { "(isMemberOf=invalid)", true, false },
+ new Object[] { "(&(isMemberOf=invalid1)(isMemberOf=invalid2))",
+ true, false },
+ new Object[] { "(isMemberOf>=cn=Test Group 1,ou=Groups,o=test)",
+ false, false },
+ new Object[] { "(isMemberOf<=cn=Test Group 1,ou=Groups,o=test)",
+ false, false },
+ new Object[] { "(isMemberOf~=cn=Test Group 1,ou=Groups,o=test)",
+ false, false },
+ new Object[] { "(isMemberOf=cn=Test Group 1,ou=Groups,o=test)",
+ true, true },
+ new Object[] { "(isMemberOf=cn=Test Group 2,ou=Groups,o=test)",
+ true, false },
+ new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
+ "(givenName=test))",
+ true, true },
+ new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
+ "(isMemberOf=invalid))",
+ true, false },
+ new Object[] { "(&(isMemberOf=invalid)" +
+ "(isMemberOf=cn=Test Group 1,ou=Groups,o=test))",
+ true, false },
+ new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
+ "(givenName=not test))",
+ true, false },
+ new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
+ "(isMemberOf=cn=Test Group 2,ou=Groups,o=test))",
+ true, false },
+ new Object[] { "(&(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
+ "(isMemberOf=cn=Test Group 3,ou=Groups,o=test))",
+ true, true },
+ new Object[] { "(&(isMemberOf=cn=Test Group 2,ou=Groups,o=test)" +
+ "(isMemberOf=cn=Test Group 4,ou=Groups,o=test))",
+ true, false },
+ new Object[] { "(|(isMemberOf=cn=Test Group 1,ou=Groups,o=test)" +
+ "(isMemberOf=cn=Test Group 3,ou=Groups,o=test))",
+ false, false },
+ };
+ }
+
+
+
+ /**
+ * Tests the {@code isSearchable} method with the provided information.
+ *
+ * @param filterString The string representation of the search filter to use
+ * for the test.
+ * @param isSearchable Indicates whether a search with the given filter
+ * should be considered searchable.
+ * @param shouldMatch Indicates whether the provided filter should match
+ * a minimal o=test entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testFilters")
+ public void testIsSearchable(String filterString, boolean isSearchable,
+ boolean shouldMatch)
+ throws Exception
+ {
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ SearchFilter filter = SearchFilter.createFilterFromString(filterString);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null,
+ DN.decode("o=test"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, null, null);
+
+ assertEquals(provider.isSearchable(rule, searchOperation), isSearchable);
+ }
+
+
+
+ /**
+ * Tests the {@code processSearch} method with the provided information.
+ *
+ * @param filterString The string representation of the search filter to use
+ * for the test.
+ * @param isSearchable Indicates whether a search with the given filter
+ * should be considered searchable.
+ * @param shouldMatch Indicates whether the provided filter should match
+ * a minimal o=test entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testFilters")
+ public void testProcessSearch(String filterString, boolean isSearchable,
+ boolean shouldMatch)
+ throws Exception
+ {
+ if (! isSearchable)
+ {
+ return;
+ }
+
+ TestCaseUtils.initializeTestBackend(true);
+
+ TestCaseUtils.addEntries(
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.user,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user",
+ "givenName: Test",
+ "sn: User",
+ "cn: Test User",
+ "userPassword: password",
+ "",
+ "dn: uid=test.user2,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user2",
+ "givenName: Test",
+ "sn: User2",
+ "cn: Test User2",
+ "userPassword: password",
+ "",
+ "dn: uid=test.user3,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.user3",
+ "givenName: Test",
+ "sn: User3",
+ "cn: Test User3",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Test Group 1,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 1",
+ "member: uid=test.user,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 2,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 2",
+ "member: uid=test.user2,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 3,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 3",
+ "member: uid=test.user,ou=People,o=test",
+ "member: uid=test.user2,ou=People,o=test",
+ "",
+ "dn: cn=Test Group 4,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Test Group 4",
+ "member: uid=test.user2,ou=People,o=test",
+ "member: uid=test.user3,ou=People,o=test");
+
+ Entry userEntry =
+ DirectoryServer.getEntry(DN.decode("uid=test.user,ou=People,o=test"));
+ assertNotNull(userEntry);
+
+ IsMemberOfVirtualAttributeProvider provider =
+ new IsMemberOfVirtualAttributeProvider();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(isMemberOfType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ SearchFilter filter = SearchFilter.createFilterFromString(filterString);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null,
+ DN.decode("o=test"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, null, null);
+ provider.processSearch(rule, searchOperation);
+
+ boolean matchFound = false;
+ for (Entry e : searchOperation.getSearchEntries())
+ {
+ if (e.getDN().equals(userEntry.getDN()))
+ {
+ if (matchFound)
+ {
+ fail("Multiple matches found for the same user.");
+ }
+ else
+ {
+ matchFound = true;
+ }
+ }
+ }
+
+ assertEquals(matchFound, shouldMatch);
+
+ DeleteOperation deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 1,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 2,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 3,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+
+ deleteOperation =
+ conn.processDelete(DN.decode("cn=test group 4,ou=groups,o=test"));
+ assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java
new file mode 100644
index 0000000..810c485
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/SubschemaSubentryVirtualAttributeProviderTestCase.java
@@ -0,0 +1,848 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ByteStringFactory;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.Control;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * A set of test cases for the subschemaSubentry virtual attribute provider.
+ */
+public class SubschemaSubentryVirtualAttributeProviderTestCase
+ extends ExtensionsTestCase
+{
+ // The attribute type for the subschemaSubentry attribute.
+ private AttributeType subschemaSubentryType;
+
+
+
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ subschemaSubentryType =
+ DirectoryServer.getAttributeType("subschemasubentry", false);
+ assertNotNull(subschemaSubentryType);
+ }
+
+
+
+ /**
+ * Retrieves a set of entry DNs for use in testing the subschemaSubentry
+ * virtual attribute.
+ *
+ * @return A set of entry DNs for use in testing the subschemaSubentry
+ * virtual attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "testEntryDNs")
+ public Object[][] getTestEntryDNs()
+ throws Exception
+ {
+ return new Object[][]
+ {
+ new Object[] { DN.decode("") },
+ new Object[] { DN.decode("o=test") },
+ new Object[] { DN.decode("dc=example,dc=com") },
+ new Object[] { DN.decode("cn=config") },
+ new Object[] { DN.decode("cn=schema") },
+ new Object[] { DN.decode("cn=tasks") },
+ new Object[] { DN.decode("cn=monitor") },
+ new Object[] { DN.decode("cn=backups") }
+ };
+ }
+
+
+
+ /**
+ * Tests the {@code getEntry} method for the specified entry to ensure that
+ * the entry returned includes the subschemaSubentry operational attribute
+ * with the correct value.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testGetEntry(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ Entry e = DirectoryServer.getEntry(entryDN);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(subschemaSubentryType));
+
+ List<Attribute> attrList = e.getAttribute(subschemaSubentryType);
+ assertNotNull(attrList);
+ assertFalse(attrList.isEmpty());
+ for (Attribute a : attrList)
+ {
+ assertTrue(a.hasValue());
+ assertEquals(a.getValues().size(), 1);
+ assertTrue(a.hasValue(new AttributeValue(subschemaSubentryType,
+ "cn=schema")));
+ }
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is not included when the list of attributes
+ * requested is empty (defaulting to all user attributes).
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchEmptyAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT, filter);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is not included when the list of requested
+ * attributes is "1.1", meaning no attributes.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchNoAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("1.1");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is not included when all user attributes
+ * are requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchAllUserAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("*");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is included when all operational attributes
+ * are requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchAllOperationalAttrs(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("+");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is included when that attribute is
+ * specifically requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchSubschemaSubentryAttr(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("subschemasubentry");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is not included when it is not in the list
+ * of attributes that is explicitly requested.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchExcludeSubschemaSubentryAttr(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("objectClass");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is included when that attribute is
+ * specifically requested and the subschemaSubentry attribute is used in the
+ * search filter with a matching value.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchSubschemaSubentryAttrInMatchingFilter(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(subschemaSubentry=cn=schema)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("subschemaSubentry");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * no entries are returned when the subschemaSubentry attribute is used in the
+ * search filter with a non-matching value.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchSubschemaSubentryAttrInNonMatchingFilter(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(subschemaSubentry=cn=foo)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("subschemaSubentry");
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ filter, attrList);
+ assertEquals(searchOperation.getSearchEntries().size(), 0);
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is not included when that attribute is
+ * specifically requested and the real attributes only control is included in
+ * the request.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchSubschemaSubentryAttrRealAttrsOnly(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("subschemaSubentry");
+
+ LinkedList<Control> requestControls = new LinkedList<Control>();
+ requestControls.add(new Control(OID_REAL_ATTRS_ONLY, true));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), requestControls,
+ entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, attrList, null);
+ searchOperation.run();
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertFalse(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Performs an internal search to retrieve the specified entry, ensuring that
+ * the subschemaSubentry attribute is included when that attribute is
+ * specifically requested and the virtual attributes only control is included
+ * in the request.
+ *
+ * @param entryDN The DN of the entry to retrieve and verify.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testEntryDNs")
+ public void testSearchSubschemaSubentryAttrVirtualAttrsOnly(DN entryDN)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+
+ SearchFilter filter =
+ SearchFilter.createFilterFromString("(objectClass=*)");
+ LinkedHashSet<String> attrList = new LinkedHashSet<String>(1);
+ attrList.add("subschemaSubentry");
+
+ LinkedList<Control> requestControls = new LinkedList<Control>();
+ requestControls.add(new Control(OID_VIRTUAL_ATTRS_ONLY, true));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), requestControls,
+ entryDN, SearchScope.BASE_OBJECT,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+ 0, false, filter, attrList, null);
+ searchOperation.run();
+ assertEquals(searchOperation.getSearchEntries().size(), 1);
+
+ Entry e = searchOperation.getSearchEntries().get(0);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(subschemaSubentryType));
+ }
+
+
+
+ /**
+ * Tests the {@code isMultiValued} method.
+ */
+ @Test()
+ public void testIsMultiValued()
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+ assertFalse(provider.isMultiValued());
+ }
+
+
+
+ /**
+ * Tests the {@code getValues} method for an entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGetValues()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+ assertNotNull(values);
+ assertEquals(values.size(), 1);
+ assertTrue(values.contains(new AttributeValue(subschemaSubentryType,
+ "cn=schema")));
+ }
+
+
+
+ /**
+ * Tests the {@code hasValue} method variant that doesn't take a specific
+ * value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValue()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertTrue(provider.hasValue(entry, rule));
+ }
+
+
+
+ /**
+ * Tests the {@code hasValue} method variant that takes a specific value when
+ * the provided value is a match.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasMatchingValue()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertTrue(provider.hasValue(entry, rule,
+ new AttributeValue(subschemaSubentryType,
+ "cn=schema")));
+ }
+
+
+
+ /**
+ * Tests the {@code hasValue} method variant that takes a specific value when
+ * the provided value is not a match.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasNonMatchingValue()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertFalse(provider.hasValue(entry, rule,
+ new AttributeValue(subschemaSubentryType,
+ "cn=not schema")));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with an empty set of values.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueEmptySet()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ assertFalse(provider.hasAnyValue(entry, rule,
+ Collections.<AttributeValue>emptySet()));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing only
+ * the correct value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueOnlyCorrect()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
+ values.add(new AttributeValue(subschemaSubentryType, "cn=schema"));
+
+ assertTrue(provider.hasAnyValue(entry, rule, values));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing only
+ * an incorrect value.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueOnlyIncorrect()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
+ values.add(new AttributeValue(subschemaSubentryType, "cn=not schema"));
+
+ assertFalse(provider.hasAnyValue(entry, rule, values));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of values containing the
+ * correct value as well as multiple incorrect values.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueIncludesCorrect()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
+ values.add(new AttributeValue(subschemaSubentryType, "cn=schema"));
+ values.add(new AttributeValue(subschemaSubentryType, "cn=not schema"));
+ values.add(new AttributeValue(subschemaSubentryType,
+ "cn=not schema either"));
+
+ assertTrue(provider.hasAnyValue(entry, rule, values));
+ }
+
+
+
+ /**
+ * Tests the {@code hasAnyValue} method with a set of multiple values, none of
+ * which are correct.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testHasAnyValueMissingCorrect()
+ throws Exception
+ {
+ SubschemaSubentryVirtualAttributeProvider provider =
+ new SubschemaSubentryVirtualAttributeProvider();
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+ entry.processVirtualAttributes();
+
+ VirtualAttributeRule rule =
+ new VirtualAttributeRule(subschemaSubentryType, provider,
+ Collections.<DN>emptySet(), Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ VirtualAttributeCfgDefn.ConflictBehavior.
+ VIRTUAL_OVERRIDES_REAL);
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(3);
+ values.add(new AttributeValue(subschemaSubentryType, "cn=not schema"));
+ values.add(new AttributeValue(subschemaSubentryType,
+ "cn=not schema either"));
+ values.add(new AttributeValue(subschemaSubentryType,
+ "cn=still not schema"));
+
+ assertFalse(provider.hasAnyValue(entry, rule, values));
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/interop/LazyDNTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/interop/LazyDNTestCase.java
index b739017..7145bae 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/interop/LazyDNTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/interop/LazyDNTestCase.java
@@ -40,6 +40,7 @@
import org.opends.server.TestCaseUtils;
import org.opends.server.types.DN;
import org.opends.server.types.RDN;
+import org.opends.server.types.SearchScope;
import static org.testng.Assert.*;
@@ -110,6 +111,10 @@
sigs.add(new String[] { "isAncestorOf",
"boolean",
"org.opends.server.types.DN" });
+ sigs.add(new String[] { "matchesBaseAndScope",
+ "boolean",
+ "org.opends.server.types.DN",
+ "org.opends.server.types.SearchScope" });
sigs.add(new String[] { "equals",
"boolean",
"java.lang.Object" });
@@ -578,6 +583,40 @@
/**
+ * Tests the {@code matchesBaseAndScope} method with valid DN strings.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testMatchesBaseAndScope()
+ throws Exception
+ {
+ assertTrue(new LazyDN("").matchesBaseAndScope(DN.nullDN(),
+ SearchScope.BASE_OBJECT));
+ assertTrue(new LazyDN("dc=example,dc=com").matchesBaseAndScope(
+ DN.decode("dc=example,dc=com"), SearchScope.BASE_OBJECT));
+ assertTrue(new LazyDN("ou=People,dc=example,dc=com").matchesBaseAndScope(
+ DN.decode("dc=example,dc=com"), SearchScope.WHOLE_SUBTREE));
+ }
+
+
+
+ /**
+ * Tests the {@code matchesBaseAndScope} method with an invalid DN string.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(expectedExceptions = { RuntimeException.class })
+ public void testMatchesBaseandScopeInvalid()
+ throws Exception
+ {
+ new LazyDN("invalid").matchesBaseAndScope(DN.decode("dc=example,dc=com"),
+ SearchScope.WHOLE_SUBTREE);
+ }
+
+
+
+ /**
* Tests the {@code equals} method with valid DN strings.
*
* @throws Exception If an unexpected problem occurs.
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java
index bc39d1a..1478b9c 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/ProtocolWindowTest.java
@@ -113,7 +113,7 @@
assertTrue(checkChangelogQueueSize(CHANGELOG_QUEUE_SIZE));
// Create an Entry (add operation) that will be later used in the test.
- Entry tmp = personEntry.duplicate();
+ Entry tmp = personEntry.duplicate(false);
AddOperation addOp = new AddOperation(connection,
InternalClientConnection.nextOperationID(), InternalClientConnection
.nextMessageID(), null, tmp.getDN(),
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java
index 9a797fd..44b8188 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/StressTest.java
@@ -121,7 +121,7 @@
*/
// Create an Entry (add operation) that will be later used in the test.
- Entry tmp = personEntry.duplicate();
+ Entry tmp = personEntry.duplicate(false);
AddOperation addOp = new AddOperation(connection,
InternalClientConnection.nextOperationID(), InternalClientConnection
.nextMessageID(), null, tmp.getDN(),
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java
index f89e5a2..fd4e449 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/SynchronizationTestCase.java
@@ -410,11 +410,11 @@
while ((count> 0) && (found != exist))
{
Thread.sleep(200);
-
+
found = DirectoryServer.entryExists(dn);
count--;
}
-
+
Lock lock = null;
for (int i=0; i < 3; i++)
{
@@ -424,19 +424,19 @@
break;
}
}
-
+
if (lock == null)
{
throw new Exception("could not lock entry " + dn);
}
-
+
try
{
Entry entry = DirectoryServer.getEntry(dn);
if (entry == null)
return null;
else
- return entry.duplicate();
+ return entry.duplicate(true);
}
finally
{
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
index c151dea..a681cc3 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/synchronization/UpdateOperationTest.java
@@ -844,7 +844,7 @@
*/
// Create an Entry (add operation)
- Entry tmp = personEntry.duplicate();
+ Entry tmp = personEntry.duplicate(false);
AddOperation addOp = new AddOperation(connection,
InternalClientConnection.nextOperationID(), InternalClientConnection
.nextMessageID(), null, tmp.getDN(),
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeRuleTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeRuleTestCase.java
new file mode 100644
index 0000000..dbec7cb
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeRuleTestCase.java
@@ -0,0 +1,337 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.
+ VirtualAttributeCfgDefn.ConflictBehavior;
+import org.opends.server.extensions.EntryDNVirtualAttributeProvider;
+import org.opends.server.protocols.internal.InternalClientConnection;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * This class provides a set of test cases for virtual attribute rules, which
+ * link a virtual attribute provider implementation with an attribute type and a
+ * set of criteria for identifying the entries with which that provider should
+ * be used.
+ */
+public class VirtualAttributeRuleTestCase
+ extends TypesTestCase
+{
+ // The attribute type for the entryDN attribute.
+ private AttributeType entryDNType;
+
+
+
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ entryDNType = DirectoryConfig.getAttributeType("entrydn", false);
+ assertNotNull(entryDNType);
+ }
+
+
+
+ /**
+ * Retrieves a set of virtual attribute rules that may be used for testing
+ * purposes. The return data will also include a Boolean value indicating
+ * whether the rule would apply to a minimal "o=test" entry.
+ *
+ * @return A set of virtual attribute rules that may be used for testing
+ * purposes.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "testRules")
+ public Object[][] getVirtualAttributeRules()
+ throws Exception
+ {
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ LinkedHashSet<DN> dnSet1 = new LinkedHashSet<DN>(1);
+ dnSet1.add(DN.decode("o=test"));
+
+ LinkedHashSet<DN> dnSet2 = new LinkedHashSet<DN>(1);
+ dnSet2.add(DN.decode("dc=example,dc=com"));
+
+ LinkedHashSet<DN> dnSet3 = new LinkedHashSet<DN>(2);
+ dnSet3.add(DN.decode("o=test"));
+ dnSet3.add(DN.decode("dc=example,dc=com"));
+
+
+ LinkedHashSet<DN> groupSet1 = new LinkedHashSet<DN>(1);
+ groupSet1.add(DN.decode("cn=Test Group,o=test"));
+
+ LinkedHashSet<DN> groupSet2 = new LinkedHashSet<DN>(1);
+ groupSet2.add(DN.decode("cn=Example Group,o=test"));
+
+ LinkedHashSet<DN> groupSet3= new LinkedHashSet<DN>(2);
+ groupSet3.add(DN.decode("cn=Test Group,o=test"));
+ groupSet3.add(DN.decode("cn=Example Group,o=test"));
+
+
+ LinkedHashSet<SearchFilter> filterSet1 = new LinkedHashSet<SearchFilter>(1);
+ filterSet1.add(SearchFilter.createFilterFromString("(objectClass=*)"));
+
+ LinkedHashSet<SearchFilter> filterSet2 = new LinkedHashSet<SearchFilter>(1);
+ filterSet2.add(SearchFilter.createFilterFromString("(o=test)"));
+
+ LinkedHashSet<SearchFilter> filterSet3 = new LinkedHashSet<SearchFilter>(1);
+ filterSet3.add(SearchFilter.createFilterFromString("(foo=bar)"));
+
+ LinkedHashSet<SearchFilter> filterSet4 = new LinkedHashSet<SearchFilter>(2);
+ filterSet4.add(SearchFilter.createFilterFromString("(o=test)"));
+ filterSet4.add(SearchFilter.createFilterFromString("(foo=bar)"));
+
+ return new Object[][]
+ {
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(),
+ Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider, dnSet1,
+ Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider, dnSet2,
+ Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ false
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider, dnSet3,
+ Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), groupSet1,
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), groupSet2,
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ false
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(), groupSet3,
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(),
+ Collections.<DN>emptySet(), filterSet1,
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(),
+ Collections.<DN>emptySet(), filterSet2,
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(),
+ Collections.<DN>emptySet(), filterSet3,
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ false
+ },
+
+ new Object[]
+ {
+ new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(),
+ Collections.<DN>emptySet(), filterSet4,
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL),
+ true
+ },
+ };
+ }
+
+
+
+ /**
+ * Tests the various getter methods in the virtual attribute rule class.
+ *
+ * @param rule The rule for which to perform the test.
+ * @param appliesToEntry Indicates whether the provided rule applies to a
+ * minimal "o=test" entry.
+ */
+ @Test(dataProvider = "testRules")
+ public void testGetters(VirtualAttributeRule rule, boolean appliesToEntry)
+ {
+ assertEquals(rule.getAttributeType(), entryDNType);
+ assertEquals(rule.getProvider().getClass().getName(),
+ EntryDNVirtualAttributeProvider.class.getName());
+ assertNotNull(rule.getBaseDNs());
+ assertNotNull(rule.getGroupDNs());
+ assertNotNull(rule.getFilters());
+ assertNotNull(rule.getConflictBehavior());
+ }
+
+
+
+ /**
+ * Tests the {@code appliesToEntry} method.
+ *
+ * @param rule The rule for which to perform the test.
+ * @param appliesToEntry Indicates whether the provided rule applies to a
+ * minimal "o=test" entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "testRules")
+ public void testAppliesToEntry(VirtualAttributeRule rule,
+ boolean appliesToEntry)
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ addGroups();
+ assertEquals(rule.appliesToEntry(
+ DirectoryConfig.getEntry(DN.decode("o=test"))),
+ appliesToEntry);
+ removeGroups();
+ }
+
+
+
+ /**
+ * Tests the {@code toString} method.
+ *
+ * @param rule The rule for which to perform the test.
+ * @param appliesToEntry Indicates whether the provided rule applies to a
+ * minimal "o=test" entry.
+ */
+ @Test(dataProvider = "testRules")
+ public void testToString(VirtualAttributeRule rule, boolean appliesToEntry)
+ {
+ String ruleString = rule.toString();
+ assertNotNull(ruleString);
+ assertTrue(ruleString.length() > 0);
+ }
+
+
+
+ /**
+ * Adds a group to the server in which the "o=test" entry is a member.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void addGroups()
+ throws Exception
+ {
+ TestCaseUtils.addEntries(
+ "dn: cn=Test Group,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "cn: Test Group",
+ "uniqueMember: o=test",
+ "",
+ "dn: cn=Example Group,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "cn: Example Group",
+ "uniqueMember: dc=example,dc=com");
+ }
+
+
+
+ /**
+ * Removes the test group from the server.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void removeGroups()
+ throws Exception
+ {
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ conn.processDelete(DN.decode("cn=Test Group,o=Test"));
+ conn.processDelete(DN.decode("cn=Example Group,o=Test"));
+ }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java
new file mode 100644
index 0000000..409a713
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/VirtualAttributeTestCase.java
@@ -0,0 +1,192 @@
+/*
+ * 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
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.
+ VirtualAttributeCfgDefn.ConflictBehavior;
+import org.opends.server.extensions.EntryDNVirtualAttributeProvider;
+import org.opends.server.protocols.internal.InternalClientConnection;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * This class provides a set of test cases for virtual attributes.
+ */
+public class VirtualAttributeTestCase
+ extends TypesTestCase
+{
+ // The attribute type for the entryDN attribute.
+ private AttributeType entryDNType;
+
+ // The virtual attribute instance that will be used for all the testing.
+ private VirtualAttribute virtualAttribute;
+
+ // The virutal attribute rule that will be used for the testing.
+ private VirtualAttributeRule virtualAttributeRule;
+
+
+
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ entryDNType = DirectoryConfig.getAttributeType("entrydn", false);
+ assertNotNull(entryDNType);
+
+ EntryDNVirtualAttributeProvider provider =
+ new EntryDNVirtualAttributeProvider();
+
+ virtualAttributeRule = new VirtualAttributeRule(entryDNType, provider,
+ Collections.<DN>emptySet(),
+ Collections.<DN>emptySet(),
+ Collections.<SearchFilter>emptySet(),
+ ConflictBehavior.VIRTUAL_OVERRIDES_REAL);
+
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: o=test",
+ "objectClass: top",
+ "objectClass: organization",
+ "o: test");
+
+ virtualAttribute = new VirtualAttribute(entryDNType, entry,
+ virtualAttributeRule);
+ }
+
+
+
+ /**
+ * Tests the various getter methods for virtual attributes.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGetters()
+ throws Exception
+ {
+ assertNotNull(virtualAttribute.getEntry());
+ assertEquals(virtualAttribute.getEntry().getDN(),
+ DN.decode("o=test"));
+
+ assertEquals(virtualAttribute.getVirtualAttributeRule(),
+ virtualAttributeRule);
+
+ assertTrue(virtualAttribute.isVirtual());
+ assertTrue(virtualAttribute.duplicate(true).isVirtual());
+ assertTrue(virtualAttribute.duplicate(false).isVirtual());
+ }
+
+
+
+ /**
+ * Tests the various methods that interact with the virtual values.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testValues()
+ throws Exception
+ {
+ LinkedHashSet<AttributeValue> values = virtualAttribute.getValues();
+ assertEquals(values.size(), 1);
+ assertTrue(values.contains(new AttributeValue(entryDNType, "o=test")));
+
+ assertTrue(virtualAttribute.hasValue());
+
+ assertTrue(virtualAttribute.hasValue(new AttributeValue(entryDNType,
+ "o=test")));
+ assertFalse(virtualAttribute.hasValue(new AttributeValue(entryDNType,
+ "o=not test")));
+
+ LinkedHashSet<AttributeValue> testValues =
+ new LinkedHashSet<AttributeValue>();
+ testValues.add(new AttributeValue(entryDNType, "o=test"));
+ assertTrue(virtualAttribute.hasAllValues(testValues));
+ assertTrue(virtualAttribute.hasAnyValue(testValues));
+
+ testValues.add(new AttributeValue(entryDNType, "o=not test"));
+ assertFalse(virtualAttribute.hasAllValues(testValues));
+ assertTrue(virtualAttribute.hasAnyValue(testValues));
+ }
+
+
+
+ /**
+ * Tests the various methods that apply to different kinds of matching.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testMatching()
+ throws Exception
+ {
+ assertEquals(virtualAttribute.matchesSubstring(
+ ByteStringFactory.create("o="), null,
+ ByteStringFactory.create("test")),
+ ConditionResult.UNDEFINED);
+
+ AttributeValue assertionValue = new AttributeValue(entryDNType, "o=test");
+ assertEquals(virtualAttribute.greaterThanOrEqualTo(assertionValue),
+ ConditionResult.UNDEFINED);
+ assertEquals(virtualAttribute.lessThanOrEqualTo(assertionValue),
+ ConditionResult.UNDEFINED);
+ assertEquals(virtualAttribute.approximatelyEqualTo(assertionValue),
+ ConditionResult.UNDEFINED);
+ }
+
+
+
+ /**
+ * Tests the {@code toString} method.
+ */
+ @Test()
+ public void testToString()
+ {
+ String vattrString = virtualAttribute.toString();
+ assertNotNull(vattrString);
+ assertTrue(vattrString.length() > 0);
+ }
+}
+
--
Gitblit v1.10.0