This adds the numSubordinates and hasSubordinates operational attribute support in OpenDS.
- Implemented as virtual attributes
- They are enabled by default
- numSubordinates and hasSubordinates methods added to the backend API and implemented for all existing backends
- JE implementation uses the id2children index to keep count of the number of subordinates for each entry.
- The behavior of exceeding the index-entry-limit (ALL-IDs) has changed to store a 8 byte entry ID set count with the most significant bit
set to 1 instead of a 0 byte array to signify the index-entry-limit has been exceeded. The previous format is still compatible but all requests
for numSubordinates will return undefined (-1).
- The DBTest tool is also included in this fix. This can be used to list root containers, entry containers, database containers, index
status, as well as dumping a database with or without decoding the data.
Fix for issues 43 and 72
7 files added
22 files modified
| New file |
| | |
| | | #!/bin/sh |
| | | # |
| | | # 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. |
| | | |
| | | |
| | | # This script may be used to debug JE backends in the Directory Server. |
| | | OPENDS_INVOKE_CLASS="org.opends.server.tools.DBTest" |
| | | export OPENDS_INVOKE_CLASS |
| | | |
| | | SCRIPT_NAME_ARG="-Dorg.opends.server.scriptName=dbtest" |
| | | export SCRIPT_NAME_ARG |
| | | |
| | | SCRIPT_DIR=`dirname "${0}"` |
| | | "${SCRIPT_DIR}/../lib/_server-script.sh" "${@}" |
| New file |
| | |
| | | |
| | | @echo off |
| | | rem CDDL HEADER START |
| | | rem |
| | | rem The contents of this file are subject to the terms of the |
| | | rem Common Development and Distribution License, Version 1.0 only |
| | | rem (the "License"). You may not use this file except in compliance |
| | | rem with the License. |
| | | rem |
| | | rem You can obtain a copy of the license at |
| | | rem trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | rem or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | rem See the License for the specific language governing permissions |
| | | rem and limitations under the License. |
| | | rem |
| | | rem When distributing Covered Code, include this CDDL HEADER in each |
| | | rem file and include the License file at |
| | | rem trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, |
| | | rem add the following below this CDDL HEADER, with the fields enclosed |
| | | rem by brackets "[]" replaced with your own identifying information: |
| | | rem Portions Copyright [yyyy] [name of copyright owner] |
| | | rem |
| | | rem CDDL HEADER END |
| | | rem |
| | | rem |
| | | rem Portions Copyright 2006-2007 Sun Microsystems, Inc. |
| | | |
| | | setlocal |
| | | |
| | | set OPENDS_INVOKE_CLASS="org.opends.server.tools.DBTest" |
| | | set SCRIPT_NAME_ARG="-Dorg.opends.server.scriptName=dbtest" |
| | | for %%i in (%~sf0) do call "%%~dPsi\..\lib\_server-script.bat" %* |
| | | |
| | |
| | | ds-cfg-virtual-attribute-filter: (&(objectClass=groupOfUniqueNames)(objectClass=ds-virtual-static-group)) |
| | | ds-cfg-allow-retrieving-membership: false |
| | | |
| | | dn: cn=numSubordinates,cn=Virtual Attributes,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-virtual-attribute |
| | | cn: numSubordinates |
| | | ds-cfg-virtual-attribute-class: org.opends.server.extensions.NumSubordinatesVirtualAttributeProvider |
| | | ds-cfg-virtual-attribute-enabled: true |
| | | ds-cfg-virtual-attribute-type: numSubordinates |
| | | ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real |
| | | |
| | | dn: cn=hasSubordinates,cn=Virtual Attributes,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-virtual-attribute |
| | | cn: hasSubordinates |
| | | ds-cfg-virtual-attribute-class: org.opends.server.extensions.HasSubordinatesVirtualAttributeProvider |
| | | ds-cfg-virtual-attribute-enabled: true |
| | | ds-cfg-virtual-attribute-type: hasSubordinates |
| | | ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real |
| | | |
| | | dn: cn=Work Queue,cn=config |
| | | objectClass: top |
| | | objectClass: ds-cfg-work-queue |
| | |
| | | attributeTypes: ( 2.5.18.4 NAME 'modifiersName' EQUALITY distinguishedNameMatch |
| | | SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION |
| | | USAGE directoryOperation X-ORIGIN 'RFC 4512' ) |
| | | attributeTypes: ( 2.5.18.9 NAME 'hasSubordinates' EQUALITY booleanMatch |
| | | SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE NO-USER-MODIFICATION |
| | | USAGE directoryOperation X-ORIGIN 'X.501' ) |
| | | attributeTypes: ( 2.5.18.10 NAME 'subschemaSubentry' |
| | | EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 |
| | | SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation |
| | |
| | | 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' ) |
| | | attributeTypes: ( 1.3.6.1.4.1.453.16.2.103 NAME 'numSubordinates' |
| | | DESC 'Count of immediate subordinates' |
| | | EQUALITY integerMatch ORDERING integerOrderingMatch |
| | | SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 |
| | | SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation |
| | | X-ORIGIN 'draft-ietf-boreham-numsubordinates' ) |
| | | 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 |
| | |
| | | included in the nested group list for the group |
| | | MILD_ERR_STATICGROUP_GROUP_INSTANCE_INVALID_540=Group instance with DN %s has \ |
| | | been deleted and is no longer valid |
| | | MILD_ERR_NUMSUBORDINATES_VATTR_NOT_SEARCHABLE_541=The %s attribute is not \ |
| | | searchable and should not be included in otherwise unindexed search filters |
| | | MILD_ERR_HASSUBORDINATES_VATTR_NOT_SEARCHABLE_542=The %s attribute is not \ |
| | | searchable and should not be included in otherwise unindexed search filters |
| | |
| | | generate the RC script: %s |
| | | SEVERE_ERR_DSCFG_ERROR_QUIET_AND_INTERACTIVE_INCOMPATIBLE_1326=If you specify \ |
| | | the {%s} argument you must also specify {%s} |
| | | INFO_DESCRIPTION_DBTEST_TOOL_1327=This utility may be used to debug the JE \ |
| | | database |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ROOT_CONTAINERS_1328=List the root \ |
| | | containers used by all JE backends |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ENTRY_CONTAINERS_1329=List the entry \ |
| | | containers for a root container |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_DUMP_DATABASE_CONTAINER_1330=Dump records from \ |
| | | a database container |
| | | INFO_DESCRIPTION_DBTEST_BACKEND_ID_1331=The backend ID of the JE backend to \ |
| | | debug |
| | | INFO_DESCRIPTION_DBTEST_BASE_DN_1332=The base DN of the entry container to debug |
| | | INFO_DESCRIPTION_DBTEST_DATABASE_NAME_1333=The name of the database container \ |
| | | to debug |
| | | INFO_DESCRIPTION_DBTEST_SKIP_DECODE_1334=Do not try to decode the JE data to \ |
| | | their appropreate types |
| | | MILD_ERR_DBTEST_DECODE_FAIL_1335=An error occured while decoding data: %s |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_INDEX_STATUS_1336=List the status of \ |
| | | indexes in a entry container |
| | | INFO_DESCRIPTION_DBTEST_MAX_KEY_VALUE_1337=Only show records with keys that \ |
| | | should be ordered before the provided value using the comparator for the \ |
| | | database container |
| | | INFO_DESCRIPTION_DBTEST_MIN_KEY_VALUE_1338=Only show records with keys that \ |
| | | should be ordered after the provided value using the comparator for the \ |
| | | database container |
| | | INFO_DESCRIPTION_DBTEST_MAX_DATA_SIZE_1339=Only show records whose data is no \ |
| | | larger than the provided value |
| | | INFO_DESCRIPTION_DBTEST_MIN_DATA_SIZE_1340=Only show records whose data is no \ |
| | | smaller than the provided value |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_DATABASE_CONTAINERS_1341=List the database \ |
| | | containers for a entry container |
| | | INFO_LABEL_DBTEST_BACKEND_ID_1342=Backend ID |
| | | INFO_LABEL_DBTEST_DB_DIRECTORY_1343=Database Directory |
| | | INFO_LABEL_DBTEST_BASE_DN_1344=Base DN |
| | | INFO_LABEL_DBTEST_JE_DATABASE_PREFIX_1345=JE Database Prefix |
| | | INFO_LABEL_DBTEST_ENTRY_COUNT_1346=Entry Count |
| | | SEVERE_ERR_DBTEST_NO_BACKENDS_FOR_ID_1347=None of the Directory Server \ |
| | | backends are configured with the requested backend ID %s |
| | | SEVERE_ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN_1348=None of the entry \ |
| | | containers are configured with the requested base DN %s in backend %s |
| | | SEVERE_ERR_DBTEST_NO_DATABASE_CONTAINERS_FOR_NAME_1349=No database container \ |
| | | exists with the requested name %s in entry container %s and backend %s |
| | | SEVERE_ERR_DBTEST_ERROR_INITIALIZING_BACKEND_1350=An unexpected error occured \ |
| | | while attempting to initialize the JE backend %s: %s |
| | | SEVERE_ERR_DBTEST_ERROR_READING_DATABASE_1351=An unexpected error occured \ |
| | | while attempting to read and/or decode records from the database: %s |
| | | SEVERE_ERR_DBTEST_DECODE_BASE_DN_1352=Unable to decode base DN string "%s" as \ |
| | | a valid distinguished name: %s |
| | | INFO_LABEL_DBTEST_DATABASE_NAME_1353=Database Name |
| | | INFO_LABEL_DBTEST_DATABASE_TYPE_1354=Database Type |
| | | INFO_LABEL_DBTEST_JE_DATABASE_NAME_1355=JE Database Name |
| | | INFO_LABEL_DBTEST_JE_RECORD_COUNT_1356=Record Count |
| | | INFO_LABEL_DBTEST_INDEX_NAME_1357=Index Name |
| | | INFO_LABEL_DBTEST_INDEX_TYPE_1358=Index Type |
| | | INFO_LABEL_DBTEST_INDEX_STATUS_1359=Index Status |
| | | INFO_LABEL_DBTEST_KEY_1360=Key |
| | | INFO_LABEL_DBTEST_DATA_1361=Data |
| | | SEVERE_WARN_DBTEST_CANNOT_UNLOCK_BACKEND_1362=An error occurred while \ |
| | | attempting to release the shared lock for backend %s: %s. This lock should \ |
| | | automatically be cleared when the process exits, so no further action \ |
| | | should be required |
| | | SEVERE_ERR_DBTEST_CANNOT_LOCK_BACKEND_1363=An error occurred while \ |
| | | attempting to acquire a shared lock for backend %s: %s. This generally \ |
| | | means that some other process has exclusive access to this backend (e.g., a \ |
| | | restore or an LDIF import) |
| | | SEVERE_ERR_DBTEST_CANNOT_DECODE_KEY_1364=An error occured while decoding the \ |
| | | min/max key value %s: %s. Values prefixed with "0x" will be decoded as raw \ |
| | | bytes in hex. When dumping the DN2ID database, the value must be a valid \ |
| | | distinguished name. When dumping the ID2Entry database, the value will be \ |
| | | decoded as a entry ID. When dumping all other databases, the value will be \ |
| | | decoded as a string |
| | | INFO_LABEL_DBTEST_ENTRY_1365=Entry |
| | | INFO_LABEL_DBTEST_ENTRY_ID_1366=Entry ID |
| | | INFO_LABEL_DBTEST_ENTRY_DN_1367=Entry DN |
| | | INFO_LABEL_DBTEST_URI_1368=URI |
| | | INFO_LABEL_DBTEST_INDEX_VALUE_1369=Indexed Value |
| | | INFO_LABEL_DBTEST_INDEX_ENTRY_ID_LIST_1370=Entry ID List |
| | | INFO_LABEL_DBTEST_VLV_INDEX_LAST_SORT_KEYS_1371=Last Sort Keys |
| | | SEVERE_ERR_DBTEST_CANNOT_DECODE_SIZE_1372=An error occured while parsing the \ |
| | | min/max data size %s as a integer: %s |
| | |
| | | import org.opends.server.types.LockManager; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.WritabilityMode; |
| | | import org.opends.server.types.ConditionResult; |
| | | |
| | | import static org.opends.messages.BackendMessages.*; |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the requested entry has any subordinates. |
| | | * |
| | | * @param entryDN The distinguished name of the entry. |
| | | * |
| | | * @return {@code ConditionResult.TRUE} if the entry has one or more |
| | | * subordinates or {@code ConditionResult.FALSE} otherwise |
| | | * or {@code ConditionResult.UNDEFINED} if it can not be |
| | | * determined. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while trying to |
| | | * retrieve the entry. |
| | | */ |
| | | public abstract ConditionResult hasSubordinates(DN entryDN) |
| | | throws DirectoryException; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the number of subordinates for the requested entry. |
| | | * |
| | | * @param entryDN The distinguished name of the entry. |
| | | * |
| | | * @return The number of subordinate entries for the requested entry |
| | | * or -1 if it can not be determined. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while trying to |
| | | * retrieve the entry. |
| | | */ |
| | | public abstract long numSubordinates(DN entryDN) |
| | | throws DirectoryException; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether an entry with the specified DN exists in the |
| | | * backend. The default implementation obtains a read lock and calls |
| | | * {@code getEntry}, but backend implementations may override this |
| | |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.schema.BooleanSyntax; |
| | | import org.opends.server.schema.GeneralizedTimeSyntax; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.BackupInfo; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | 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.LDIFExportConfig; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.LDIFImportResult; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.RDN; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.*; |
| | | import static org.opends.messages.BackendMessages.*; |
| | | |
| | | import static org.opends.server.util.ServerConstants.*; |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | // If the requested entry was null, then return undefined. |
| | | if (entryDN == null) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | // If the requested entry was the backend base entry, then return |
| | | // the number of backup directories. |
| | | if (backupBaseDN.equals(entryDN)) |
| | | { |
| | | long count = 0; |
| | | for (File f : backupDirectories) |
| | | { |
| | | // Check to see if the descriptor file exists. If not, then skip this |
| | | // backup directory. |
| | | File descriptorFile = new File(f, BACKUP_DIRECTORY_DESCRIPTOR_FILE); |
| | | if (! descriptorFile.exists()) |
| | | { |
| | | continue; |
| | | } |
| | | count ++; |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | // See if the requested entry was one level below the backend base entry. |
| | | // If so, then it must point to a backup directory. Otherwise, it must be |
| | | // two levels below the backup base entry and must point to a specific |
| | | // backup. |
| | | DN parentDN = entryDN.getParentDNInSuffix(); |
| | | if (parentDN == null) |
| | | { |
| | | return -1; |
| | | } |
| | | else if (backupBaseDN.equals(parentDN)) |
| | | { |
| | | long count = 0; |
| | | Entry backupDirEntry = getBackupDirectoryEntry(entryDN); |
| | | |
| | | AttributeType t = |
| | | DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); |
| | | List<Attribute> attrList = backupDirEntry.getAttribute(t); |
| | | if ((attrList != null) && (! attrList.isEmpty())) |
| | | { |
| | | for (AttributeValue v : attrList.get(0).getValues()) |
| | | { |
| | | try |
| | | { |
| | | BackupDirectory backupDirectory = |
| | | BackupDirectory.readBackupDirectoryDescriptor( |
| | | v.getStringValue()); |
| | | count += backupDirectory.getBackups().keySet().size(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return -1; |
| | | } |
| | | } |
| | | } |
| | | return count; |
| | | } |
| | | else if (backupBaseDN.equals(parentDN.getParentDNInSuffix())) |
| | | { |
| | | return 0; |
| | | } |
| | | else |
| | | { |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested entry from this backend. |
| | |
| | | import org.opends.messages.Message; |
| | | |
| | | |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedList; |
| | | import java.util.*; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.config.ConfigException; |
| | |
| | | import org.opends.server.core.ModifyOperation; |
| | | import org.opends.server.core.ModifyDNOperation; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.Control; |
| | | 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.LDIFExportConfig; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.LDIFImportResult; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.util.LDIFException; |
| | | import org.opends.server.util.LDIFReader; |
| | | import org.opends.server.util.LDIFWriter; |
| | |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.*; |
| | | import static org.opends.messages.BackendMessages.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | // Try to look up the immediate children for the DN |
| | | Set<DN> children = childDNs.get(entryDN); |
| | | if (children == null) |
| | | { |
| | | if(entryMap.get(entryDN) != null) |
| | | { |
| | | // The entry does exist but just no children. |
| | | return 0; |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | return children.size(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | |
| | | import org.opends.server.core.ModifyDNOperation; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | 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.LDIFExportConfig; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.LDIFImportResult; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.RDN; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.util.DynamicConstants; |
| | | import org.opends.server.util.LDIFWriter; |
| | | import org.opends.server.util.TimeThread; |
| | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.*; |
| | | import static org.opends.messages.BackendMessages.*; |
| | | import static org.opends.messages.ConfigMessages.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | // If the requested entry was null, then return undefined. |
| | | if (entryDN == null) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | |
| | | // If the requested entry was the monitor base entry, then return |
| | | // the number of monitor providers. |
| | | if (entryDN.equals(baseMonitorDN)) |
| | | { |
| | | return DirectoryServer.getMonitorProviders().size(); |
| | | } |
| | | |
| | | |
| | | // See if the monitor base entry is the immediate parent for the requested |
| | | // entry. If not, then its undefined. |
| | | DN parentDN = entryDN.getParentDNInSuffix(); |
| | | if ((parentDN == null) || (! parentDN.equals(baseMonitorDN))) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | |
| | | // Get the RDN for the requested DN and make sure it is single-valued. |
| | | RDN entryRDN = entryDN.getRDN(); |
| | | if (entryRDN.isMultiValued()) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | |
| | | // Get the RDN value and see if it matches the instance name for one of |
| | | // the directory server monitor providers. |
| | | String rdnValue = entryRDN.getAttributeValue(0).getStringValue(); |
| | | MonitorProvider<? extends MonitorProviderCfg> monitorProvider = |
| | | DirectoryServer.getMonitorProvider(rdnValue.toLowerCase()); |
| | | if (monitorProvider == null) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested entry from this backend. |
| | |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.LDIFExportConfig; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.LDIFImportResult; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.LDIFWriter; |
| | | import org.opends.server.util.Validator; |
| | | |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | if (entryDN == null || ! entryDN.isNullDN()) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | long count = 0; |
| | | |
| | | Map<DN,Backend> baseMap; |
| | | if (subordinateBaseDNs == null) |
| | | { |
| | | baseMap = DirectoryServer.getPublicNamingContexts(); |
| | | } |
| | | else |
| | | { |
| | | baseMap = subordinateBaseDNs; |
| | | } |
| | | |
| | | for (DN subBase : baseMap.keySet()) |
| | | { |
| | | |
| | | Backend b = baseMap.get(subBase); |
| | | Entry subBaseEntry = b.getEntry(subBase); |
| | | if (subBaseEntry != null) |
| | | { |
| | | count++; |
| | | } |
| | | } |
| | | |
| | | return count; |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested entry from this backend. |
| | |
| | | import org.opends.server.schema.MatchingRuleUseSyntax; |
| | | import org.opends.server.schema.NameFormSyntax; |
| | | import org.opends.server.schema.ObjectClassSyntax; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.BackupInfo; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.CryptoManager; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DITContentRule; |
| | | import org.opends.server.types.DITStructureRule; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.ExistingFileBehavior; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.LDIFExportConfig; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.LDIFImportResult; |
| | | import org.opends.server.types.MatchingRuleUse; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.ModificationType; |
| | | import org.opends.server.types.NameForm; |
| | | import org.opends.server.types.ObjectClass; |
| | | import org.opends.server.types.ObjectClassType; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.RDN; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.Schema; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.util.DynamicConstants; |
| | | import org.opends.server.util.LDIFException; |
| | | import org.opends.server.util.LDIFWriter; |
| | | import org.opends.server.util.Validator; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.*; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | boolean found = false; |
| | | |
| | | for (DN dn : baseDNs) |
| | | { |
| | | if (dn.equals(entryDN)) |
| | | { |
| | | found = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (! found) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested entry from this backend. |
| | |
| | | EnvironmentConfig envConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | |
| | | initializeRootContainer(envConfig); |
| | | rootContainer = initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | // Preload the database cache. |
| | |
| | | return -1; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | if(ec == null) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | readerBegin(); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | long count = ec.getNumSubordinates(entryDN); |
| | | if(count == Long.MAX_VALUE) |
| | | { |
| | | // The index entry limit has exceeded and there is no count maintained. |
| | | return -1; |
| | | } |
| | | return count; |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw createDirectoryException(e); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | readerEnd(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested entry from this backend. Note that the caller must |
| | |
| | | envConfig.setConfigParam("je.env.isLocking", "true"); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", "true"); |
| | | |
| | | initializeRootContainer(envConfig); |
| | | rootContainer = initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | |
| | |
| | | envConfig.setConfigParam("je.env.runCheckpointer", "false"); |
| | | } |
| | | |
| | | initializeRootContainer(envConfig); |
| | | rootContainer = initializeRootContainer(envConfig); |
| | | |
| | | ImportJob importJob = new ImportJob(importConfig); |
| | | return importJob.importLDIF(rootContainer); |
| | |
| | | envConfig.setConfigParam("je.env.isLocking", "true"); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", "true"); |
| | | |
| | | initializeRootContainer(envConfig); |
| | | rootContainer = initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(verifyConfig); |
| | |
| | | EnvironmentConfig envConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | |
| | | initializeRootContainer(envConfig); |
| | | rootContainer = initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | RebuildJob rebuildJob = new RebuildJob(rebuildConfig); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns a new read-only handle to the JE root container for this backend. |
| | | * The caller is responsible for closing the root container after use. |
| | | * |
| | | * @return The read-only RootContainer object for this backend. |
| | | * |
| | | * @throws ConfigException If an unrecoverable problem arises during |
| | | * initialization. |
| | | * @throws InitializationException If a problem occurs during initialization |
| | | * that is not related to the server |
| | | * configuration. |
| | | */ |
| | | public RootContainer getReadOnlyRootContainer() |
| | | throws ConfigException, InitializationException |
| | | { |
| | | EnvironmentConfig envConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | |
| | | envConfig.setReadOnly(true); |
| | | envConfig.setAllowCreate(false); |
| | | envConfig.setTransactional(false); |
| | | envConfig.setTxnNoSync(false); |
| | | envConfig.setConfigParam("je.env.isLocking", "true"); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", "true"); |
| | | |
| | | return initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | /** |
| | | * Clears all the entries from the backend. This method is for test cases |
| | | * that use the JE backend. |
| | | * |
| | |
| | | return cfg.dn(); |
| | | } |
| | | |
| | | private void initializeRootContainer(EnvironmentConfig envConfig) |
| | | private RootContainer initializeRootContainer(EnvironmentConfig envConfig) |
| | | throws ConfigException, InitializationException |
| | | { |
| | | // Open the database environment |
| | | try |
| | | { |
| | | rootContainer = new RootContainer(this, cfg); |
| | | rootContainer.open(envConfig); |
| | | RootContainer rc = new RootContainer(this, cfg); |
| | | rc.open(envConfig); |
| | | return rc; |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | |
| | | { |
| | | if (entryLimit > 0 && entryIDList.size() >= entryLimit) |
| | | { |
| | | bufferedValue.value = new EntryIDSet(); |
| | | entryIDList = new EntryIDSet(entryIDList.size()); |
| | | entryIDList.add(entryID); |
| | | bufferedValue.value = entryIDList; |
| | | bufferedValue.isDirty = true; |
| | | return; |
| | | } |
| | | else |
| | | { |
| | | bufferedValue.isDirty = entryIDList.add(entryID); |
| | | } |
| | | |
| | | } |
| | | bufferedValue.isDirty = entryIDList.add(entryID); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Determine the number of subordinate entries for a given entry. |
| | | * |
| | | * @param entryDN The distinguished name of the entry. |
| | | * @return The number of subordinate entries for the given entry or -1 if |
| | | * the entry does not exist. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public long getNumSubordinates(DN entryDN) throws DatabaseException |
| | | { |
| | | EntryID entryID = dn2id.get(null, entryDN); |
| | | if (entryID != null) |
| | | { |
| | | DatabaseEntry key = |
| | | new DatabaseEntry(JebFormat.entryIDToDatabase(entryID.longValue())); |
| | | EntryIDSet entryIDSet = |
| | | id2children.readKey(key, null, LockMode.DEFAULT); |
| | | long count = entryIDSet.size(); |
| | | if(count != Long.MAX_VALUE) |
| | | { |
| | | return count; |
| | | } |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | /** |
| | | * Processes the specified search in this entryContainer. |
| | | * Matching entries should be provided back to the core server using the |
| | | * <CODE>SearchOperation.returnEntry</CODE> method. |
| | |
| | | private long[] values = null; |
| | | |
| | | /** |
| | | * The size of the set when it is not defined. This value is only maintained |
| | | * when the set is undefined. |
| | | */ |
| | | private long undefinedSize = Long.MAX_VALUE; |
| | | |
| | | /** |
| | | * The database key containing this set, if the set was constructed |
| | | * directly from the database. |
| | | */ |
| | |
| | | public EntryIDSet() |
| | | { |
| | | values = null; |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } |
| | | |
| | | /** |
| | | * Create a new undefined set with a initial size. |
| | | * |
| | | * @param size The undefined size for this set. |
| | | */ |
| | | public EntryIDSet(long size) |
| | | { |
| | | values = null; |
| | | undefinedSize = size; |
| | | } |
| | | |
| | | /** |
| | |
| | | return; |
| | | } |
| | | |
| | | values = JebFormat.entryIDListFromDatabase(bytes); |
| | | if (bytes.length == 0) |
| | | { |
| | | // Entry limit has exceeded and there is no encoded undefined set size. |
| | | values = null; |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } |
| | | else if ((bytes[0] & 0x80) == 0x80) |
| | | { |
| | | // Entry limit has exceeded and there is an encoded undefined set size. |
| | | values = null; |
| | | undefinedSize = JebFormat.entryIDUndefinedSizeFromDatabase(bytes); |
| | | } |
| | | else |
| | | { |
| | | // Seems like entry limit has not been exceeded and the bytes is a |
| | | // list of entry IDs. |
| | | values = JebFormat.entryIDListFromDatabase(bytes); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | boolean needSort = false; |
| | | int count = 0; |
| | | |
| | | boolean undefined = false; |
| | | for (EntryIDSet l : sets) |
| | | { |
| | | if (!l.isDefined()) |
| | | { |
| | | return new EntryIDSet(); |
| | | if(l.undefinedSize == Long.MAX_VALUE) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | | else |
| | | { |
| | | undefined = true; |
| | | } |
| | | } |
| | | count += l.size(); |
| | | } |
| | | |
| | | if(undefined) |
| | | { |
| | | return new EntryIDSet(count); |
| | | } |
| | | |
| | | long[] n = new long[count]; |
| | | int pos = 0; |
| | | for (EntryIDSet l : sets) |
| | |
| | | * |
| | | * @return The number of IDs in the set. |
| | | */ |
| | | public int size() |
| | | public long size() |
| | | { |
| | | if (values == null) |
| | | { |
| | | return Integer.MAX_VALUE; |
| | | return undefinedSize; |
| | | } |
| | | else |
| | | { |
| | |
| | | if (keyBytes != null) |
| | | { |
| | | // The index entry limit was exceeded |
| | | buffer.append("[LIMIT-EXCEEDED]"); |
| | | if(undefinedSize == Long.MAX_VALUE) |
| | | { |
| | | buffer.append("[LIMIT-EXCEEDED]"); |
| | | } |
| | | else |
| | | { |
| | | buffer.append("[LIMIT-EXCEEDED:"); |
| | | buffer.append(undefinedSize); |
| | | buffer.append("]"); |
| | | } |
| | | } |
| | | else |
| | | { |
| | |
| | | */ |
| | | public byte[] toDatabase() |
| | | { |
| | | return JebFormat.entryIDListToDatabase(values); |
| | | if(isDefined()) |
| | | { |
| | | return JebFormat.entryIDListToDatabase(values); |
| | | } |
| | | else |
| | | { |
| | | return JebFormat.entryIDUndefinedSizeToDatabase(undefinedSize); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | if (values == null) |
| | | { |
| | | return false; |
| | | if(undefinedSize != Long.MAX_VALUE) |
| | | { |
| | | undefinedSize++; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | long id = entryID.longValue(); |
| | |
| | | { |
| | | if (values == null) |
| | | { |
| | | return false; |
| | | if(undefinedSize != Long.MAX_VALUE) |
| | | { |
| | | undefinedSize--; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | if (values.length == 0) |
| | |
| | | if (!this.isDefined()) |
| | | { |
| | | this.values = that.values; |
| | | this.undefinedSize = that.undefinedSize; |
| | | return; |
| | | } |
| | | |
| | |
| | | { |
| | | if (!this.isDefined()) |
| | | { |
| | | // Can't simply add the undefined size of this set to that set since |
| | | // we don't know if there are any duplicates. In this case, we can't |
| | | // maintain the undefined size anymore. |
| | | if(undefinedSize != Long.MAX_VALUE && that.size() > 0) |
| | | { |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } |
| | | return; |
| | | } |
| | | |
| | | if (!that.isDefined()) |
| | | { |
| | | if(that.size() == 0) |
| | | { |
| | | undefinedSize = values.length; |
| | | } |
| | | else |
| | | { |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } |
| | | values = null; |
| | | return; |
| | | } |
| | |
| | | { |
| | | if (!this.isDefined()) |
| | | { |
| | | // Can't simply subtract the undefined size of this set to that set since |
| | | // we don't know if there are any duplicates. In this case, we can't |
| | | // maintain the undefined size anymore. |
| | | if(undefinedSize != Long.MAX_VALUE && that.size() > 0) |
| | | { |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } |
| | | return; |
| | | } |
| | | |
| | | if (!that.isDefined()) |
| | | { |
| | | if(that.size() == 0) |
| | | { |
| | | undefinedSize = values.length; |
| | | } |
| | | else |
| | | { |
| | | undefinedSize = Long.MAX_VALUE; |
| | | } |
| | | values = null; |
| | | return; |
| | | } |
| | |
| | | { |
| | | if (indexEntryLimit > 0 && entryIDList.size() >= indexEntryLimit) |
| | | { |
| | | entryIDList = new EntryIDSet(); |
| | | entryIDList = new EntryIDSet(entryIDList.size()); |
| | | entryLimitExceededCount++; |
| | | |
| | | if(debugEnabled()) |
| | |
| | | |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if(!entryIDList.add(entryID)) |
| | | { |
| | | success = false; |
| | | } |
| | | } |
| | | |
| | | byte[] after = entryIDList.toDatabase(); |
| | | data.setData(after); |
| | | put(txn, key, data); |
| | | } |
| | | |
| | | success = entryIDList.add(entryID); |
| | | |
| | | byte[] after = entryIDList.toDatabase(); |
| | | data.setData(after); |
| | | put(txn, key, data); |
| | | } |
| | | else |
| | | { |
| | |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void removeID(Transaction txn, DatabaseEntry key, EntryID entryID) |
| | | throws DatabaseException |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status; |
| | | LockMode lockMode = LockMode.RMW; |
| | |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | | EntryIDSet entryIDList = new EntryIDSet(key.getData(), data.getData()); |
| | | if (entryIDList.isDefined()) |
| | | // Ignore failures if rebuild is running since the entry ID is |
| | | // probably already removed. |
| | | if (!entryIDList.remove(entryID) && !rebuildRunning) |
| | | { |
| | | // Ignore failures if rebuild is running since the entry ID is |
| | | // probably already removed. |
| | | if (!entryIDList.remove(entryID) && !rebuildRunning) |
| | | if(trusted) |
| | | { |
| | | // Invalidate the key by setting it undefined |
| | | byte[] after = new EntryIDSet().toDatabase(); |
| | | data.setData(after); |
| | | put(txn, key, data); |
| | | setTrusted(txn, false); |
| | | |
| | | if(trusted) |
| | | |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | setTrusted(txn, false); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); |
| | | TRACER.debugError("The expected entry ID does not exist in " + |
| | | "the entry ID list for index %s.\nKey:%s", |
| | | name, builder.toString()); |
| | | } |
| | | |
| | | logError(ERR_JEB_INDEX_CORRUPT_REQUIRES_REBUILD.get(name)); |
| | | StringBuilder builder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); |
| | | TRACER.debugError("The expected entry ID does not exist in " + |
| | | "the entry ID list for index %s.\nKey:%s", |
| | | name, builder.toString()); |
| | | } |
| | | |
| | | logError(ERR_JEB_INDEX_CORRUPT_REQUIRES_REBUILD.get(name)); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | byte[] after = entryIDList.toDatabase(); |
| | | if (after == null) |
| | | { |
| | | // No more IDs, so remove the key. If index is not |
| | | // trusted then this will cause all subsequent reads |
| | | // for this key to return undefined set. |
| | | delete(txn, key); |
| | | } |
| | | else |
| | | { |
| | | byte[] after = entryIDList.toDatabase(); |
| | | if (after == null) |
| | | { |
| | | // No more IDs, so remove the key. If index is not |
| | | // trusted then this will cause all subsequent reads |
| | | // for this key to return undefined set. |
| | | delete(txn, key); |
| | | } |
| | | else |
| | | { |
| | | data.setData(after); |
| | | put(txn, key, data); |
| | | } |
| | | data.setData(after); |
| | | put(txn, key, data); |
| | | } |
| | | } |
| | | } |
| | |
| | | if (index.read(txn, dbKey, dbData, LockMode.RMW) == |
| | | OperationStatus.SUCCESS) |
| | | { |
| | | if (dbData.getSize() == 0) |
| | | if (dbData.getSize() == 8 && |
| | | (dbData.getData()[0] & 0x80) == 0x80) |
| | | { |
| | | // Entry limit already exceeded. |
| | | // Entry limit already exceeded. Just update the |
| | | // undefined size assuming no overlap will occur between |
| | | // the add values and the longs in the DB. |
| | | long undefinedSize = |
| | | JebFormat.entryIDUndefinedSizeFromDatabase(dbData.getData()); |
| | | |
| | | for(Longs l : addValues) |
| | | { |
| | | undefinedSize += l.size(); |
| | | } |
| | | |
| | | if(replaceExisting) |
| | | { |
| | | for(Longs l : delValues) |
| | | { |
| | | undefinedSize -= l.size(); |
| | | } |
| | | } |
| | | |
| | | byte[] undefinedSizeBytes = |
| | | JebFormat.entryIDUndefinedSizeToDatabase(undefinedSize); |
| | | dbData.setData(undefinedSizeBytes); |
| | | index.put(txn, dbKey, dbData); |
| | | break writeMergedValue; |
| | | } |
| | | merged.decode(dbData.getData()); |
| | |
| | | |
| | | if (merged.size() > entryLimit) |
| | | { |
| | | index.writeKey(txn, dbKey, new EntryIDSet()); |
| | | byte[] undefinedSizeBytes = |
| | | JebFormat.entryIDUndefinedSizeToDatabase(merged.size()); |
| | | dbData.setData(undefinedSizeBytes); |
| | | index.put(txn, dbKey, dbData); |
| | | } |
| | | else |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Decode an entry ID count from its database representation. |
| | | * |
| | | * @param bytes The database value of the entry ID count. |
| | | * @return The entry ID count. |
| | | */ |
| | | public static long entryIDUndefinedSizeFromDatabase(byte[] bytes) |
| | | { |
| | | if(bytes == null) |
| | | { |
| | | return 0; |
| | | } |
| | | |
| | | if(bytes.length == 8) |
| | | { |
| | | long v = 0; |
| | | v |= (bytes[0] & 0x7F); |
| | | for (int i = 1; i < 8; i++) |
| | | { |
| | | v <<= 8; |
| | | v |= (bytes[i] & 0xFF); |
| | | } |
| | | return v; |
| | | } |
| | | else |
| | | { |
| | | return Long.MAX_VALUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Decode an array of entry ID values from its database representation. |
| | | * |
| | | * @param bytes The raw database value, null if there is no value and |
| | | * hence no entry IDs. Zero length means the index entry |
| | | * limit has been exceeded. |
| | | * hence no entry IDs. Note that this method will throw an |
| | | * ArrayIndexOutOfBoundsException if the bytes array length is |
| | | * not a multiple of 8. |
| | | * |
| | | * @return An array of entry ID values. |
| | | */ |
| | | public static long[] entryIDListFromDatabase(byte[] bytes) |
| | | { |
| | | if (bytes == null) |
| | | { |
| | | return new long[0]; |
| | | } |
| | | |
| | | if (bytes.length == 0) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | byte[] decodedBytes = bytes; |
| | | |
| | | int count = decodedBytes.length / 8; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Encode an entry ID set count to its database representation. |
| | | * @param count The entry ID set count to be encoded. |
| | | * @return The encoded database value of the entry ID. |
| | | */ |
| | | public static byte[] entryIDUndefinedSizeToDatabase(long count) |
| | | { |
| | | byte[] bytes = new byte[8]; |
| | | long v = count; |
| | | for (int i = 7; i >= 1; i--) |
| | | { |
| | | bytes[i] = (byte) (v & 0xFF); |
| | | v >>>= 8; |
| | | } |
| | | bytes[0] = (byte) ((v | 0x80) & 0xFF); |
| | | return bytes; |
| | | } |
| | | |
| | | /** |
| | | * Encode an array of entry ID values to its database representation. |
| | | * |
| | | * @param entryIDArray An array of entry ID values. A null value indicates |
| | | * that the entry limit is exceeded, and a zero length array indicates no |
| | | * values. |
| | | * @param entryIDArray An array of entry ID values. |
| | | * |
| | | * @return The encoded database value. |
| | | */ |
| | | public static byte[] entryIDListToDatabase(long[] entryIDArray) |
| | | { |
| | | if (entryIDArray == null) |
| | | { |
| | | // index entry limit exceeded |
| | | return new byte[0]; |
| | | } |
| | | |
| | | if (entryIDArray.length == 0) |
| | | { |
| | | // Zero values |
| | |
| | | import org.opends.server.core.ModifyDNOperation; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.CancelledOperationException; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.LDIFExportConfig; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.LDIFImportResult; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.Validator; |
| | | |
| | | import static org.opends.server.config.ConfigConstants.*; |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | if (entryDN == null) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | if (entryDN.equals(taskRootDN)) |
| | | { |
| | | // scheduled and recurring parents. |
| | | return 2; |
| | | } |
| | | else if (entryDN.equals(scheduledTaskParentDN)) |
| | | { |
| | | return taskScheduler.getScheduledTaskCount(); |
| | | } |
| | | else if (entryDN.equals(recurringTaskParentDN)) |
| | | { |
| | | return taskScheduler.getRecurringTaskCount(); |
| | | } |
| | | |
| | | DN parentDN = entryDN.getParentDNInSuffix(); |
| | | if (parentDN == null) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | if (parentDN.equals(scheduledTaskParentDN) && |
| | | taskScheduler.getScheduledTask(entryDN) != null) |
| | | { |
| | | return 0; |
| | | } |
| | | else if (parentDN.equals(recurringTaskParentDN) && |
| | | taskScheduler.getRecurringTask(entryDN) != null) |
| | | { |
| | | return 0; |
| | | } |
| | | else |
| | | { |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested entry from this backend. |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the number of scheduled tasks in the task backend. |
| | | * |
| | | * @return The total number of entries in the task backend. |
| | | */ |
| | | public long getScheduledTaskCount() |
| | | { |
| | | schedulerLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return tasks.size(); |
| | | } |
| | | finally |
| | | { |
| | | schedulerLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the number of recurring tasks in the task backend. |
| | | * |
| | | * @return The total number of entries in the task backend. |
| | | */ |
| | | public long getRecurringTaskCount() |
| | | { |
| | | schedulerLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return recurringTasks.size(); |
| | | } |
| | | finally |
| | | { |
| | | schedulerLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.schema.GeneralizedTimeSyntax; |
| | | import org.opends.server.tools.LDIFModify; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.BackupConfig; |
| | | import org.opends.server.types.BackupDirectory; |
| | | import org.opends.server.types.BackupInfo; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.CryptoManager; |
| | | 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.ExistingFileBehavior; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.LDIFExportConfig; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.types.LDIFImportResult; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.Privilege; |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.types.RestoreConfig; |
| | | import org.opends.server.types.SearchFilter; |
| | | import org.opends.server.types.SearchScope; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.DynamicConstants; |
| | | import org.opends.server.util.LDIFException; |
| | | import org.opends.server.util.LDIFReader; |
| | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | long ret = numSubordinates(entryDN); |
| | | if(ret < 0) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | else if(ret == 0) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.TRUE; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public long numSubordinates(DN entryDN) throws DirectoryException |
| | | { |
| | | ConfigEntry baseEntry = configEntries.get(entryDN); |
| | | if (baseEntry == null) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | return baseEntry.getChildren().size(); |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested entry from this backend. |
| New file |
| | |
| | | /* |
| | | * 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 org.opends.server.admin.std.server.VirtualAttributeCfg; |
| | | import org.opends.server.api.VirtualAttributeProvider; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.messages.Message; |
| | | import static org.opends.messages.ExtensionMessages.*; |
| | | |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * This class implements a virtual attribute provider that is meant to serve the |
| | | * hasSubordinates operational attribute as described in X.501. |
| | | */ |
| | | public class HasSubordinatesVirtualAttributeProvider |
| | | extends VirtualAttributeProvider<VirtualAttributeCfg> |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * Creates a new instance of this HasSubordinates virtual attribute provider. |
| | | */ |
| | | public HasSubordinatesVirtualAttributeProvider() |
| | | { |
| | | 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); |
| | | |
| | | Backend backend = DirectoryServer.getBackend(entry.getDN()); |
| | | |
| | | try |
| | | { |
| | | ConditionResult ret = backend.hasSubordinates(entry.getDN()); |
| | | if(ret != null && ret != ConditionResult.UNDEFINED) |
| | | { |
| | | AttributeValue value = |
| | | new AttributeValue(ByteStringFactory.create(ret.toString()), |
| | | ByteStringFactory.create(ret.toString())); |
| | | values.add(value); |
| | | } |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | } |
| | | |
| | | return values; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean hasValue(Entry entry, VirtualAttributeRule rule) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(entry.getDN()); |
| | | |
| | | try |
| | | { |
| | | ConditionResult ret = backend.hasSubordinates(entry.getDN()); |
| | | return ret != null && ret != ConditionResult.UNDEFINED; |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean hasValue(Entry entry, VirtualAttributeRule rule, |
| | | AttributeValue value) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(entry.getDN()); |
| | | |
| | | try |
| | | { |
| | | ConditionResult ret = backend.hasSubordinates(entry.getDN()); |
| | | if(ret != null && ret != ConditionResult.UNDEFINED) |
| | | { |
| | | return ConditionResult.valueOf(value.getNormalizedStringValue()). |
| | | equals(ret); |
| | | } |
| | | return false; |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public ConditionResult matchesSubstring(Entry entry, |
| | | VirtualAttributeRule rule, |
| | | ByteString subInitial, |
| | | List<ByteString> subAny, |
| | | ByteString subFinal) |
| | | { |
| | | // This virtual attribute does not support substring matching. |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public ConditionResult greaterThanOrEqualTo(Entry entry, |
| | | VirtualAttributeRule rule, |
| | | AttributeValue value) |
| | | { |
| | | // This virtual attribute does not support ordering matching. |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public ConditionResult lessThanOrEqualTo(Entry entry, |
| | | VirtualAttributeRule rule, |
| | | AttributeValue value) |
| | | { |
| | | // This virtual attribute does not support ordering matching. |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public ConditionResult approximatelyEqualTo(Entry entry, |
| | | VirtualAttributeRule rule, |
| | | AttributeValue value) |
| | | { |
| | | // This virtual attribute does not support 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 false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void processSearch(VirtualAttributeRule rule, |
| | | SearchOperation searchOperation) |
| | | { |
| | | searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | Message message = ERR_HASSUBORDINATES_VATTR_NOT_SEARCHABLE.get( |
| | | rule.getAttributeType().getNameOrOID()); |
| | | searchOperation.appendErrorMessage(message); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 org.opends.server.admin.std.server.VirtualAttributeCfg; |
| | | import org.opends.server.api.VirtualAttributeProvider; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.messages.Message; |
| | | import static org.opends.messages.ExtensionMessages.*; |
| | | |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * This class implements a virtual attribute provider that is meant to serve the |
| | | * hasSubordinates operational attribute as described in |
| | | * draft-ietf-boreham-numsubordinates. |
| | | */ |
| | | public class NumSubordinatesVirtualAttributeProvider |
| | | extends VirtualAttributeProvider<VirtualAttributeCfg> |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * Creates a new instance of this NumSubordinates virtual attribute provider. |
| | | */ |
| | | public NumSubordinatesVirtualAttributeProvider() |
| | | { |
| | | 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); |
| | | |
| | | Backend backend = DirectoryServer.getBackend(entry.getDN()); |
| | | |
| | | try |
| | | { |
| | | long count = backend.numSubordinates(entry.getDN()); |
| | | if(count >= 0) |
| | | { |
| | | AttributeValue value = |
| | | new AttributeValue(ByteStringFactory.create(String.valueOf(count)), |
| | | ByteStringFactory.create(String.valueOf(count))); |
| | | values.add(value); |
| | | } |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | } |
| | | |
| | | return values; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean hasValue(Entry entry, VirtualAttributeRule rule) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(entry.getDN()); |
| | | |
| | | try |
| | | { |
| | | return backend.numSubordinates(entry.getDN()) >= 0; |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public boolean hasValue(Entry entry, VirtualAttributeRule rule, |
| | | AttributeValue value) |
| | | { |
| | | Backend backend = DirectoryServer.getBackend(entry.getDN()); |
| | | |
| | | try |
| | | { |
| | | long count = backend.numSubordinates(entry.getDN()); |
| | | if(count >= 0) |
| | | { |
| | | return Long.parseLong(value.getNormalizedStringValue()) == count; |
| | | } |
| | | return false; |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public ConditionResult matchesSubstring(Entry entry, |
| | | VirtualAttributeRule rule, |
| | | ByteString subInitial, |
| | | List<ByteString> subAny, |
| | | ByteString subFinal) |
| | | { |
| | | // This virtual attribute does not support substring matching. |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public ConditionResult approximatelyEqualTo(Entry entry, |
| | | VirtualAttributeRule rule, |
| | | AttributeValue value) |
| | | { |
| | | // This virtual attribute does not support 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 false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public void processSearch(VirtualAttributeRule rule, |
| | | SearchOperation searchOperation) |
| | | { |
| | | searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM); |
| | | |
| | | Message message = ERR_NUMSUBORDINATES_VATTR_NOT_SEARCHABLE.get( |
| | | rule.getAttributeType().getNameOrOID()); |
| | | searchOperation.appendErrorMessage(message); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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.tools; |
| | | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | import org.opends.server.util.args.*; |
| | | import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH; |
| | | import org.opends.server.util.StaticUtils; |
| | | import org.opends.server.util.table.TableBuilder; |
| | | import org.opends.server.util.table.TextTablePrinter; |
| | | import org.opends.server.types.*; |
| | | import static org.opends.messages.ToolMessages.*; |
| | | import org.opends.messages.Message; |
| | | import static org.opends.server.tools.ToolConstants.OPTION_SHORT_CONFIG_CLASS; |
| | | import static org.opends.server.tools.ToolConstants.OPTION_LONG_CONFIG_CLASS; |
| | | import static org.opends.server.tools.ToolConstants.OPTION_VALUE_CONFIG_CLASS; |
| | | import static org.opends.server.tools.ToolConstants.OPTION_SHORT_HELP; |
| | | import static org.opends.server.tools.ToolConstants.OPTION_LONG_HELP; |
| | | import org.opends.server.extensions.ConfigFileHandler; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.core.LockFileManager; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.admin.std.server.BackendCfg; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | import org.opends.server.backends.jeb.*; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.asn1.ASN1Element; |
| | | |
| | | import java.io.*; |
| | | import java.util.*; |
| | | |
| | | import com.sleepycat.je.*; |
| | | |
| | | /** |
| | | * This program provides a utility that may be used to debug a JE backend. This |
| | | * tool provides the ability list various containers in the backend as well as |
| | | * dump the contents of database containers. This will be |
| | | * a process that is intended to run separate from Directory Server and not |
| | | * internally within the server process (e.g., via the tasks interface). |
| | | */ |
| | | public class DBTest |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | // The error stream which this application should use. |
| | | private final PrintStream err; |
| | | |
| | | // The output stream which this application should use. |
| | | private final PrintStream out; |
| | | |
| | | // Flag indicating whether or not the global arguments have |
| | | // already been initialized. |
| | | private boolean globalArgumentsInitialized = false; |
| | | |
| | | // The command-line argument parser. |
| | | private final SubCommandArgumentParser parser; |
| | | |
| | | // The argument which should be used to request usage information. |
| | | private BooleanArgument showUsageArgument; |
| | | |
| | | // The argument which should be used to specify the config class. |
| | | private StringArgument configClass; |
| | | |
| | | // THe argument which should be used to specify the config file. |
| | | private StringArgument configFile; |
| | | |
| | | // Flag indicating whether or not the sub-commands have |
| | | // already been initialized. |
| | | private boolean subCommandsInitialized = false; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Provides the command-line arguments to the main application for |
| | | * processing. |
| | | * |
| | | * @param args |
| | | * The set of command-line arguments provided to this |
| | | * program. |
| | | */ |
| | | public static void main(String[] args) { |
| | | int exitCode = main(args, true, System.out, System.err); |
| | | if (exitCode != 0) { |
| | | System.exit(filterExitCode(exitCode)); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Provides the command-line arguments to the main application for |
| | | * processing and returns the exit code as an integer. |
| | | * |
| | | * @param args |
| | | * The set of command-line arguments provided to this |
| | | * program. |
| | | * @param initializeServer |
| | | * Indicates whether to perform basic initialization (which |
| | | * should not be done if the tool is running in the same |
| | | * JVM as the server). |
| | | * @param outStream |
| | | * The output stream for standard output. |
| | | * @param errStream |
| | | * The output stream for standard error. |
| | | * @return Zero to indicate that the program completed successfully, |
| | | * or non-zero to indicate that an error occurred. |
| | | */ |
| | | public static int main(String[] args, boolean initializeServer, |
| | | OutputStream outStream, OutputStream errStream) { |
| | | DBTest app = new DBTest(outStream, errStream); |
| | | |
| | | // Run the application. |
| | | return app.run(args, initializeServer); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new dsconfig application instance. |
| | | * |
| | | * @param out |
| | | * The application output stream. |
| | | * @param err |
| | | * The application error stream. |
| | | */ |
| | | public DBTest(OutputStream out, OutputStream err) |
| | | { |
| | | if (out != null) { |
| | | this.out = new PrintStream(out); |
| | | } else { |
| | | this.out = NullOutputStream.printStream(); |
| | | } |
| | | |
| | | if (err != null) { |
| | | this.err = new PrintStream(err); |
| | | } else { |
| | | this.err = NullOutputStream.printStream(); |
| | | } |
| | | |
| | | Message toolDescription = INFO_DESCRIPTION_DBTEST_TOOL.get(); |
| | | this.parser = new SubCommandArgumentParser(this.getClass().getName(), |
| | | toolDescription, false); |
| | | } |
| | | |
| | | // Displays the provided message followed by a help usage reference. |
| | | private void displayMessageAndUsageReference(Message message) { |
| | | printMessage(message); |
| | | printMessage(Message.EMPTY); |
| | | printMessage(parser.getHelpUsageReference()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Registers the global arguments with the argument parser. |
| | | * |
| | | * @throws ArgumentException |
| | | * If a global argument could not be registered. |
| | | */ |
| | | private void initializeGlobalArguments() throws ArgumentException { |
| | | if (!globalArgumentsInitialized) { |
| | | configClass = |
| | | new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS, |
| | | OPTION_LONG_CONFIG_CLASS, true, false, |
| | | true, OPTION_VALUE_CONFIG_CLASS, |
| | | ConfigFileHandler.class.getName(), null, |
| | | INFO_DESCRIPTION_CONFIG_CLASS.get()); |
| | | configClass.setHidden(true); |
| | | |
| | | configFile = |
| | | new StringArgument("configfile", 'f', "configFile", true, false, |
| | | true, "{configFile}", null, null, |
| | | INFO_DESCRIPTION_CONFIG_FILE.get()); |
| | | configFile.setHidden(true); |
| | | |
| | | |
| | | showUsageArgument = |
| | | new BooleanArgument("help", OPTION_SHORT_HELP, OPTION_LONG_HELP, |
| | | INFO_DESCRIPTION_USAGE.get()); |
| | | |
| | | // Register the global arguments. |
| | | parser.addGlobalArgument(showUsageArgument); |
| | | parser.setUsageArgument(showUsageArgument, out); |
| | | parser.addGlobalArgument(configClass); |
| | | parser.addGlobalArgument(configFile); |
| | | |
| | | globalArgumentsInitialized = true; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Registers the sub-commands with the argument parser. |
| | | * |
| | | * @throws ArgumentException |
| | | * If a sub-command could not be created. |
| | | */ |
| | | private void initializeSubCommands() throws ArgumentException { |
| | | if (!subCommandsInitialized) { |
| | | StringArgument backendID; |
| | | StringArgument baseDN; |
| | | StringArgument databaseName; |
| | | BooleanArgument skipDecode; |
| | | StringArgument maxKeyValue; |
| | | StringArgument minKeyValue; |
| | | IntegerArgument maxDataSize; |
| | | IntegerArgument minDataSize; |
| | | SubCommand sub; |
| | | |
| | | sub = new SubCommand(parser, "list-root-containers", |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ROOT_CONTAINERS.get()); |
| | | |
| | | |
| | | sub = new SubCommand(parser, "list-entry-containers", |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ENTRY_CONTAINERS.get()); |
| | | backendID = |
| | | new StringArgument("backendid", 'n', "backendID", true, false, true, |
| | | "{backendID}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_BACKEND_ID.get()); |
| | | sub.addArgument(backendID); |
| | | |
| | | |
| | | sub = new SubCommand(parser, "list-database-containers", |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_DATABASE_CONTAINERS.get()); |
| | | backendID = |
| | | new StringArgument("backendid", 'n', "backendID", true, false, true, |
| | | "{backendID}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_BACKEND_ID.get()); |
| | | sub.addArgument(backendID); |
| | | baseDN = |
| | | new StringArgument("basedn", 'b', "baseDN", false, |
| | | false, true, "{baseDN}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_BASE_DN.get()); |
| | | sub.addArgument(baseDN); |
| | | |
| | | |
| | | sub = new SubCommand(parser, "dump-database-container", |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_DUMP_DATABASE_CONTAINER.get()); |
| | | backendID = |
| | | new StringArgument("backendid", 'n', "backendID", true, false, true, |
| | | "{backendID}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_BACKEND_ID.get()); |
| | | sub.addArgument(backendID); |
| | | baseDN = |
| | | new StringArgument("basedn", 'b', "baseDN", true, |
| | | false, true, "{baseDN}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_BASE_DN.get()); |
| | | sub.addArgument(baseDN); |
| | | databaseName = |
| | | new StringArgument("databasename", 'd', "databaseName", true, |
| | | false, true, "{databaseName}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_DATABASE_NAME.get()); |
| | | sub.addArgument(databaseName); |
| | | skipDecode = |
| | | new BooleanArgument("skipdecode", 'p', "skipDecode", |
| | | INFO_DESCRIPTION_DBTEST_SKIP_DECODE.get()); |
| | | sub.addArgument(skipDecode); |
| | | maxKeyValue = new StringArgument("maxkeyvalue", 'K', "maxKeyValue", false, |
| | | false, true, "{maxKeyValue}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_MAX_KEY_VALUE.get()); |
| | | sub.addArgument(maxKeyValue); |
| | | minKeyValue = new StringArgument("minkeyvalue", 'k', "minKeyValue", false, |
| | | false, true, "{minKeyValue}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_MIN_KEY_VALUE.get()); |
| | | sub.addArgument(minKeyValue); |
| | | maxDataSize = new IntegerArgument("maxdatasize", 'S', "maxDataSize", |
| | | false, false, true, "{maxDataSize}", -1, |
| | | null, |
| | | INFO_DESCRIPTION_DBTEST_MAX_DATA_SIZE.get()); |
| | | sub.addArgument(maxDataSize); |
| | | minDataSize = new IntegerArgument("mindatasize", 's', "minDataSize", |
| | | false, false, true, "{minDataSize}", -1, |
| | | null, |
| | | INFO_DESCRIPTION_DBTEST_MIN_DATA_SIZE.get()); |
| | | sub.addArgument(minDataSize); |
| | | |
| | | |
| | | sub = new SubCommand(parser, "list-index-status", |
| | | INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_INDEX_STATUS.get()); |
| | | backendID = |
| | | new StringArgument("backendid", 'n', "backendID", true, false, true, |
| | | "{backendID}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_BACKEND_ID.get()); |
| | | sub.addArgument(backendID); |
| | | baseDN = |
| | | new StringArgument("basedn", 'b', "baseDN", true, |
| | | true, true, "{baseDN}", null, null, |
| | | INFO_DESCRIPTION_DBTEST_BASE_DN.get()); |
| | | sub.addArgument(baseDN); |
| | | |
| | | subCommandsInitialized = true; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Parses the provided command-line arguments and makes the |
| | | * appropriate changes to the Directory Server configuration. |
| | | * |
| | | * @param args |
| | | * The command-line arguments provided to this program. |
| | | * @param initializeServer |
| | | * Indicates whether to perform basic initialization (which |
| | | * should not be done if the tool is running in the same |
| | | * JVM as the server). |
| | | * @return The exit code from the configuration processing. A |
| | | * nonzero value indicates that there was some kind of |
| | | * problem during the configuration processing. |
| | | */ |
| | | private int run(String[] args, boolean initializeServer) { |
| | | // Register global arguments and sub-commands. |
| | | try { |
| | | initializeGlobalArguments(); |
| | | initializeSubCommands(); |
| | | } catch (ArgumentException e) { |
| | | Message message = ERR_CANNOT_INITIALIZE_ARGS.get(e.getMessage()); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | |
| | | // Parse the command-line arguments provided to this program. |
| | | try { |
| | | parser.parseArguments(args); |
| | | } catch (ArgumentException ae) { |
| | | Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage()); |
| | | displayMessageAndUsageReference(message); |
| | | return 1; |
| | | } |
| | | |
| | | // If the usage/version argument was provided, then we don't need |
| | | // to do anything else. |
| | | if (parser.usageOrVersionDisplayed()) { |
| | | return 0; |
| | | } |
| | | |
| | | // Only initialize the server when run as a standalone |
| | | // application. |
| | | if (initializeServer) { |
| | | // Perform the initial bootstrap of the Directory Server and process the |
| | | // configuration. |
| | | DirectoryServer directoryServer = DirectoryServer.getInstance(); |
| | | try |
| | | { |
| | | directoryServer.bootstrapClient(); |
| | | directoryServer.initializeJMX(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = ERR_SERVER_BOOTSTRAP_ERROR.get( |
| | | getExceptionMessage(e)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | |
| | | try |
| | | { |
| | | directoryServer.initializeConfiguration(configClass.getValue(), |
| | | configFile.getValue()); |
| | | } |
| | | catch (InitializationException ie) |
| | | { |
| | | Message message = ERR_CANNOT_LOAD_CONFIG.get( |
| | | ie.getMessage()); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = ERR_CANNOT_LOAD_CONFIG.get( |
| | | getExceptionMessage(e)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | |
| | | |
| | | |
| | | // Initialize the Directory Server schema elements. |
| | | try |
| | | { |
| | | directoryServer.initializeSchema(); |
| | | } |
| | | catch (ConfigException ce) |
| | | { |
| | | Message message = ERR_CANNOT_LOAD_SCHEMA.get( |
| | | ce.getMessage()); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | catch (InitializationException ie) |
| | | { |
| | | Message message = ERR_CANNOT_LOAD_SCHEMA.get( |
| | | ie.getMessage()); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = ERR_CANNOT_LOAD_SCHEMA.get( |
| | | getExceptionMessage(e)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | // Make sure that we have a sub-command. |
| | | if (parser.getSubCommand() == null) |
| | | { |
| | | Message message = ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get(); |
| | | displayMessageAndUsageReference(message); |
| | | return 1; |
| | | } |
| | | |
| | | // Retrieve the sub-command implementation and run it. |
| | | SubCommand subCommand = parser.getSubCommand(); |
| | | try { |
| | | if(subCommand.getName().equals("list-root-containers")) |
| | | { |
| | | return listRootContainers(); |
| | | } |
| | | else if(subCommand.getName().equals("list-entry-containers")) |
| | | { |
| | | return listEntryContainers(subCommand.getArgument("backendid")); |
| | | } |
| | | else if(subCommand.getName().equals("list-database-containers")) |
| | | { |
| | | return listDatabaseContainers(subCommand.getArgument("backendid"), |
| | | subCommand.getArgument("basedn")); |
| | | } |
| | | else if(subCommand.getName().equals("dump-database-container")) |
| | | { |
| | | return dumpDatabaseContainer(subCommand.getArgument("backendid"), |
| | | subCommand.getArgument("basedn"), |
| | | subCommand.getArgument("databasename"), |
| | | subCommand.getArgument("skipdecode"), |
| | | subCommand.getArgument("maxkeyvalue"), |
| | | subCommand.getArgument("minkeyvalue"), |
| | | subCommand.getArgument("maxdatasize"), |
| | | subCommand.getArgument("mindatasize")); |
| | | } |
| | | else if(subCommand.getName().equals("list-index-status")) |
| | | { |
| | | return listIndexStatus(subCommand.getArgument("backendid"), |
| | | subCommand.getArgument("basedn")); |
| | | } |
| | | { |
| | | return 0; |
| | | } |
| | | } catch (Exception e) { |
| | | if (debugEnabled()) { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | printMessage(Message.raw(StaticUtils.stackTraceToString(e))); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | private int listRootContainers() |
| | | { |
| | | TreeMap<JEBackendCfg, BackendImpl> jeBackends = getJEBackends(); |
| | | int count = 0; |
| | | |
| | | // Create a table of their properties. |
| | | TableBuilder builder = new TableBuilder(); |
| | | |
| | | builder.appendHeading(INFO_LABEL_DBTEST_BACKEND_ID.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_DB_DIRECTORY.get()); |
| | | |
| | | for(Map.Entry<JEBackendCfg, BackendImpl> backend : jeBackends.entrySet()) |
| | | { |
| | | builder.startRow(); |
| | | builder.appendCell(backend.getValue().getBackendID()); |
| | | builder.appendCell(backend.getKey().getBackendDirectory()); |
| | | count++; |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(out); |
| | | builder.print(printer); |
| | | out.format("%nTotal: %d%n", count); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | private int listEntryContainers(Argument backendID) |
| | | { |
| | | TreeMap<JEBackendCfg, BackendImpl> jeBackends = getJEBackends(); |
| | | BackendImpl backend = null; |
| | | |
| | | for(BackendImpl b : jeBackends.values()) |
| | | { |
| | | if(b.getBackendID().equalsIgnoreCase(backendID.getValue())) |
| | | { |
| | | backend = b; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(backend == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue())); |
| | | return 1; |
| | | } |
| | | |
| | | // Acquire an shared lock for the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.acquireSharedLock(lockFile, failureReason)) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | |
| | | RootContainer rc; |
| | | try |
| | | { |
| | | rc = backend.getReadOnlyRootContainer(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get( |
| | | backend.getBackendID(), |
| | | StaticUtils.stackTraceToSingleLineString(e))); |
| | | return 1; |
| | | } |
| | | |
| | | try |
| | | { |
| | | // Create a table of their properties. |
| | | TableBuilder builder = new TableBuilder(); |
| | | int count = 0; |
| | | |
| | | builder.appendHeading(INFO_LABEL_DBTEST_BASE_DN.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_PREFIX.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_ENTRY_COUNT.get()); |
| | | |
| | | for(EntryContainer ec : rc.getEntryContainers()) |
| | | { |
| | | builder.startRow(); |
| | | builder.appendCell(ec.getBaseDN().toNormalizedString()); |
| | | builder.appendCell(ec.getDatabasePrefix()); |
| | | builder.appendCell(ec.getEntryCount()); |
| | | count++; |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(out); |
| | | builder.print(printer); |
| | | out.format("%nTotal: %d%n", count); |
| | | |
| | | return 0; |
| | | |
| | | |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get( |
| | | StaticUtils.stackTraceToSingleLineString(de))); |
| | | return 1; |
| | | } |
| | | finally |
| | | { |
| | | try |
| | | { |
| | | // Close the root container |
| | | rc.close(); |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | // Ignore. |
| | | } |
| | | |
| | | // Release the shared lock on the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.releaseLock(lockFile, failureReason)) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private int listDatabaseContainers(Argument backendID, |
| | | Argument baseDN) |
| | | { |
| | | TreeMap<JEBackendCfg, BackendImpl> jeBackends = getJEBackends(); |
| | | BackendImpl backend = null; |
| | | DN base = null; |
| | | |
| | | for(BackendImpl b : jeBackends.values()) |
| | | { |
| | | if(b.getBackendID().equalsIgnoreCase(backendID.getValue())) |
| | | { |
| | | backend = b; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(backend == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue())); |
| | | return 1; |
| | | } |
| | | |
| | | if(baseDN.isPresent()) |
| | | { |
| | | try |
| | | { |
| | | base = DN.decode(baseDN.getValue()); |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | printMessage(ERR_DBTEST_DECODE_BASE_DN.get(backendID.getValue(), |
| | | getExceptionMessage(de))); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | // Acquire an shared lock for the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.acquireSharedLock(lockFile, failureReason)) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | |
| | | RootContainer rc; |
| | | try |
| | | { |
| | | rc = backend.getReadOnlyRootContainer(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get( |
| | | backend.getBackendID(), |
| | | StaticUtils.stackTraceToSingleLineString(e))); |
| | | return 1; |
| | | } |
| | | |
| | | |
| | | try |
| | | { |
| | | // Create a table of their properties. |
| | | TableBuilder builder = new TableBuilder(); |
| | | int count = 0; |
| | | |
| | | builder.appendHeading(INFO_LABEL_DBTEST_DATABASE_NAME.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_DATABASE_TYPE.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_NAME.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_ENTRY_COUNT.get()); |
| | | |
| | | if(base != null) |
| | | { |
| | | EntryContainer ec = rc.getEntryContainer(base); |
| | | |
| | | if(ec == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get( |
| | | base.toNormalizedString(), backend.getBackendID())); |
| | | return 1; |
| | | } |
| | | |
| | | ArrayList<DatabaseContainer> databaseContainers = |
| | | new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databaseContainers); |
| | | for(DatabaseContainer dc : databaseContainers) |
| | | { |
| | | builder.startRow(); |
| | | builder.appendCell(dc.getName().replace(ec.getDatabasePrefix()+"_", |
| | | "")); |
| | | builder.appendCell(dc.getClass().getSimpleName()); |
| | | builder.appendCell(dc.getName()); |
| | | builder.appendCell(dc.getRecordCount()); |
| | | count++; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | for(EntryContainer ec : rc.getEntryContainers()) |
| | | { |
| | | builder.startRow(); |
| | | ArrayList<DatabaseContainer> databaseContainers = |
| | | new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databaseContainers); |
| | | builder.appendCell("Base DN: " + |
| | | ec.getBaseDN().toNormalizedString()); |
| | | for(DatabaseContainer dc : databaseContainers) |
| | | { |
| | | builder.startRow(); |
| | | builder.appendCell(dc.getName().replace( |
| | | ec.getDatabasePrefix()+"_","")); |
| | | builder.appendCell(dc.getClass().getSimpleName()); |
| | | builder.appendCell(dc.getName()); |
| | | builder.appendCell(dc.getRecordCount()); |
| | | count++; |
| | | } |
| | | } |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(out); |
| | | builder.print(printer); |
| | | out.format("%nTotal: %d%n", count); |
| | | return 0; |
| | | |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get( |
| | | StaticUtils.stackTraceToSingleLineString(de))); |
| | | return 1; |
| | | } |
| | | finally |
| | | { |
| | | try |
| | | { |
| | | // Close the root container |
| | | rc.close(); |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | // Ignore. |
| | | } |
| | | |
| | | // Release the shared lock on the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.releaseLock(lockFile, failureReason)) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private int listIndexStatus(Argument backendID, |
| | | Argument baseDN) |
| | | { |
| | | TreeMap<JEBackendCfg, BackendImpl> jeBackends = getJEBackends(); |
| | | BackendImpl backend = null; |
| | | DN base = null; |
| | | |
| | | for(BackendImpl b : jeBackends.values()) |
| | | { |
| | | if(b.getBackendID().equalsIgnoreCase(backendID.getValue())) |
| | | { |
| | | backend = b; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(backend == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue())); |
| | | return 1; |
| | | } |
| | | |
| | | if(baseDN.isPresent()) |
| | | { |
| | | try |
| | | { |
| | | base = DN.decode(baseDN.getValue()); |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | printMessage(ERR_DBTEST_DECODE_BASE_DN.get(backendID.getValue(), |
| | | getExceptionMessage(de))); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | // Acquire an shared lock for the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.acquireSharedLock(lockFile, failureReason)) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | |
| | | RootContainer rc; |
| | | try |
| | | { |
| | | rc = backend.getReadOnlyRootContainer(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get( |
| | | backend.getBackendID(), |
| | | StaticUtils.stackTraceToSingleLineString(e))); |
| | | return 1; |
| | | } |
| | | |
| | | |
| | | try |
| | | { |
| | | // Create a table of their properties. |
| | | TableBuilder builder = new TableBuilder(); |
| | | int count = 0; |
| | | |
| | | builder.appendHeading(INFO_LABEL_DBTEST_INDEX_NAME.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_INDEX_TYPE.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_NAME.get()); |
| | | builder.appendHeading(INFO_LABEL_DBTEST_INDEX_STATUS.get()); |
| | | |
| | | EntryContainer ec = rc.getEntryContainer(base); |
| | | |
| | | if(ec == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get( |
| | | base.toNormalizedString(), backend.getBackendID())); |
| | | return 1; |
| | | } |
| | | |
| | | ArrayList<DatabaseContainer> databaseContainers = |
| | | new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databaseContainers); |
| | | for(DatabaseContainer dc : databaseContainers) |
| | | { |
| | | if(dc instanceof Index || dc instanceof VLVIndex) |
| | | { |
| | | builder.startRow(); |
| | | builder.appendCell(dc.getName().replace(ec.getDatabasePrefix()+"_", |
| | | "")); |
| | | builder.appendCell(dc.getClass().getSimpleName()); |
| | | builder.appendCell(dc.getName()); |
| | | if(dc instanceof Index) |
| | | { |
| | | builder.appendCell(ec.getState().getIndexTrustState(null, |
| | | ((Index)dc))); |
| | | } |
| | | else if(dc instanceof VLVIndex) |
| | | { |
| | | builder.appendCell(ec.getState().getIndexTrustState(null, |
| | | ((VLVIndex)dc))); |
| | | } |
| | | count++; |
| | | } |
| | | } |
| | | |
| | | TextTablePrinter printer = new TextTablePrinter(out); |
| | | builder.print(printer); |
| | | out.format("%nTotal: %d%n", count); |
| | | return 0; |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get( |
| | | StaticUtils.stackTraceToSingleLineString(de))); |
| | | return 1; |
| | | } |
| | | finally |
| | | { |
| | | try |
| | | { |
| | | // Close the root container |
| | | rc.close(); |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | // Ignore. |
| | | } |
| | | |
| | | // Release the shared lock on the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.releaseLock(lockFile, failureReason)) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private int dumpDatabaseContainer(Argument backendID, Argument baseDN, |
| | | Argument databaseName, Argument skipDecode, |
| | | Argument maxKeyValue, Argument minKeyValue, |
| | | Argument maxDataSize, |
| | | Argument minDataSize) |
| | | { |
| | | TreeMap<JEBackendCfg, BackendImpl> jeBackends = getJEBackends(); |
| | | BackendImpl backend = null; |
| | | DN base = null; |
| | | |
| | | for(BackendImpl b : jeBackends.values()) |
| | | { |
| | | if(b.getBackendID().equalsIgnoreCase(backendID.getValue())) |
| | | { |
| | | backend = b; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(backend == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue())); |
| | | return 1; |
| | | } |
| | | |
| | | try |
| | | { |
| | | base = DN.decode(baseDN.getValue()); |
| | | } |
| | | catch(DirectoryException de) |
| | | { |
| | | printMessage(ERR_DBTEST_DECODE_BASE_DN.get(backendID.getValue(), |
| | | getExceptionMessage(de))); |
| | | return 1; |
| | | } |
| | | |
| | | // Acquire an shared lock for the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.acquireSharedLock(lockFile, failureReason)) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | return 1; |
| | | } |
| | | |
| | | RootContainer rc; |
| | | try |
| | | { |
| | | rc = backend.getReadOnlyRootContainer(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get( |
| | | backend.getBackendID(), |
| | | StaticUtils.stackTraceToSingleLineString(e))); |
| | | return 1; |
| | | } |
| | | |
| | | try |
| | | { |
| | | EntryContainer ec = rc.getEntryContainer(base); |
| | | |
| | | if(ec == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get( |
| | | base.toNormalizedString(), backend.getBackendID())); |
| | | return 1; |
| | | } |
| | | |
| | | DatabaseContainer databaseContainer = null; |
| | | ArrayList<DatabaseContainer> databaseContainers = |
| | | new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databaseContainers); |
| | | for(DatabaseContainer dc : databaseContainers) |
| | | { |
| | | if(dc.getName().replace(ec.getDatabasePrefix()+"_",""). |
| | | equalsIgnoreCase(databaseName.getValue())) |
| | | { |
| | | databaseContainer = dc; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(databaseContainer == null) |
| | | { |
| | | printMessage(ERR_DBTEST_NO_DATABASE_CONTAINERS_FOR_NAME.get( |
| | | databaseName.getValue(), base.toNormalizedString(), |
| | | backend.getBackendID())); |
| | | return 1; |
| | | } |
| | | |
| | | int count = 0; |
| | | long totalKeySize = 0; |
| | | long totalDataSize = 0; |
| | | int indent = 4; |
| | | |
| | | Cursor cursor = |
| | | databaseContainer.openCursor(null, CursorConfig.DEFAULT); |
| | | |
| | | try |
| | | { |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | LockMode lockMode = LockMode.DEFAULT; |
| | | OperationStatus status; |
| | | Comparator<byte[]> defaultComparator = |
| | | new AttributeIndex.KeyComparator(); |
| | | Comparator<byte[]> dnComparator = |
| | | new EntryContainer.KeyReverseComparator(); |
| | | byte[] start = null; |
| | | byte[] end = null; |
| | | int minSize = -1; |
| | | int maxSize = -1; |
| | | |
| | | if(maxDataSize.isPresent()) |
| | | { |
| | | try |
| | | { |
| | | maxSize = maxDataSize.getIntValue(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_CANNOT_DECODE_SIZE.get( |
| | | maxDataSize.getValue(), getExceptionMessage(e))); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | if(minDataSize.isPresent()) |
| | | { |
| | | try |
| | | { |
| | | minSize = minDataSize.getIntValue(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_CANNOT_DECODE_SIZE.get( |
| | | minDataSize.getValue(), getExceptionMessage(e))); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | // Parse the min value if given |
| | | if(minKeyValue.isPresent()) |
| | | { |
| | | try |
| | | { |
| | | if(minKeyValue.getValue().startsWith("0x")) |
| | | { |
| | | start = |
| | | StaticUtils.hexStringToByteArray(minKeyValue.getValue(). |
| | | substring(2)); |
| | | } |
| | | else |
| | | { |
| | | if(databaseContainer instanceof DN2ID || |
| | | databaseContainer instanceof DN2URI) |
| | | { |
| | | // Encode the value as a DN |
| | | start = StaticUtils.getBytes( |
| | | DN.decode(minKeyValue.getValue()).toNormalizedString()); |
| | | } |
| | | else if(databaseContainer instanceof ID2Entry) |
| | | { |
| | | // Encode the value as an entryID |
| | | start = JebFormat.entryIDToDatabase( |
| | | Long.parseLong(minKeyValue.getValue())); |
| | | } |
| | | else if(databaseContainer instanceof VLVIndex) |
| | | { |
| | | // Encode the value as a size/value pair |
| | | byte[] vBytes = |
| | | new ASN1OctetString(minKeyValue.getValue()).value(); |
| | | byte[] vLength = ASN1Element.encodeLength(vBytes.length); |
| | | start = new byte[vBytes.length + vLength.length]; |
| | | System.arraycopy(vLength, 0, start, 0, vLength.length); |
| | | System.arraycopy(vBytes, 0, start, vLength.length, |
| | | vBytes.length); |
| | | } |
| | | else |
| | | { |
| | | start = new ASN1OctetString(minKeyValue.getValue()).value(); |
| | | } |
| | | } |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_CANNOT_DECODE_KEY.get( |
| | | minKeyValue.getValue(), getExceptionMessage(e))); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | // Parse the max value if given |
| | | if(maxKeyValue.isPresent()) |
| | | { |
| | | try |
| | | { |
| | | if(maxKeyValue.getValue().startsWith("0x")) |
| | | { |
| | | end = |
| | | StaticUtils.hexStringToByteArray(maxKeyValue.getValue(). |
| | | substring(2)); |
| | | } |
| | | else |
| | | { |
| | | if(databaseContainer instanceof DN2ID || |
| | | databaseContainer instanceof DN2URI) |
| | | { |
| | | // Encode the value as a DN |
| | | end = StaticUtils.getBytes( |
| | | DN.decode(maxKeyValue.getValue()).toNormalizedString()); |
| | | } |
| | | else if(databaseContainer instanceof ID2Entry) |
| | | { |
| | | // Encode the value as an entryID |
| | | end = JebFormat.entryIDToDatabase( |
| | | Long.parseLong(maxKeyValue.getValue())); |
| | | } |
| | | else if(databaseContainer instanceof VLVIndex) |
| | | { |
| | | // Encode the value as a size/value pair |
| | | byte[] vBytes = |
| | | new ASN1OctetString(maxKeyValue.getValue()).value(); |
| | | byte[] vLength = ASN1Element.encodeLength(vBytes.length); |
| | | end = new byte[vBytes.length + vLength.length]; |
| | | System.arraycopy(vLength, 0, end, 0, vLength.length); |
| | | System.arraycopy(vBytes, 0, end, vLength.length, |
| | | vBytes.length); |
| | | } |
| | | else |
| | | { |
| | | end = new ASN1OctetString(maxKeyValue.getValue()).value(); |
| | | } |
| | | } |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | printMessage(ERR_DBTEST_CANNOT_DECODE_KEY.get( |
| | | maxKeyValue.getValue(), getExceptionMessage(e))); |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | |
| | | if(start != null) |
| | | { |
| | | key.setData(start); |
| | | status = cursor.getSearchKey(key, data, lockMode); |
| | | } |
| | | else |
| | | { |
| | | status = cursor.getFirst(key, data, lockMode); |
| | | } |
| | | |
| | | while(status == OperationStatus.SUCCESS) |
| | | { |
| | | // Make sure this record is within the value size params |
| | | if((minSize > 0 && data.getSize() < minSize) || |
| | | (maxSize > 0 && data.getSize() > maxSize)) |
| | | { |
| | | status = cursor.getNext(key, data, lockMode); |
| | | continue; |
| | | } |
| | | |
| | | // Make sure we haven't gone pass the max value yet |
| | | if(end != null) |
| | | { |
| | | if(databaseContainer instanceof DN2ID) |
| | | { |
| | | if(dnComparator.compare(key.getData(), end) > 0) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | else if(databaseContainer instanceof DN2URI) |
| | | { |
| | | if(dnComparator.compare(key.getData(), end) > 0) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | else if(databaseContainer instanceof Index) |
| | | { |
| | | if(((Index)databaseContainer).indexer.getComparator(). |
| | | compare(key.getData(), end) > 0) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | else if(databaseContainer instanceof VLVIndex) |
| | | { |
| | | if(((VLVIndex)databaseContainer).comparator. |
| | | compare(key.getData(), end) > 0) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if(defaultComparator.compare(key.getData(), |
| | | end) > 0) |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | Message keyLabel = INFO_LABEL_DBTEST_KEY.get(); |
| | | Message dataLabel = INFO_LABEL_DBTEST_DATA.get(); |
| | | |
| | | String formatedKey = null; |
| | | String formatedData = null; |
| | | |
| | | if(!skipDecode.isPresent()) |
| | | { |
| | | if(databaseContainer instanceof DN2ID) |
| | | { |
| | | try |
| | | { |
| | | formatedKey = DN.decode(new ASN1OctetString( |
| | | key.getData())).toNormalizedString(); |
| | | keyLabel = INFO_LABEL_DBTEST_ENTRY_DN.get(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | Message message = |
| | | ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | formatedData = String.valueOf( |
| | | JebFormat.entryIDFromDatabase(data.getData())); |
| | | dataLabel = INFO_LABEL_DBTEST_ENTRY_ID.get(); |
| | | } |
| | | else if(databaseContainer instanceof ID2Entry) |
| | | { |
| | | formatedKey = String.valueOf( |
| | | JebFormat.entryIDFromDatabase(key.getData())); |
| | | keyLabel = INFO_LABEL_DBTEST_ENTRY_ID.get(); |
| | | try |
| | | { |
| | | formatedData = System.getProperty("line.separator") + |
| | | JebFormat.entryFromDatabase(data.getData()).toLDIFString(); |
| | | dataLabel = INFO_LABEL_DBTEST_ENTRY.get(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | Message message = |
| | | ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | else if(databaseContainer instanceof DN2URI) |
| | | { |
| | | try |
| | | { |
| | | formatedKey = DN.decode(new ASN1OctetString( |
| | | key.getData())).toNormalizedString(); |
| | | keyLabel = INFO_LABEL_DBTEST_ENTRY_DN.get(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | Message message = |
| | | ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | formatedData = new ASN1OctetString(key.getData()).stringValue(); |
| | | dataLabel = INFO_LABEL_DBTEST_URI.get(); |
| | | } |
| | | else if(databaseContainer instanceof Index) |
| | | { |
| | | formatedKey = new ASN1OctetString(key.getData()).stringValue(); |
| | | keyLabel = INFO_LABEL_DBTEST_INDEX_VALUE.get(); |
| | | |
| | | EntryIDSet idSet = new EntryIDSet(key.getData(), |
| | | data.getData()); |
| | | if(idSet.isDefined()) |
| | | { |
| | | int lineCount = 0; |
| | | StringBuilder builder = new StringBuilder(); |
| | | |
| | | Iterator<EntryID> i = idSet.iterator(); |
| | | while(i.hasNext()) |
| | | { |
| | | builder.append(i.next()); |
| | | if(lineCount == 10) |
| | | { |
| | | builder.append(System.getProperty("line.separator")); |
| | | lineCount = 0; |
| | | } |
| | | else |
| | | { |
| | | builder.append(" "); |
| | | lineCount++; |
| | | } |
| | | } |
| | | formatedData = builder.toString(); |
| | | } |
| | | else |
| | | { |
| | | formatedData = idSet.toString(); |
| | | } |
| | | dataLabel = INFO_LABEL_DBTEST_INDEX_ENTRY_ID_LIST.get(); |
| | | } |
| | | else if(databaseContainer instanceof VLVIndex) |
| | | { |
| | | VLVIndex index = (VLVIndex)databaseContainer; |
| | | SortKey[] sortKeys = index.sortOrder.getSortKeys(); |
| | | |
| | | int pos = 0; |
| | | byte[] keyBytes = key.getData(); |
| | | if(keyBytes.length > 0) |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | |
| | | // Decode the attribute values |
| | | for(SortKey sortKey : sortKeys) |
| | | { |
| | | int valueLength = keyBytes[pos] & 0x7F; |
| | | if (keyBytes[pos++] != valueLength) |
| | | { |
| | | int numLengthBytes = valueLength; |
| | | valueLength = 0; |
| | | for (int k=0; k < numLengthBytes; k++, pos++) |
| | | { |
| | | valueLength = (valueLength << 8) | |
| | | (keyBytes[pos] & 0xFF); |
| | | } |
| | | } |
| | | |
| | | byte[] valueBytes = new byte[valueLength]; |
| | | System.arraycopy(keyBytes, pos, valueBytes, 0, |
| | | valueLength); |
| | | builder.append(sortKey.getAttributeType().getNameOrOID()); |
| | | builder.append(": "); |
| | | if(valueBytes.length == 0) |
| | | { |
| | | builder.append("NULL"); |
| | | } |
| | | else |
| | | { |
| | | builder.append( |
| | | new ASN1OctetString(valueBytes).stringValue()); |
| | | } |
| | | builder.append(" "); |
| | | pos += valueLength; |
| | | } |
| | | |
| | | byte[] entryIDBytes = new byte[8]; |
| | | System.arraycopy(keyBytes, pos, entryIDBytes, 0, |
| | | entryIDBytes.length); |
| | | long entryID = JebFormat.entryIDFromDatabase(entryIDBytes); |
| | | |
| | | formatedKey = System.getProperty("line.separator") + |
| | | String.valueOf(entryID) + ": " + builder.toString(); |
| | | } |
| | | else |
| | | { |
| | | formatedKey = "UNBOUNDED"; |
| | | } |
| | | keyLabel = INFO_LABEL_DBTEST_VLV_INDEX_LAST_SORT_KEYS.get(); |
| | | |
| | | try |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | SortValuesSet svs = new SortValuesSet(key.getData(), |
| | | data.getData(), |
| | | index, |
| | | null); |
| | | long[] entryIDs = svs.getEntryIDs(); |
| | | for(int i = 0; i < entryIDs.length; i++) |
| | | { |
| | | builder.append(String.valueOf(entryIDs[i])); |
| | | builder.append(": "); |
| | | for(int j = 0; j < sortKeys.length; j++) |
| | | { |
| | | SortKey sortKey = index.sortOrder.getSortKeys()[j]; |
| | | byte[] value = svs.getValue(i * sortKeys.length + j); |
| | | builder.append(sortKey.getAttributeType().getNameOrOID()); |
| | | builder.append(": "); |
| | | if(value == null) |
| | | { |
| | | builder.append("NULL"); |
| | | } |
| | | else if(value.length == 0) |
| | | { |
| | | builder.append("SIZE-EXCEEDED"); |
| | | } |
| | | else |
| | | { |
| | | builder.append( |
| | | new ASN1OctetString(value).stringValue()); |
| | | } |
| | | builder.append(" "); |
| | | } |
| | | builder.append(System.getProperty("line.separator")); |
| | | } |
| | | formatedData = System.getProperty("line.separator") + |
| | | builder.toString(); |
| | | dataLabel = INFO_LABEL_DBTEST_INDEX_ENTRY_ID_LIST.get(); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | Message message = |
| | | ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if(formatedKey == null) |
| | | { |
| | | StringBuilder keyBuilder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(keyBuilder, key.getData(), |
| | | indent); |
| | | formatedKey = System.getProperty("line.separator") + |
| | | keyBuilder.toString(); |
| | | } |
| | | if(formatedData == null) |
| | | { |
| | | StringBuilder dataBuilder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(dataBuilder, data.getData(), |
| | | indent); |
| | | formatedData = System.getProperty("line.separator") + |
| | | dataBuilder.toString(); |
| | | } |
| | | |
| | | out.format("%s (%d bytes): %s%n", keyLabel, |
| | | key.getData().length, formatedKey); |
| | | out.format("%s (%d bytes): %s%n%n", dataLabel, |
| | | data.getData().length, formatedData); |
| | | |
| | | status = cursor.getNext(key, data, lockMode); |
| | | count++; |
| | | totalKeySize += key.getData().length; |
| | | totalDataSize += data.getData().length; |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | cursor.close(); |
| | | } |
| | | out.format("%nTotal Records: %d%n", count); |
| | | if(count > 0) |
| | | { |
| | | out.format("Total / Average Key Size: %d bytes / %d bytes%n", |
| | | totalKeySize, totalKeySize / count); |
| | | out.format("Total / Average Data Size: %d bytes / %d bytes%n", |
| | | totalDataSize, totalDataSize / count); |
| | | } |
| | | return 0; |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get( |
| | | StaticUtils.stackTraceToSingleLineString(de))); |
| | | return 1; |
| | | } |
| | | finally |
| | | { |
| | | try |
| | | { |
| | | // Close the root container |
| | | rc.close(); |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | // Ignore. |
| | | } |
| | | |
| | | // Release the shared lock on the backend. |
| | | try |
| | | { |
| | | String lockFile = LockFileManager.getBackendLockFileName(backend); |
| | | StringBuilder failureReason = new StringBuilder(); |
| | | if (! LockFileManager.releaseLock(lockFile, failureReason)) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), String.valueOf(failureReason)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get( |
| | | backend.getBackendID(), getExceptionMessage(e)); |
| | | printMessage(message); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private TreeMap<JEBackendCfg, BackendImpl> getJEBackends() |
| | | { |
| | | ArrayList<Backend> backendList = new ArrayList<Backend>(); |
| | | ArrayList<BackendCfg> entryList = new ArrayList<BackendCfg>(); |
| | | ArrayList<List<DN>> dnList = new ArrayList<List<DN>>(); |
| | | int code = BackendToolUtils.getBackends(backendList, entryList, dnList); |
| | | // TODO: Throw error if return code is not 0 |
| | | |
| | | TreeMap<JEBackendCfg, BackendImpl> jeBackends = |
| | | new TreeMap<JEBackendCfg, BackendImpl>(); |
| | | for(int i = 0; i < backendList.size(); i++) |
| | | { |
| | | Backend backend = backendList.get(i); |
| | | if(backend instanceof BackendImpl) |
| | | { |
| | | jeBackends.put((JEBackendCfg)entryList.get(i), (BackendImpl)backend); |
| | | } |
| | | } |
| | | |
| | | return jeBackends; |
| | | } |
| | | |
| | | /** |
| | | * Displays a message to the error stream. |
| | | * |
| | | * @param msg |
| | | * The message. |
| | | */ |
| | | public final void printMessage(Message msg) { |
| | | err.println(wrapText(msg.toString(), MAX_LINE_WIDTH)); |
| | | } |
| | | } |
| | |
| | | ds-cfg-index-attribute: uid |
| | | ds-cfg-index-type: equality |
| | | |
| | | dn: ds-cfg-index-attribute=ds-sync-hist,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | | objectClass: ds-cfg-je-index |
| | | ds-cfg-index-attribute: ds-sync-hist |
| | | ds-cfg-index-type: ordering |
| | | |
| | | dn: ds-cfg-index-attribute=entryuuid,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | | objectClass: ds-cfg-je-index |
| | | ds-cfg-index-attribute: entryuuid |
| | | ds-cfg-index-type: equality |
| | | |
| | | dn: cn=Virtual Static member,cn=Virtual Attributes,cn=config |
| | | changetype: modify |
| | | replace: ds-cfg-allow-retrieving-membership |
| | |
| | | } |
| | | |
| | | @Test(dependsOnMethods = "testAdd") |
| | | public void testNumSubordinates() throws Exception |
| | | { |
| | | DN dn = DN.decode("dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 1); |
| | | dn = DN.decode("ou=People,dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 12); |
| | | dn = DN.decode("dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), -1); |
| | | dn = DN.decode("dc=test1,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 2); |
| | | dn = DN.decode("uid=user.10,ou=People,dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 0); |
| | | dn = DN.decode("uid=does not exist,ou=People,dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), -1); |
| | | } |
| | | |
| | | @Test(dependsOnMethods = "testAdd") |
| | | public void testSearchIndex() throws Exception { |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | |
| | | |
| | | @Test(dependsOnMethods = {"testAdd", "testSearchIndex", |
| | | "testSearchScope", "testSearchNotIndexed", "testModifyDNNewSuperior", |
| | | "testMatchedDN"}) |
| | | "testMatchedDN", "testNumSubordinates", |
| | | "testNumSubordinatesIndexEntryLimitExceeded"}) |
| | | public void testDeleteSubtree() throws Exception { |
| | | Control control = new Control(OID_SUBTREE_DELETE_CONTROL, false); |
| | | ArrayList<Control> deleteSubTreeControl = new ArrayList<Control>(); |
| | |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testSearchNotIndexed", "testAdd", |
| | | "testSearchIndex", "testSearchScope", "testMatchedDN"}) |
| | | "testSearchIndex", "testSearchScope", "testMatchedDN", |
| | | "testNumSubordinates", "testNumSubordinatesIndexEntryLimitExceeded"}) |
| | | public void testReplaceEntry() throws Exception { |
| | | Entry entry; |
| | | Entry oldEntry; |
| | |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testSearchNotIndexed", "testAdd", |
| | | "testSearchIndex", "testSearchScope", "testMatchedDN"}) |
| | | "testSearchIndex", "testSearchScope", "testMatchedDN", |
| | | "testNumSubordinates", "testNumSubordinatesIndexEntryLimitExceeded"}) |
| | | public void testModifyEntry() throws Exception { |
| | | Entry entry; |
| | | Entry newEntry; |
| | |
| | | |
| | | @Test(dependsOnMethods = {"testSearchNotIndexed", "testAdd", "testSearchIndex", |
| | | "testSearchScope", "testModifyEntry", "testModifyDN", "testReplaceEntry", |
| | | "testDeleteEntry", "testMatchedDN"}) |
| | | "testDeleteEntry", "testMatchedDN", "testNumSubordinates", |
| | | "testNumSubordinatesIndexEntryLimitExceeded"}) |
| | | public void testModifyDNNewSuperior() throws Exception { |
| | | //Add the new superior entry we want to move to. Test to see if the child ID |
| | | //always above parent invarient is preseved. |
| | |
| | | |
| | | } |
| | | |
| | | @Test(dependsOnMethods = "testSearchNotIndexed") |
| | | public void testNumSubordinatesIndexEntryLimitExceeded() throws Exception |
| | | { |
| | | DN dn = DN.decode("dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 1); |
| | | |
| | | // 1 entry was deleted and 2 added for a total of 13 |
| | | dn = DN.decode("ou=People,dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 13); |
| | | dn = DN.decode("dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), -1); |
| | | dn = DN.decode("dc=test1,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 2); |
| | | dn = DN.decode("uid=user.10,ou=People,dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), 0); |
| | | dn = DN.decode("uid=does not exist,ou=People,dc=test,dc=com"); |
| | | assertEquals(backend.numSubordinates(dn), -1); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Provides a set of DNs for the matched DN test case. |
| New file |
| | |
| | | /* |
| | | * 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 org.opends.server.types.*; |
| | | import org.opends.server.TestCaseUtils; |
| | | import static org.opends.server.util.StaticUtils.getBytes; |
| | | import static org.opends.server.util.ServerConstants.OID_REAL_ATTRS_ONLY; |
| | | import static org.opends.server.util.ServerConstants.OID_VIRTUAL_ATTRS_ONLY; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | import static org.testng.Assert.assertNotNull; |
| | | import static org.testng.Assert.assertTrue; |
| | | import static org.testng.Assert.assertFalse; |
| | | import static org.testng.Assert.assertEquals; |
| | | |
| | | import java.util.List; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.UUID; |
| | | import java.util.LinkedList; |
| | | |
| | | public class HasSubordinatesVirtualAttributeProviderTestCase |
| | | { |
| | | // The attribute type for the hasSubordinates attribute. |
| | | private AttributeType hasSubordinatesType; |
| | | |
| | | private List<Entry> entries; |
| | | |
| | | /** |
| | | * Ensures that the Directory Server is running. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @BeforeClass() |
| | | public void startServer() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.startServer(); |
| | | |
| | | hasSubordinatesType = |
| | | DirectoryServer.getAttributeType("hassubordinates", false); |
| | | assertNotNull(hasSubordinatesType); |
| | | |
| | | entries = TestCaseUtils.makeEntries( |
| | | "dn: dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: domain", |
| | | "dc: example", |
| | | "", |
| | | "dn: ou=People,dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Employees,ou=People,dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: organizationalUnit", |
| | | "ou: Employees", |
| | | "", |
| | | "dn: ou=Buildings,dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: organizationalUnit", |
| | | "ou: Buildings", |
| | | "", |
| | | "dn: uid=user.0,ou=People,dc=example,dc=com", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "givenName: Aaccf", |
| | | "sn: Amar", |
| | | "cn: Aaccf Amar", |
| | | "initials: AQA", |
| | | "employeeNumber: 0", |
| | | "uid: user.0", |
| | | "mail: user.0@example.com", |
| | | "userPassword: password", |
| | | "telephoneNumber: 380-535-2354", |
| | | "homePhone: 707-626-3913", |
| | | "pager: 456-345-7750", |
| | | "mobile: 366-674-7274", |
| | | "street: 99262 Eleventh Street", |
| | | "l: Salem", |
| | | "st: NM", |
| | | "postalCode: 36530", |
| | | "postalAddress: Aaccf Amar$99262 Eleventh Street$Salem, NM 36530", |
| | | "description: This is the description for Aaccf Amar.", |
| | | "", |
| | | "dn: uid=user.1,ou=People,dc=example,dc=com", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "givenName: Aaren", |
| | | "sn: Atp", |
| | | "cn: Aaren Atp", |
| | | "initials: APA", |
| | | "employeeNumber: 1", |
| | | "uid: user.1", |
| | | "mail: user.1@example.com", |
| | | "userPassword: password", |
| | | "telephoneNumber: 643-278-6134", |
| | | "homePhone: 546-786-4099", |
| | | "pager: 508-261-3187", |
| | | "mobile: 377-267-7824", |
| | | "street: 78113 Fifth Street", |
| | | "l: Chico", |
| | | "st: TN", |
| | | "postalCode: 72322", |
| | | "postalAddress: Aaren Atp$78113 Fifth Street$Chico, TN 72322", |
| | | "description: This is the description for Aaren Atp.", |
| | | "", |
| | | "dn: uid=user.2,ou=Employees,ou=People,dc=example,dc=com", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "givenName: Aarika", |
| | | "sn: Atpco", |
| | | "cn: Aarika Atpco", |
| | | "initials: ARA", |
| | | "employeeNumber: 2", |
| | | "uid: user.2", |
| | | "mail: user.2@example.com", |
| | | "userPassword: password", |
| | | "telephoneNumber: 547-504-3498", |
| | | "homePhone: 955-899-7308", |
| | | "pager: 710-832-9316", |
| | | "mobile: 688-388-4525", |
| | | "street: 59208 Elm Street", |
| | | "l: Youngstown", |
| | | "st: HI", |
| | | "postalCode: 57377", |
| | | "postalAddress: Aarika Atpco$59208 Elm Street$Youngstown, HI 57377", |
| | | "description: This is the description for Aarika Atpco."); |
| | | |
| | | TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); |
| | | |
| | | TestCaseUtils.addEntries(entries); |
| | | } |
| | | |
| | | /** |
| | | * Retrieves a set of entry DNs for use in testing the hasSubordinates virtual |
| | | * attribute. |
| | | * |
| | | * @return A set of entry DNs for use in testing the hasSubordinates virtual |
| | | * attribute. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @DataProvider(name = "testEntryDNs") |
| | | public Object[][] getTestEntryDNs() |
| | | throws Exception |
| | | { |
| | | return new Object[][] |
| | | { |
| | | new Object[] { DN.decode("dc=example,dc=com"), true }, |
| | | new Object[] { DN.decode("ou=People,dc=example,dc=com"), true }, |
| | | new Object[] { DN.decode("ou=Employees,ou=People,dc=example,dc=com"), true }, |
| | | new Object[] { DN.decode("ou=Buildings,dc=example,dc=com"), false }, |
| | | new Object[] { DN.decode("uid=user.0,ou=People,dc=example,dc=com"), false }, |
| | | new Object[] { DN.decode("uid=user.1,ou=People,dc=example,dc=com"), false }, |
| | | new Object[] { DN.decode("uid=user.2,ou=Employees,ou=People" + |
| | | ",dc=example,dc=com"), false }, |
| | | new Object[] { DN.decode("cn=monitor"), true }, |
| | | new Object[] { DN.decode("cn=Backends,cn=config"), true }, |
| | | new Object[] { DN.decode("cn=Work Queue,cn=config"), false }, |
| | | new Object[] { DN.decode("cn=tasks"), true }, |
| | | new Object[] { DN.decode("cn=Recurring Tasks,cn=tasks"), false }, |
| | | new Object[] { DN.decode("cn=Scheduled Tasks,cn=tasks"), false }, |
| | | new Object[] { DN.decode("cn=backups"), false } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * Tests the {@code getEntry} method for the specified entry to ensure that |
| | | * the entry returned includes the hasSubordinates operational attribute |
| | | * with the correct value. |
| | | * |
| | | * @param entryDN The DN of the entry to retrieve and verify. |
| | | * @param hasSubs Whether this entry has any subs. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test(dataProvider = "testEntryDNs") |
| | | public void testGetEntry(DN entryDN, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | Entry e = DirectoryServer.getEntry(entryDN); |
| | | assertNotNull(e); |
| | | assertTrue(e.hasAttribute(hasSubordinatesType)); |
| | | |
| | | List<Attribute> attrList = e.getAttribute(hasSubordinatesType); |
| | | assertNotNull(attrList); |
| | | assertFalse(attrList.isEmpty()); |
| | | for (Attribute a : attrList) |
| | | { |
| | | assertTrue(a.hasValue()); |
| | | assertEquals(a.getValues().size(), 1); |
| | | assertTrue(a.getValues().contains(new AttributeValue( |
| | | ByteStringFactory.create(String.valueOf(hasSubs)), |
| | | ByteStringFactory.create(String.valueOf(hasSubs))))); |
| | | assertTrue(a.hasValue(new AttributeValue(hasSubordinatesType, |
| | | String.valueOf(hasSubs)))); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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 testSearchhasSubordinatesAttr(DN entryDN, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(objectClass=*)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("hasSubordinates"); |
| | | |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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 testSearchExcludehasSubordinatesAttr(DN entryDN, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates attribute is included when that attribute is specifically |
| | | * requested and the hasSubordinates 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 testSearchhasSubordinatesAttrInMatchingFilter(DN entryDN, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(hasSubordinates=" + hasSubs + ")"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("hasSubordinates"); |
| | | |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * no entries are returned when the hasSubordinates 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 testSearchhasSubordinatesAttrInNonMatchingFilter(DN entryDN, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(hasSubordinates=wrong)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("hasSubordinates"); |
| | | |
| | | 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 hasSubordinates 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 testSearchhasSubordinatesAttrRealAttrsOnly(DN entryDN, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(objectClass=*)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("hasSubordinates"); |
| | | |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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 testSearchhasSubordinatesAttrVirtualAttrsOnly(DN entryDN, boolean hasSubs) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(objectClass=*)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("hasSubordinates"); |
| | | |
| | | 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(hasSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Tests the {@code isMultiValued} method. |
| | | */ |
| | | @Test() |
| | | public void testIsMultiValued() |
| | | { |
| | | NumSubordinatesVirtualAttributeProvider provider = |
| | | new NumSubordinatesVirtualAttributeProvider(); |
| | | assertFalse(provider.isMultiValued()); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * 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 c; |
| | | |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.extensions.NumSubordinatesVirtualAttributeProvider; |
| | | import org.opends.server.protocols.internal.InternalClientConnection; |
| | | import org.opends.server.protocols.internal.InternalSearchOperation; |
| | | import static org.opends.server.util.StaticUtils.getBytes; |
| | | import static org.opends.server.util.ServerConstants.OID_REAL_ATTRS_ONLY; |
| | | import static org.opends.server.util.ServerConstants.OID_VIRTUAL_ATTRS_ONLY; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | import static org.testng.Assert.assertNotNull; |
| | | import static org.testng.Assert.assertTrue; |
| | | import static org.testng.Assert.assertFalse; |
| | | import static org.testng.Assert.assertEquals; |
| | | |
| | | import java.util.*; |
| | | |
| | | public class NumSubordinatesVirtualAttributeProviderTestCase |
| | | { |
| | | // The attribute type for the numSubordinates attribute. |
| | | private AttributeType numSubordinatesType; |
| | | |
| | | private List<Entry> entries; |
| | | |
| | | /** |
| | | * Ensures that the Directory Server is running. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @BeforeClass() |
| | | public void startServer() |
| | | throws Exception |
| | | { |
| | | TestCaseUtils.startServer(); |
| | | |
| | | numSubordinatesType = |
| | | DirectoryServer.getAttributeType("numsubordinates", false); |
| | | assertNotNull(numSubordinatesType); |
| | | |
| | | entries = TestCaseUtils.makeEntries( |
| | | "dn: dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: domain", |
| | | "dc: example", |
| | | "", |
| | | "dn: ou=People,dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: organizationalUnit", |
| | | "ou: People", |
| | | "", |
| | | "dn: ou=Employees,ou=People,dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: organizationalUnit", |
| | | "ou: Employees", |
| | | "", |
| | | "dn: ou=Buildings,dc=example,dc=com", |
| | | "objectclass: top", |
| | | "objectclass: organizationalUnit", |
| | | "ou: Buildings", |
| | | "", |
| | | "dn: uid=user.0,ou=People,dc=example,dc=com", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "givenName: Aaccf", |
| | | "sn: Amar", |
| | | "cn: Aaccf Amar", |
| | | "initials: AQA", |
| | | "employeeNumber: 0", |
| | | "uid: user.0", |
| | | "mail: user.0@example.com", |
| | | "userPassword: password", |
| | | "telephoneNumber: 380-535-2354", |
| | | "homePhone: 707-626-3913", |
| | | "pager: 456-345-7750", |
| | | "mobile: 366-674-7274", |
| | | "street: 99262 Eleventh Street", |
| | | "l: Salem", |
| | | "st: NM", |
| | | "postalCode: 36530", |
| | | "postalAddress: Aaccf Amar$99262 Eleventh Street$Salem, NM 36530", |
| | | "description: This is the description for Aaccf Amar.", |
| | | "", |
| | | "dn: uid=user.1,ou=People,dc=example,dc=com", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "givenName: Aaren", |
| | | "sn: Atp", |
| | | "cn: Aaren Atp", |
| | | "initials: APA", |
| | | "employeeNumber: 1", |
| | | "uid: user.1", |
| | | "mail: user.1@example.com", |
| | | "userPassword: password", |
| | | "telephoneNumber: 643-278-6134", |
| | | "homePhone: 546-786-4099", |
| | | "pager: 508-261-3187", |
| | | "mobile: 377-267-7824", |
| | | "street: 78113 Fifth Street", |
| | | "l: Chico", |
| | | "st: TN", |
| | | "postalCode: 72322", |
| | | "postalAddress: Aaren Atp$78113 Fifth Street$Chico, TN 72322", |
| | | "description: This is the description for Aaren Atp.", |
| | | "", |
| | | "dn: uid=user.2,ou=Employees,ou=People,dc=example,dc=com", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "givenName: Aarika", |
| | | "sn: Atpco", |
| | | "cn: Aarika Atpco", |
| | | "initials: ARA", |
| | | "employeeNumber: 2", |
| | | "uid: user.2", |
| | | "mail: user.2@example.com", |
| | | "userPassword: password", |
| | | "telephoneNumber: 547-504-3498", |
| | | "homePhone: 955-899-7308", |
| | | "pager: 710-832-9316", |
| | | "mobile: 688-388-4525", |
| | | "street: 59208 Elm Street", |
| | | "l: Youngstown", |
| | | "st: HI", |
| | | "postalCode: 57377", |
| | | "postalAddress: Aarika Atpco$59208 Elm Street$Youngstown, HI 57377", |
| | | "description: This is the description for Aarika Atpco."); |
| | | |
| | | TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com"); |
| | | |
| | | TestCaseUtils.addEntries(entries); |
| | | } |
| | | |
| | | /** |
| | | * Retrieves a set of entry DNs for use in testing the numSubordinates virtual |
| | | * attribute. |
| | | * |
| | | * @return A set of entry DNs for use in testing the numSubordinates virtual |
| | | * attribute. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @DataProvider(name = "testEntryDNs") |
| | | public Object[][] getTestEntryDNs() |
| | | throws Exception |
| | | { |
| | | return new Object[][] |
| | | { |
| | | new Object[] { DN.decode("dc=example,dc=com"), 2 }, |
| | | new Object[] { DN.decode("ou=People,dc=example,dc=com"), 3 }, |
| | | new Object[] { DN.decode("ou=Employees,ou=People,dc=example,dc=com"), 1 }, |
| | | new Object[] { DN.decode("ou=Buildings,dc=example,dc=com"), 0 }, |
| | | new Object[] { DN.decode("uid=user.0,ou=People,dc=example,dc=com"), 0 }, |
| | | new Object[] { DN.decode("uid=user.1,ou=People,dc=example,dc=com"), 0 }, |
| | | new Object[] { DN.decode("uid=user.2,ou=Employees,ou=People" + |
| | | ",dc=example,dc=com"), 0 }, |
| | | new Object[] { DN.decode("cn=monitor"), |
| | | DirectoryServer.getMonitorProviders().size() }, |
| | | new Object[] { DN.decode("cn=Backends,cn=config"), |
| | | DirectoryServer.getBackends().size() }, |
| | | new Object[] { DN.decode("cn=Work Queue,cn=config"), 0 }, |
| | | new Object[] { DN.decode("cn=tasks"), 2 }, |
| | | new Object[] { DN.decode("cn=Recurring Tasks,cn=tasks"), 0 }, |
| | | new Object[] { DN.decode("cn=Scheduled Tasks,cn=tasks"), 0 }, |
| | | new Object[] { DN.decode("cn=backups"), 0 } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * Tests the {@code getEntry} method for the specified entry to ensure that |
| | | * the entry returned includes the numSubordinates operational attribute |
| | | * with the correct value. |
| | | * |
| | | * @param entryDN The DN of the entry to retrieve and verify. |
| | | * @param count The number of subordinates the entry should have. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test(dataProvider = "testEntryDNs") |
| | | public void testGetEntry(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | Entry e = DirectoryServer.getEntry(entryDN); |
| | | assertNotNull(e); |
| | | assertTrue(e.hasAttribute(numSubordinatesType)); |
| | | |
| | | List<Attribute> attrList = e.getAttribute(numSubordinatesType); |
| | | assertNotNull(attrList); |
| | | assertFalse(attrList.isEmpty()); |
| | | for (Attribute a : attrList) |
| | | { |
| | | assertTrue(a.hasValue()); |
| | | assertEquals(a.getValues().size(), 1); |
| | | assertTrue(a.getValues().contains(new AttributeValue( |
| | | ByteStringFactory.create(String.valueOf(count)), |
| | | ByteStringFactory.create(String.valueOf(count))))); |
| | | assertTrue(a.hasValue(new AttributeValue(numSubordinatesType, |
| | | String.valueOf(count)))); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the hasSubordinates 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, int count) |
| | | throws Exception |
| | | { |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates 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, int count) |
| | | throws Exception |
| | | { |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates 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, int count) |
| | | throws Exception |
| | | { |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates 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, int count) |
| | | throws Exception |
| | | { |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates 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 testSearchnumSubordinatesAttr(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(objectClass=*)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("numSubordinates"); |
| | | |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates 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 testSearchExcludenumSubordinatesAttr(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates attribute is included when that attribute is specifically |
| | | * requested and the numSubordinates 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 testSearchnumSubordinatesAttrInMatchingFilter(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(numSubordinates=" + count + ")"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("numSubordinates"); |
| | | |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * no entries are returned when the numSubordinates 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 testSearchnumSubordinatesAttrInNonMatchingFilter(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(numSubordinates=wrong)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("numSubordinates"); |
| | | |
| | | 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 numSubordinates 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 testSearchnumSubordinatesAttrRealAttrsOnly(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(objectClass=*)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("numSubordinates"); |
| | | |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates 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 testSearchnumSubordinatesAttrVirtualAttrsOnly(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(objectClass=*)"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("numSubordinates"); |
| | | |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates attribute is included when that attribute is specifically |
| | | * requested and the numSubordinates attribute is used in the search filter with a |
| | | * greater than value. |
| | | * |
| | | * @param entryDN The DN of the entry to retrieve and verify. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test |
| | | public void testSearchnumSubordinatesAttrInGTEFilter(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(numSubordinates>=" + count + ")"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("numSubordinates"); |
| | | |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | /** |
| | | * Performs an internal search to retrieve the specified entry, ensuring that |
| | | * the numSubordinates attribute is included when that attribute is specifically |
| | | * requested and the numSubordinates attribute is used in the search filter with a |
| | | * less than value. |
| | | * |
| | | * @param entryDN The DN of the entry to retrieve and verify. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test |
| | | public void testSearchnumSubordinatesAttrInLTEFilter(DN entryDN, int count) |
| | | throws Exception |
| | | { |
| | | SearchFilter filter = |
| | | SearchFilter.createFilterFromString("(numSubordinates<=" + count + ")"); |
| | | LinkedHashSet<String> attrList = new LinkedHashSet<String>(1); |
| | | attrList.add("numSubordinates"); |
| | | |
| | | 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(numSubordinatesType)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Tests the {@code isMultiValued} method. |
| | | */ |
| | | @Test() |
| | | public void testIsMultiValued() |
| | | { |
| | | NumSubordinatesVirtualAttributeProvider provider = |
| | | new NumSubordinatesVirtualAttributeProvider(); |
| | | assertFalse(provider.isMultiValued()); |
| | | } |
| | | } |