| | |
| | | </condition> |
| | | |
| | | <!-- This sets org.opends.test.debug.target if and only if its's not |
| | | alreadly set. --> |
| | | already set. --> |
| | | <condition property="org.opends.test.debug.target" |
| | | value="org.opends.server:level=warning,category=caught|data|database_access|message|protocol"> |
| | | <not> |
| | |
| | | attributeTypes: ( 1.3.6.1.4.1.26027.1.1.380 NAME 'ds-sync-conflict' |
| | | SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 |
| | | USAGE directoryOperation X-ORIGIN 'OpenDS Directory Server' ) |
| | | attributeTypes: ( 1.3.6.1.4.1.26027.1.1.382 |
| | | NAME 'ds-cfg-index-substring-length' |
| | | SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE |
| | | X-ORIGIN 'OpenDS Directory Server' ) |
| | | objectClasses: ( 1.3.6.1.4.1.26027.1.2.1 |
| | | NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL |
| | | MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled ) |
| | |
| | | <ldap:superior>ds-cfg-backend</ldap:superior> |
| | | </ldap:object-class> |
| | | </adm:profile> |
| | | <adm:relation name="je-index"> |
| | | <adm:one-to-many /> |
| | | <adm:profile name="ldap"> |
| | | <ldap:rdn-sequence> |
| | | cn=Index |
| | | </ldap:rdn-sequence> |
| | | <ldap:naming-attribute> |
| | | ds-cfg-index-attribute |
| | | </ldap:naming-attribute> |
| | | </adm:profile> |
| | | </adm:relation> |
| | | <adm:property-override name="backend-class"> |
| | | <adm:default-behavior> |
| | | <adm:defined> |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!-- |
| | | ! CDDL HEADER START |
| | | ! |
| | | ! The contents of this file are subject to the terms of the |
| | | ! Common Development and Distribution License, Version 1.0 only |
| | | ! (the "License"). You may not use this file except in compliance |
| | | ! with the License. |
| | | ! |
| | | ! You can obtain a copy of the license at |
| | | ! trunk/opends/resource/legal-notices/OpenDS.LICENSE |
| | | ! or https://OpenDS.dev.java.net/OpenDS.LICENSE. |
| | | ! See the License for the specific language governing permissions |
| | | ! and limitations under the License. |
| | | ! |
| | | ! When distributing Covered Code, include this CDDL HEADER in each |
| | | ! file and include the License file at |
| | | ! trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, |
| | | ! add the following below this CDDL HEADER, with the fields enclosed |
| | | ! by brackets "[]" replaced with your own identifying information: |
| | | ! Portions Copyright [yyyy] [name of copyright owner] |
| | | ! |
| | | ! CDDL HEADER END |
| | | ! |
| | | ! |
| | | ! Portions Copyright 2007 Sun Microsystems, Inc. |
| | | ! --> |
| | | |
| | | <adm:managed-object name="je-index" plural-name="je-indexes" |
| | | package="org.opends.server.admin.std" |
| | | xmlns:adm="http://www.opends.org/admin" |
| | | xmlns:ldap="http://www.opends.org/admin-ldap"> |
| | | <adm:synopsis> |
| | | The <adm:user-friendly-plural-name/> are used to store information |
| | | that makes it possible to locate entries very quickly when processing |
| | | search operations. |
| | | </adm:synopsis> |
| | | <adm:description> |
| | | Indexing is performed on a per-attribute level and different types |
| | | of indexing may be performed for different kinds of attributes based |
| | | on how they are expected to be accessed during search operations. |
| | | </adm:description> |
| | | <adm:tag name="database" /> |
| | | <adm:profile name="ldap"> |
| | | <ldap:object-class> |
| | | <ldap:oid>1.3.6.1.4.1.26027.1.2.8</ldap:oid> |
| | | <ldap:name>ds-cfg-je-index</ldap:name> |
| | | <ldap:superior>top</ldap:superior> |
| | | </ldap:object-class> |
| | | </adm:profile> |
| | | <adm:property name="index-attribute" |
| | | mandatory="true" |
| | | multi-valued="false" |
| | | read-only="true"> |
| | | <adm:synopsis> |
| | | This specifies the name of the attribute for which the index is |
| | | to be maintained. |
| | | </adm:synopsis> |
| | | <adm:syntax> |
| | | <adm:attribute-type/> |
| | | </adm:syntax> |
| | | <adm:profile name="ldap"> |
| | | <ldap:attribute> |
| | | <ldap:oid>1.3.6.1.4.1.26027.1.1.40</ldap:oid> |
| | | <ldap:name>ds-cfg-index-attribute</ldap:name> |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | <adm:property name="index-entry-limit" |
| | | mandatory="false" |
| | | multi-valued="false"> |
| | | <adm:synopsis> |
| | | This specifies the maximum number of entries that will be allowed |
| | | to match a given index key before that particular index key is no |
| | | longer maintained. |
| | | </adm:synopsis> |
| | | <adm:description> |
| | | This is analogous to the ALL IDs threshold in the Sun Java System |
| | | Directory Server. If this is specified, it will override the JE |
| | | backend-wide configuration. If any index keys have already |
| | | reached this limit, indexes will need to be rebuilt before they |
| | | will be allowed to use the new limit. For no limit, use 0 for the |
| | | value. |
| | | </adm:description> |
| | | <adm:requires-admin-action> |
| | | <adm:other> |
| | | <adm:synopsis> |
| | | If any index keys have already reached this limit, indexes |
| | | will need to be rebuilt before they will be allowed to use |
| | | the new limit. |
| | | </adm:synopsis> |
| | | </adm:other> |
| | | </adm:requires-admin-action> |
| | | <adm:default-behavior> |
| | | <adm:alias> |
| | | <adm:synopsis> |
| | | The index entry limit specified in the JE backend-wide |
| | | configuration will be used. |
| | | </adm:synopsis> |
| | | </adm:alias> |
| | | </adm:default-behavior> |
| | | <adm:syntax> |
| | | <adm:integer lower-limit="0" upper-limit="2147483647"> |
| | | <adm:unit-synopsis> |
| | | Number of entries |
| | | </adm:unit-synopsis> |
| | | </adm:integer> |
| | | </adm:syntax> |
| | | <adm:profile name="ldap"> |
| | | <ldap:attribute> |
| | | <ldap:oid>1.3.6.1.4.1.26027.1.1.41</ldap:oid> |
| | | <ldap:name>ds-cfg-index-entry-limit</ldap:name> |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | <adm:property name="index-type" |
| | | mandatory="true" |
| | | multi-valued="true"> |
| | | <adm:synopsis> |
| | | This specifies the type(s) of indexing that should be performed for |
| | | the associated attribute. |
| | | </adm:synopsis> |
| | | <adm:description> |
| | | For equality, presence, and substring index types, the associated |
| | | attribute type must have a corrsponding matching rule. |
| | | </adm:description> |
| | | <adm:requires-admin-action> |
| | | <adm:other> |
| | | <adm:synopsis> |
| | | If any new index types are added for an attribute and there |
| | | are values for that attribute which already exist in the |
| | | database, then the index will need to be rebuilt before it |
| | | will be accurate. |
| | | </adm:synopsis> |
| | | </adm:other> |
| | | </adm:requires-admin-action> |
| | | <adm:syntax> |
| | | <adm:enumeration> |
| | | <adm:value name="equality"> |
| | | <adm:synopsis> |
| | | This index type will be used to help improve the efficiency of |
| | | searches using equality search filters. |
| | | </adm:synopsis> |
| | | </adm:value> |
| | | <adm:value name="ordering"> |
| | | <adm:synopsis> |
| | | This index type will be used to help improve the efficiency of |
| | | searches using "greater than or equal to" or "less then or equal |
| | | to" search filters. |
| | | </adm:synopsis> |
| | | </adm:value> |
| | | <adm:value name="presence"> |
| | | <adm:synopsis> |
| | | This index type will be used to help improve the efficiency of |
| | | searches using the presence search filters. |
| | | </adm:synopsis> |
| | | </adm:value> |
| | | <adm:value name="substring"> |
| | | <adm:synopsis> |
| | | This index type will be used to help improve the efficiency of |
| | | searches using substring search filters. |
| | | </adm:synopsis> |
| | | </adm:value> |
| | | <adm:value name="approximate"> |
| | | <adm:synopsis> |
| | | This index type will be used to help improve the efficiency of |
| | | searches using approximate matching search filters. |
| | | </adm:synopsis> |
| | | </adm:value> |
| | | </adm:enumeration> |
| | | </adm:syntax> |
| | | <adm:profile name="ldap"> |
| | | <ldap:attribute> |
| | | <ldap:oid>1.3.6.1.4.1.26027.1.1.42</ldap:oid> |
| | | <ldap:name>ds-cfg-index-type</ldap:name> |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | <adm:property name="index-substring-length" |
| | | mandatory="false" |
| | | multi-valued="false"> |
| | | <adm:synopsis> |
| | | The length of substrings in a substring index. |
| | | </adm:synopsis> |
| | | <adm:requires-admin-action> |
| | | <adm:other> |
| | | <adm:synopsis> |
| | | The index will need to be rebuilt before it will reflect |
| | | the new value. |
| | | </adm:synopsis> |
| | | </adm:other> |
| | | </adm:requires-admin-action> |
| | | <adm:default-behavior> |
| | | <adm:defined> |
| | | <adm:value>6</adm:value> |
| | | </adm:defined> |
| | | </adm:default-behavior> |
| | | <adm:syntax> |
| | | <adm:integer lower-limit="3" /> |
| | | </adm:syntax> |
| | | <adm:profile name="ldap"> |
| | | <ldap:attribute> |
| | | <ldap:oid>1.3.6.1.4.1.26027.1.1.380</ldap:oid> |
| | | <ldap:name>ds-cfg-ds-cfg-index-substring-length</ldap:name> |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | </adm:managed-object> |
| | |
| | | } |
| | | catch(IOException ie) |
| | | { |
| | | System.out.println("WARNING: An error occured while loading EMMA " + |
| | | System.out.println("WARNING: An error occurred while loading EMMA " + |
| | | "data. Report will not contain any coverage information."); |
| | | } |
| | | |
| | |
| | | } |
| | | catch(IOException ie) |
| | | { |
| | | System.out.println("ERROR: An error occured while processing diff output: " + ie.toString() + " Quitting..."); |
| | | System.out.println("ERROR: An error occurred while processing diff output: " + ie.toString() + " Quitting..."); |
| | | return; |
| | | } |
| | | System.out.println("Coverage diff completed in " + (System.currentTimeMillis() - start) + " ms."); |
| | |
| | | |
| | | if(workingCopyFlag == null || otherCopyFlag == null) |
| | | { |
| | | throw new IOException("Error occured while parsing diff output"); |
| | | throw new IOException("Error occurred while parsing diff output"); |
| | | } |
| | | else |
| | | { |
| | |
| | | * for backing up an installation during an upgrade. |
| | | * @return File representing a new backup directory. The directory |
| | | * can be assumed to exist if this method returns cleanly. |
| | | * @throws IOException if an error occured creating the directory. |
| | | * @throws IOException if an error occurred creating the directory. |
| | | */ |
| | | public File createHistoryBackupDirectory() throws IOException { |
| | | File backupDirectory = |
| | |
| | | |
| | | |
| | | /** |
| | | * Determine whether a trace setting is alreadly defined for a |
| | | * Determine whether a trace setting is already defined for a |
| | | * particular scope. |
| | | * |
| | | * @param scope The scope for which to make the determination. |
| | |
| | | * @param key The key to dump. |
| | | * @param data The data to dump. |
| | | * @param stackTrace The stack trace at the time the access |
| | | * occured or null if its not available. |
| | | * occurred or null if its not available. |
| | | */ |
| | | public abstract void traceJEAccess(LogLevel level, |
| | | TraceSettings settings, |
| | |
| | | private ApproximateMatchingRule approximateRule; |
| | | |
| | | /** |
| | | * The attribute index configuration for which this instance will |
| | | * The attribute type for which this instance will |
| | | * generate index keys. |
| | | */ |
| | | private IndexConfig indexConfig; |
| | | private AttributeType attributeType; |
| | | |
| | | /** |
| | | * Create a new attribute approximate indexer for the given index |
| | | * configuration. |
| | | * @param indexConfig The index configuration for which an indexer is |
| | | * @param attributeType The attribute type for which an indexer is |
| | | * required. |
| | | */ |
| | | public ApproximateIndexer(IndexConfig indexConfig) |
| | | public ApproximateIndexer(AttributeType attributeType) |
| | | { |
| | | this.indexConfig = indexConfig; |
| | | approximateRule = |
| | | indexConfig.getAttributeType().getApproximateMatchingRule(); |
| | | this.attributeType = attributeType; |
| | | this.approximateRule = attributeType.getApproximateMatchingRule(); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public String toString() |
| | | { |
| | | return indexConfig.getAttributeType().getNameOrOID() + ".approximate"; |
| | | return attributeType.getNameOrOID() + ".approximate"; |
| | | } |
| | | |
| | | |
| | |
| | | Set<ASN1OctetString> keys) |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.getAttribute(indexConfig.getAttributeType()); |
| | | entry.getAttribute(attributeType); |
| | | if (attrList != null) |
| | | { |
| | | indexAttribute(attrList, keys); |
| | |
| | | { |
| | | List<Attribute> attrList; |
| | | |
| | | attrList = oldEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = oldEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> oldSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, oldSet); |
| | | |
| | | attrList = newEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = newEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> newSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, newSet); |
| | | |
| | |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import com.sleepycat.je.DatabaseConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.*; |
| | | |
| | | import org.opends.server.api.SubstringMatchingRule; |
| | | import org.opends.server.api.OrderingMatchingRule; |
| | | import org.opends.server.api.ApproximateMatchingRule; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.SearchFilter; |
| | | |
| | | 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 org.opends.server.admin.std.server.JEIndexCfg; |
| | | import org.opends.server.admin.std.meta.JEIndexCfgDefn; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.config.ConfigException; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | /** |
| | | * Class representing an attribute index. |
| | |
| | | * then we would not need a separate ordering index. |
| | | */ |
| | | public class AttributeIndex |
| | | implements ConfigurationChangeListener<JEIndexCfg> |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | |
| | | /** |
| | | * The entryContainer in which this attribute index resides. |
| | | */ |
| | | EntryContainer entryContainer; |
| | | private EntryContainer entryContainer; |
| | | |
| | | private Environment env; |
| | | |
| | | /** |
| | | * The attribute index configuration. |
| | | */ |
| | | IndexConfig indexConfig; |
| | | private JEIndexCfg indexConfig; |
| | | |
| | | /** |
| | | * The index database for attribute equality. |
| | |
| | | */ |
| | | Index approximateIndex = null; |
| | | |
| | | private State state; |
| | | |
| | | private int cursorEntryLimit = 100000; |
| | | |
| | | private int backendIndexEntryLimit = 4000; |
| | | |
| | | /** |
| | | * Create a new attribute index object. |
| | | * @param entryContainer The entryContainer of this attribute index. |
| | | * @param state The state database to persist index state info. |
| | | * @param env The JE environment handle. |
| | | * @param indexConfig The attribute index configuration. |
| | | * @param backendIndexEntryLimit The backend index entry limit to use |
| | | * if none is specified for this attribute index. |
| | | * @throws DatabaseException if a JE database error occurs. |
| | | * @throws ConfigException if a configuration related error occurs. |
| | | */ |
| | | public AttributeIndex(EntryContainer entryContainer, IndexConfig indexConfig) |
| | | public AttributeIndex(JEIndexCfg indexConfig, State state, |
| | | int backendIndexEntryLimit, |
| | | Environment env, |
| | | EntryContainer entryContainer) |
| | | throws DatabaseException, ConfigException |
| | | { |
| | | this.entryContainer = entryContainer; |
| | | this.env = env; |
| | | this.indexConfig = indexConfig; |
| | | this.backendIndexEntryLimit = backendIndexEntryLimit; |
| | | this.state = state; |
| | | |
| | | AttributeType attrType = indexConfig.getAttributeType(); |
| | | AttributeType attrType = indexConfig.getIndexAttribute(); |
| | | String name = attrType.getNameOrOID(); |
| | | int indexEntryLimit = backendIndexEntryLimit; |
| | | |
| | | if (indexConfig.isEqualityIndex()) |
| | | if(indexConfig.getIndexEntryLimit() != null) |
| | | { |
| | | Indexer equalityIndexer = new EqualityIndexer(indexConfig); |
| | | this.equalityIndex = new Index(this.entryContainer, name + ".equality", |
| | | indexEntryLimit = indexConfig.getIndexEntryLimit(); |
| | | } |
| | | |
| | | if (indexConfig.getIndexType().contains(JEIndexCfgDefn.IndexType.EQUALITY)) |
| | | { |
| | | if (attrType.getEqualityMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "equality"); |
| | | throw new ConfigException(messageID, message); |
| | | } |
| | | |
| | | Indexer equalityIndexer = new EqualityIndexer(attrType); |
| | | this.equalityIndex = new Index(name + ".equality", |
| | | equalityIndexer, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | } |
| | | |
| | | if (indexConfig.isPresenceIndex()) |
| | | if (indexConfig.getIndexType().contains(JEIndexCfgDefn.IndexType.PRESENCE)) |
| | | { |
| | | Indexer presenceIndexer = new PresenceIndexer(indexConfig); |
| | | this.presenceIndex = new Index(this.entryContainer, name + ".presence", |
| | | Indexer presenceIndexer = new PresenceIndexer(attrType); |
| | | this.presenceIndex = new Index(name + ".presence", |
| | | presenceIndexer, |
| | | indexConfig.getPresenceEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | } |
| | | |
| | | if (indexConfig.isSubstringIndex()) |
| | | if (indexConfig.getIndexType().contains(JEIndexCfgDefn.IndexType.SUBSTRING)) |
| | | { |
| | | Indexer substringIndexer = new SubstringIndexer(indexConfig); |
| | | this.substringIndex = new Index(this.entryContainer, name + ".substring", |
| | | if (attrType.getSubstringMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "substring"); |
| | | throw new ConfigException(messageID, message); |
| | | } |
| | | |
| | | Indexer substringIndexer = new SubstringIndexer(attrType, |
| | | indexConfig.getIndexSubstringLength()); |
| | | this.substringIndex = new Index(name + ".substring", |
| | | substringIndexer, |
| | | indexConfig.getSubstringEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | } |
| | | |
| | | if (indexConfig.isOrderingIndex()) |
| | | if (indexConfig.getIndexType().contains(JEIndexCfgDefn.IndexType.ORDERING)) |
| | | { |
| | | Indexer orderingIndexer = new OrderingIndexer(indexConfig); |
| | | this.orderingIndex = new Index(this.entryContainer, name + ".ordering", |
| | | if (attrType.getOrderingMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "ordering"); |
| | | throw new ConfigException(messageID, message); |
| | | } |
| | | |
| | | Indexer orderingIndexer = new OrderingIndexer(attrType); |
| | | this.orderingIndex = new Index(name + ".ordering", |
| | | orderingIndexer, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | } |
| | | if (indexConfig.isApproximateIndex()) |
| | | if (indexConfig.getIndexType().contains( |
| | | JEIndexCfgDefn.IndexType.APPROXIMATE)) |
| | | { |
| | | Indexer approximateIndexer = new ApproximateIndexer(indexConfig); |
| | | this.approximateIndex = new Index(this.entryContainer, |
| | | name + ".approximate", |
| | | if (attrType.getApproximateMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "approximate"); |
| | | throw new ConfigException(messageID, message); |
| | | } |
| | | |
| | | Indexer approximateIndexer = new ApproximateIndexer(attrType); |
| | | this.approximateIndex = new Index(name + ".approximate", |
| | | approximateIndexer, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | } |
| | | |
| | | this.indexConfig.addChangeListener(this); |
| | | } |
| | | |
| | | /** |
| | | * Open the attribute index. |
| | | * |
| | | * @param dbConfig The JE Database Config that will be used to open |
| | | * underlying JE databases. |
| | | * |
| | | * @throws DatabaseException If an error occurs opening the underlying |
| | | * databases. |
| | | * @throws DatabaseException if a JE database error occurs while |
| | | * openning the index. |
| | | */ |
| | | public void open(DatabaseConfig dbConfig) throws DatabaseException |
| | | public void open() throws DatabaseException |
| | | { |
| | | if (equalityIndex != null) |
| | | { |
| | | equalityIndex.open(dbConfig); |
| | | equalityIndex.open(); |
| | | } |
| | | |
| | | if (presenceIndex != null) |
| | | { |
| | | presenceIndex.open(dbConfig); |
| | | presenceIndex.open(); |
| | | } |
| | | |
| | | if (substringIndex != null) |
| | | { |
| | | substringIndex.open(dbConfig); |
| | | substringIndex.open(); |
| | | } |
| | | |
| | | if (orderingIndex != null) |
| | | { |
| | | orderingIndex.open(dbConfig); |
| | | orderingIndex.open(); |
| | | } |
| | | |
| | | if (approximateIndex != null) |
| | | { |
| | | approximateIndex.open(dbConfig); |
| | | approximateIndex.open(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Close the attribute index. |
| | | * |
| | | * @throws DatabaseException if a JE database error occurs while |
| | | * openning the index. |
| | | */ |
| | | public void close() |
| | | public void close() throws DatabaseException |
| | | { |
| | | if (equalityIndex != null) |
| | | { |
| | | equalityIndex.close(); |
| | | } |
| | | |
| | | if (presenceIndex != null) |
| | | { |
| | | presenceIndex.close(); |
| | | } |
| | | |
| | | if (substringIndex != null) |
| | | { |
| | | substringIndex.close(); |
| | | } |
| | | |
| | | if (orderingIndex != null) |
| | | { |
| | | orderingIndex.close(); |
| | | } |
| | | |
| | | if (approximateIndex != null) |
| | | { |
| | | approximateIndex.close(); |
| | | } |
| | | |
| | | indexConfig.removeChangeListener(this); |
| | | // The entryContainer is responsible for closing the JE databases. |
| | | } |
| | | |
| | |
| | | */ |
| | | public AttributeType getAttributeType() |
| | | { |
| | | return indexConfig.getAttributeType(); |
| | | return indexConfig.getIndexAttribute(); |
| | | } |
| | | |
| | | //TODO: Make all modify/add methods return success boolean |
| | | /** |
| | | * Get the JE index configuration used by this index. |
| | | * @return The configuration in effect. |
| | | */ |
| | | public JEIndexCfg getConfiguration() |
| | | { |
| | | return indexConfig; |
| | | } |
| | | |
| | | /** |
| | | * Update the attribute index for a new entry. |
| | | * |
| | |
| | | * @param entryID The entry ID. |
| | | * @param entry The contents of the new entry. |
| | | * @return True if all the index keys for the entry are added. False if the |
| | | * entry ID alreadly exists for some keys. |
| | | * entry ID already exists for some keys. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | |
| | | // concurrent writers. |
| | | Set<ByteString> set = new HashSet<ByteString>(); |
| | | |
| | | int substrLength = indexConfig.getSubstringLength(); |
| | | int substrLength = indexConfig.getIndexSubstringLength(); |
| | | byte[] keyBytes; |
| | | |
| | | // Example: The value is ABCDE and the substring length is 3. |
| | |
| | | */ |
| | | private EntryIDSet matchSubstring(byte[] bytes) |
| | | { |
| | | int substrLength = indexConfig.getSubstringLength(); |
| | | int substrLength = indexConfig.getIndexSubstringLength(); |
| | | |
| | | // There are two cases, depending on whether the user-provided |
| | | // substring is smaller than the configured index substring length or not. |
| | |
| | | */ |
| | | public EntryIDSet evaluateEqualityFilter(SearchFilter equalityFilter) |
| | | { |
| | | if (!indexConfig.isEqualityIndex()) |
| | | if (equalityIndex == null) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | |
| | | */ |
| | | public EntryIDSet evaluatePresenceFilter(SearchFilter filter) |
| | | { |
| | | if (!indexConfig.isPresenceIndex()) |
| | | if (presenceIndex == null) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | |
| | | */ |
| | | public EntryIDSet evaluateGreaterOrEqualFilter(SearchFilter filter) |
| | | { |
| | | if (!indexConfig.isOrderingIndex() || orderingIndex == null) |
| | | if (orderingIndex == null) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | |
| | | */ |
| | | public EntryIDSet evaluateLessOrEqualFilter(SearchFilter filter) |
| | | { |
| | | if (!indexConfig.isOrderingIndex() || orderingIndex == null) |
| | | if (orderingIndex == null) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | |
| | | if (filter.getSubInitialElement() != null) |
| | | { |
| | | // Use the equality index for initial substrings if possible. |
| | | if (indexConfig.isEqualityIndex()) |
| | | if (equalityIndex != null) |
| | | { |
| | | ByteString normValue = |
| | | matchRule.normalizeSubstring(filter.getSubInitialElement()); |
| | |
| | | } |
| | | } |
| | | |
| | | if (!indexConfig.isSubstringIndex()) |
| | | if (substringIndex == null) |
| | | { |
| | | return results; |
| | | } |
| | |
| | | */ |
| | | public EntryIDSet evaluateApproximateFilter(SearchFilter approximateFilter) |
| | | { |
| | | if (!indexConfig.isApproximateIndex()) |
| | | if (approximateIndex == null) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Remove the index from disk. The index must not be open. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void removeIndex() throws DatabaseException |
| | | { |
| | | AttributeType attrType = indexConfig.getAttributeType(); |
| | | String name = attrType.getNameOrOID(); |
| | | if (indexConfig.isEqualityIndex()) |
| | | { |
| | | entryContainer.removeDatabase(name + ".equality"); |
| | | } |
| | | if (indexConfig.isPresenceIndex()) |
| | | { |
| | | entryContainer.removeDatabase(name + ".presence"); |
| | | } |
| | | if (indexConfig.isSubstringIndex()) |
| | | { |
| | | entryContainer.removeDatabase(name + ".substring"); |
| | | } |
| | | if (indexConfig.isOrderingIndex()) |
| | | { |
| | | entryContainer.removeDatabase(name + ".ordering"); |
| | | } |
| | | if (indexConfig.isApproximateIndex()) |
| | | { |
| | | entryContainer.removeDatabase(name + ".approximate"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Return the number of values that have exceeded the entry limit since this |
| | | * object was created. |
| | |
| | | } |
| | | |
| | | /** |
| | | * Removes all records related to this attribute index. |
| | | * @param txn A JE database transaction to be used during the clear operation |
| | | * or null if not required. Using transactions increases the chance |
| | | * of lock contention. |
| | | * @return The number of records removed. |
| | | * @throws DatabaseException If an error occurs while cleaning the database. |
| | | * Get a list of the databases opened by this attribute index. |
| | | * @param dbList A list of database containers. |
| | | */ |
| | | public long clear(Transaction txn) throws DatabaseException |
| | | public void listDatabases(List<DatabaseContainer> dbList) |
| | | { |
| | | long deletedCount = 0; |
| | | |
| | | if (equalityIndex != null) |
| | | { |
| | | deletedCount += equalityIndex.clear(txn); |
| | | dbList.add(equalityIndex); |
| | | } |
| | | |
| | | if (presenceIndex != null) |
| | | { |
| | | deletedCount += presenceIndex.clear(txn); |
| | | dbList.add(presenceIndex); |
| | | } |
| | | |
| | | if (substringIndex != null) |
| | | { |
| | | deletedCount += substringIndex.clear(txn); |
| | | dbList.add(substringIndex); |
| | | } |
| | | |
| | | if (orderingIndex != null) |
| | | { |
| | | deletedCount += orderingIndex.clear(txn); |
| | | dbList.add(orderingIndex); |
| | | } |
| | | |
| | | if (approximateIndex != null) |
| | | { |
| | | deletedCount += approximateIndex.clear(txn); |
| | | dbList.add(approximateIndex); |
| | | } |
| | | |
| | | return deletedCount; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get a string representation of this object. |
| | | * @return return A string representation of this object. |
| | | */ |
| | | public String toString() |
| | | { |
| | | return indexConfig.getAttributeType().getNameOrOID(); |
| | | return getName(); |
| | | } |
| | | |
| | | /** |
| | | * Set the index entry limit used by the backend using this attribute index. |
| | | * This index will use the backend entry limit only if there is not one |
| | | * specified for this index. |
| | | * |
| | | * @param backendIndexEntryLimit The backend index entry limit. |
| | | * @return True if a rebuild is required or false otherwise. |
| | | */ |
| | | public synchronized boolean setBackendIndexEntryLimit( |
| | | int backendIndexEntryLimit) |
| | | { |
| | | // Only update if there is no limit specified for this index. |
| | | boolean rebuildRequired = false; |
| | | if(indexConfig.getIndexEntryLimit() == null) |
| | | { |
| | | if(equalityIndex != null) |
| | | { |
| | | rebuildRequired |= |
| | | equalityIndex.setIndexEntryLimit(backendIndexEntryLimit); |
| | | } |
| | | |
| | | if(presenceIndex != null) |
| | | { |
| | | rebuildRequired |= |
| | | presenceIndex.setIndexEntryLimit(backendIndexEntryLimit); |
| | | } |
| | | |
| | | if(substringIndex != null) |
| | | { |
| | | rebuildRequired |= |
| | | substringIndex.setIndexEntryLimit(backendIndexEntryLimit); |
| | | } |
| | | |
| | | if(orderingIndex != null) |
| | | { |
| | | rebuildRequired |= |
| | | orderingIndex.setIndexEntryLimit(backendIndexEntryLimit); |
| | | } |
| | | |
| | | if(approximateIndex != null) |
| | | { |
| | | rebuildRequired |= |
| | | approximateIndex.setIndexEntryLimit(backendIndexEntryLimit); |
| | | } |
| | | } |
| | | |
| | | this.backendIndexEntryLimit = backendIndexEntryLimit; |
| | | |
| | | return rebuildRequired; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized boolean isConfigurationChangeAcceptable( |
| | | JEIndexCfg cfg, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | AttributeType attrType = cfg.getIndexAttribute(); |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.EQUALITY)) |
| | | { |
| | | if (equalityIndex == null && attrType.getEqualityMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "equality"); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.SUBSTRING)) |
| | | { |
| | | if (substringIndex == null && attrType.getSubstringMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "substring"); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | |
| | | } |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.ORDERING)) |
| | | { |
| | | if (orderingIndex == null && attrType.getOrderingMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "ordering"); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | } |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.APPROXIMATE)) |
| | | { |
| | | if (approximateIndex == null && |
| | | attrType.getApproximateMatchingRule() == null) |
| | | { |
| | | int messageID = MSGID_CONFIG_INDEX_TYPE_NEEDS_MATCHING_RULE; |
| | | String message = getMessage(messageID, attrType, "approximate"); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized ConfigChangeResult applyConfigurationChange( |
| | | JEIndexCfg cfg) |
| | | { |
| | | ConfigChangeResult ccr; |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | try |
| | | { |
| | | AttributeType attrType = cfg.getIndexAttribute(); |
| | | String name = attrType.getNameOrOID(); |
| | | int indexEntryLimit = backendIndexEntryLimit; |
| | | |
| | | if(cfg.getIndexEntryLimit() != null) |
| | | { |
| | | indexEntryLimit = cfg.getIndexEntryLimit(); |
| | | } |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.EQUALITY)) |
| | | { |
| | | if (equalityIndex == null) |
| | | { |
| | | // Adding equality index |
| | | Indexer equalityIndexer = new EqualityIndexer(attrType); |
| | | equalityIndex = new Index(name + ".equality", |
| | | equalityIndexer, |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | equalityIndex.open(); |
| | | |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | messages.add(getMessage(msgID, name + ".equality")); |
| | | |
| | | } |
| | | else |
| | | { |
| | | // already exists. Just update index entry limit. |
| | | if(this.equalityIndex.setIndexEntryLimit(indexEntryLimit)) |
| | | { |
| | | |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, name + ".equality"); |
| | | messages.add(message); |
| | | this.equalityIndex.setIndexEntryLimit(indexEntryLimit); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (equalityIndex != null) |
| | | { |
| | | entryContainer.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | entryContainer.removeDatabase(equalityIndex); |
| | | equalityIndex = null; |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(de)); |
| | | ccr = new ConfigChangeResult( |
| | | DirectoryServer.getServerErrorResultCode(), false, messages); |
| | | return ccr; |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.PRESENCE)) |
| | | { |
| | | if(presenceIndex == null) |
| | | { |
| | | Indexer presenceIndexer = new PresenceIndexer(attrType); |
| | | presenceIndex = new Index(name + ".presence", |
| | | presenceIndexer, |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | presenceIndex.open(); |
| | | |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | messages.add(getMessage(msgID, name + ".presence")); |
| | | } |
| | | else |
| | | { |
| | | // already exists. Just update index entry limit. |
| | | if(this.presenceIndex.setIndexEntryLimit(indexEntryLimit)) |
| | | { |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, name + ".presence"); |
| | | messages.add(message); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (presenceIndex != null) |
| | | { |
| | | entryContainer.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | entryContainer.removeDatabase(presenceIndex); |
| | | presenceIndex = null; |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(de)); |
| | | ccr = new ConfigChangeResult( |
| | | DirectoryServer.getServerErrorResultCode(), false, messages); |
| | | return ccr; |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.SUBSTRING)) |
| | | { |
| | | if(substringIndex == null) |
| | | { |
| | | Indexer substringIndexer = new SubstringIndexer( |
| | | attrType, cfg.getIndexSubstringLength()); |
| | | substringIndex = new Index(name + ".substring", |
| | | substringIndexer, |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | substringIndex.open(); |
| | | |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | messages.add(getMessage(msgID, name + ".substring")); |
| | | } |
| | | else |
| | | { |
| | | // already exists. Just update index entry limit. |
| | | if(this.substringIndex.setIndexEntryLimit(indexEntryLimit)) |
| | | { |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, name + ".substring"); |
| | | messages.add(message); |
| | | } |
| | | |
| | | if(indexConfig.getIndexSubstringLength() != |
| | | cfg.getIndexSubstringLength()) |
| | | { |
| | | Indexer substringIndexer = new SubstringIndexer( |
| | | attrType, cfg.getIndexSubstringLength()); |
| | | this.substringIndex.setIndexer(substringIndexer); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (substringIndex != null) |
| | | { |
| | | entryContainer.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | entryContainer.removeDatabase(substringIndex); |
| | | substringIndex = null; |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(de)); |
| | | ccr = new ConfigChangeResult( |
| | | DirectoryServer.getServerErrorResultCode(), false, messages); |
| | | return ccr; |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.ORDERING)) |
| | | { |
| | | if(orderingIndex == null) |
| | | { |
| | | Indexer orderingIndexer = new OrderingIndexer(attrType); |
| | | orderingIndex = new Index(name + ".ordering", |
| | | orderingIndexer, |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | orderingIndex.open(); |
| | | |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | messages.add(getMessage(msgID, name + ".ordering")); |
| | | } |
| | | else |
| | | { |
| | | // already exists. Just update index entry limit. |
| | | if(this.orderingIndex.setIndexEntryLimit(indexEntryLimit)) |
| | | { |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, name + ".ordering"); |
| | | messages.add(message); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (orderingIndex != null) |
| | | { |
| | | entryContainer.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | entryContainer.removeDatabase(orderingIndex); |
| | | orderingIndex = null; |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(de)); |
| | | ccr = new ConfigChangeResult( |
| | | DirectoryServer.getServerErrorResultCode(), false, messages); |
| | | return ccr; |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (cfg.getIndexType().contains(JEIndexCfgDefn.IndexType.APPROXIMATE)) |
| | | { |
| | | if(approximateIndex == null) |
| | | { |
| | | Indexer approximateIndexer = new ApproximateIndexer(attrType); |
| | | approximateIndex = new Index(name + ".approximate", |
| | | approximateIndexer, |
| | | state, |
| | | indexEntryLimit, |
| | | cursorEntryLimit, |
| | | env, |
| | | entryContainer); |
| | | approximateIndex.open(); |
| | | |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | messages.add(getMessage(msgID, name + ".approximate")); |
| | | } |
| | | else |
| | | { |
| | | // already exists. Just update index entry limit. |
| | | if(this.approximateIndex.setIndexEntryLimit(indexEntryLimit)) |
| | | { |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, name + ".approximate"); |
| | | messages.add(message); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (approximateIndex != null) |
| | | { |
| | | entryContainer.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | entryContainer.removeDatabase(approximateIndex); |
| | | approximateIndex = null; |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(de)); |
| | | ccr = new ConfigChangeResult( |
| | | DirectoryServer.getServerErrorResultCode(), false, messages); |
| | | return ccr; |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | indexConfig = cfg; |
| | | |
| | | return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired, |
| | | messages); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(e)); |
| | | ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(), |
| | | adminActionRequired, |
| | | messages); |
| | | return ccr; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Set the index trust state. |
| | | * @param txn A database transaction, or null if none is required. |
| | | * @param trusted True if this index should be trusted or false |
| | | * otherwise. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public synchronized void setTrusted(Transaction txn, boolean trusted) |
| | | throws DatabaseException |
| | | { |
| | | if (equalityIndex != null) |
| | | { |
| | | equalityIndex.setTrusted(txn, trusted); |
| | | } |
| | | |
| | | if (presenceIndex != null) |
| | | { |
| | | presenceIndex.setTrusted(txn, trusted); |
| | | } |
| | | |
| | | if (substringIndex != null) |
| | | { |
| | | substringIndex.setTrusted(txn, trusted); |
| | | } |
| | | |
| | | if (orderingIndex != null) |
| | | { |
| | | orderingIndex.setTrusted(txn, trusted); |
| | | } |
| | | |
| | | if (approximateIndex != null) |
| | | { |
| | | approximateIndex.setTrusted(txn, trusted); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Set the rebuild status of this index. |
| | | * @param rebuildRunning True if a rebuild process on this index |
| | | * is running or False otherwise. |
| | | */ |
| | | public synchronized void setRebuildStatus(boolean rebuildRunning) |
| | | { |
| | | if (equalityIndex != null) |
| | | { |
| | | equalityIndex.setRebuildStatus(rebuildRunning); |
| | | } |
| | | |
| | | if (presenceIndex != null) |
| | | { |
| | | presenceIndex.setRebuildStatus(rebuildRunning); |
| | | } |
| | | |
| | | if (substringIndex != null) |
| | | { |
| | | substringIndex.setRebuildStatus(rebuildRunning); |
| | | } |
| | | |
| | | if (orderingIndex != null) |
| | | { |
| | | orderingIndex.setRebuildStatus(rebuildRunning); |
| | | } |
| | | |
| | | if (approximateIndex != null) |
| | | { |
| | | approximateIndex.setRebuildStatus(rebuildRunning); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Get the JE database name prefix for indexes in this attribute |
| | | * index. |
| | | * |
| | | * @return JE database name for this database container. |
| | | */ |
| | | public String getName() |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | builder.append(entryContainer.getContainerName()); |
| | | builder.append("_"); |
| | | builder.append(indexConfig.getIndexAttribute().getNameOrOID()); |
| | | return builder.toString(); |
| | | } |
| | | } |
| | |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.File; |
| | | import java.util.ArrayList; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.FilenameFilter; |
| | | import java.util.Arrays; |
| | |
| | | import java.util.zip.CheckedInputStream; |
| | | |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.EnvironmentConfig; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.MonitorProvider; |
| | |
| | | import org.opends.server.core.SearchOperation; |
| | | import org.opends.server.util.LDIFException; |
| | | import org.opends.server.util.Validator; |
| | | import org.opends.server.util.StaticUtils; |
| | | import static org.opends.server.util.StaticUtils.getFileForPath; |
| | | |
| | | import static org.opends.server.messages.BackendMessages.*; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | |
| | | * locally in a Sleepycat JE database. |
| | | */ |
| | | public class BackendImpl |
| | | extends Backend |
| | | implements ConfigurationChangeListener<JEBackendCfg> |
| | | extends Backend |
| | | implements ConfigurationChangeListener<JEBackendCfg> |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | |
| | | /** |
| | | * The configuration of this JE backend. |
| | | */ |
| | | private Config config; |
| | | private JEBackendCfg cfg; |
| | | |
| | | /** |
| | |
| | | * A list of monitor providers created for this backend instance. |
| | | */ |
| | | private ArrayList<MonitorProvider> monitorProviders = |
| | | new ArrayList<MonitorProvider>(); |
| | | new ArrayList<MonitorProvider>(); |
| | | |
| | | /** |
| | | * The controls supported by this backend. |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This method will attempt to checksum the current JE db environment by |
| | | * computing the Adler-32 checksum on the latest JE log file available. |
| | | * |
| | | * @return The checksum of JE db environment or zero if checksum failed. |
| | | */ |
| | | /** |
| | | * This method will attempt to checksum the current JE db environment by |
| | | * computing the Adler-32 checksum on the latest JE log file available. |
| | | * |
| | | * @return The checksum of JE db environment or zero if checksum failed. |
| | | */ |
| | | private long checksumDbEnv() { |
| | | |
| | | List<File> jdbFiles = |
| | | Arrays.asList(config.getBackendDirectory().listFiles(new FilenameFilter() { |
| | | public boolean accept(File dir, String name) { |
| | | return name.endsWith(".jdb"); |
| | | } |
| | | })); |
| | | File backendDirectory = getFileForPath(cfg.getBackendDirectory()); |
| | | List<File> jdbFiles = new ArrayList<File>(); |
| | | if(backendDirectory.isDirectory()) |
| | | { |
| | | jdbFiles = |
| | | Arrays.asList(backendDirectory.listFiles(new FilenameFilter() { |
| | | public boolean accept(File dir, String name) { |
| | | return name.endsWith(".jdb"); |
| | | } |
| | | })); |
| | | } |
| | | |
| | | if ( !jdbFiles.isEmpty() ) { |
| | | Collections.sort(jdbFiles, Collections.reverseOrder()); |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public void configureBackend(Configuration cfg) |
| | | throws ConfigException |
| | | throws ConfigException |
| | | { |
| | | Validator.ensureNotNull(cfg); |
| | | Validator.ensureTrue(cfg instanceof JEBackendCfg); |
| | | |
| | | // Initialize a config object |
| | | Config config = new Config(); |
| | | config.initializeConfig((JEBackendCfg)cfg); |
| | | |
| | | this.cfg = (JEBackendCfg)cfg; |
| | | this.config = config; |
| | | } |
| | | |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public void initializeBackend() |
| | | throws ConfigException, InitializationException |
| | | throws ConfigException, InitializationException |
| | | { |
| | | // Checksum this db environment and register its offline state id/checksum. |
| | | DirectoryServer.registerOfflineBackendStateID(this.getBackendID(), |
| | | checksumDbEnv()); |
| | | checksumDbEnv()); |
| | | |
| | | // Open the database environment |
| | | try { |
| | | rootContainer = new RootContainer(config, this); |
| | | rootContainer.open(); |
| | | } |
| | | catch (DatabaseException e) |
| | | if(rootContainer == null) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | String message = getMessage(MSGID_JEB_OPEN_ENV_FAIL, |
| | | e.getMessage()); |
| | | throw new InitializationException(MSGID_JEB_OPEN_ENV_FAIL, message, e); |
| | | } |
| | | EnvironmentConfig envConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | |
| | | try |
| | | { |
| | | rootContainer.openEntryContainers(config.getBaseDNs()); |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, databaseException); |
| | | } |
| | | String message = getMessage(MSGID_JEB_OPEN_DATABASE_FAIL, |
| | | databaseException.getMessage()); |
| | | throw new InitializationException(MSGID_JEB_OPEN_DATABASE_FAIL, message, |
| | | databaseException); |
| | | initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | // Preload the database cache. |
| | | rootContainer.preload(); |
| | | rootContainer.preload(cfg.getBackendPreloadTimeLimit()); |
| | | |
| | | try |
| | | { |
| | |
| | | message, databaseException); |
| | | } |
| | | |
| | | for (DN dn : config.getBaseDNs()) |
| | | for (DN dn : cfg.getBackendBaseDN()) |
| | | { |
| | | try |
| | | { |
| | |
| | | |
| | | // Register this backend as a change listener. |
| | | cfg.addJEChangeListener(this); |
| | | cfg.addJEChangeListener(config); |
| | | cfg.addJEChangeListener(rootContainer); |
| | | } |
| | | |
| | | /** |
| | |
| | | public void finalizeBackend() |
| | | { |
| | | // Deregister as a change listener. |
| | | cfg.removeJEChangeListener(rootContainer); |
| | | cfg.removeJEChangeListener(config); |
| | | cfg.removeJEChangeListener(this); |
| | | |
| | | // Deregister our base DNs. |
| | |
| | | */ |
| | | public DN[] getBaseDNs() |
| | | { |
| | | return config.getBaseDNs(); |
| | | Set<DN> dnSet = cfg.getBackendBaseDN(); |
| | | DN[] baseDNs = new DN[dnSet.size()]; |
| | | return dnSet.toArray(baseDNs); |
| | | } |
| | | |
| | | |
| | |
| | | public Entry getEntry(DN entryDN) throws DirectoryException |
| | | { |
| | | readerBegin(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | ec.sharedLock.lock(); |
| | | Entry entry; |
| | | try |
| | | { |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | Entry entry; |
| | | try |
| | | entry = ec.getEntry(entryDN); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | entry = ec.getEntry(entryDN); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (DatabaseException e) |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | |
| | | return entry; |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | readerEnd(); |
| | | } |
| | | |
| | | return entry; |
| | | } |
| | | |
| | | |
| | |
| | | * entry. |
| | | */ |
| | | public void addEntry(Entry entry, AddOperation addOperation) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | writerBegin(); |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | |
| | | try |
| | | ec.addEntry(entry, addOperation); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | ec.addEntry(entry, addOperation); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (DatabaseException e) |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | * entry. |
| | | */ |
| | | public void deleteEntry(DN entryDN, DeleteOperation deleteOperation) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | writerBegin(); |
| | | |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | try |
| | | ec.deleteEntry(entryDN, deleteOperation); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | ec.deleteEntry(entryDN, deleteOperation); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (DatabaseException e) |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | * entry. |
| | | */ |
| | | public void replaceEntry(Entry entry, ModifyOperation modifyOperation) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | writerBegin(); |
| | | |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | ec.sharedLock.lock(); |
| | | |
| | | try |
| | | { |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | |
| | | try |
| | | ec.replaceEntry(entry, modifyOperation); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | ec.replaceEntry(entry, modifyOperation); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (DatabaseException e) |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | String message = getMessage(MSGID_JEB_DATABASE_EXCEPTION, |
| | | e.getMessage()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | */ |
| | | public void renameEntry(DN currentDN, Entry entry, |
| | | ModifyDNOperation modifyDNOperation) |
| | | throws DirectoryException, CancelledOperationException |
| | | throws DirectoryException, CancelledOperationException |
| | | { |
| | | writerBegin(); |
| | | EntryContainer currentContainer = rootContainer.getEntryContainer( |
| | | currentDN); |
| | | EntryContainer container = rootContainer.getEntryContainer(entry.getDN()); |
| | | |
| | | if (currentContainer != container) |
| | | { |
| | | // FIXME: No reason why we cannot implement a move between containers |
| | | // since the containers share the same database environment. |
| | | int msgID = MSGID_JEB_FUNCTION_NOT_SUPPORTED; |
| | | String msg = getMessage(MSGID_JEB_FUNCTION_NOT_SUPPORTED); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | msg, msgID); |
| | | } |
| | | try |
| | | { |
| | | EntryContainer currentContainer = rootContainer.getEntryContainer( |
| | | currentDN); |
| | | EntryContainer container = rootContainer.getEntryContainer(entry.getDN()); |
| | | |
| | | if (currentContainer != container) |
| | | { |
| | | // FIXME: No reason why we cannot implement a move between containers |
| | | // since the containers share the same database environment. |
| | | int msgID = MSGID_JEB_FUNCTION_NOT_SUPPORTED; |
| | | String msg = getMessage(MSGID_JEB_FUNCTION_NOT_SUPPORTED); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | msg, msgID); |
| | | } |
| | | currentContainer.sharedLock.lock(); |
| | | |
| | | currentContainer.renameEntry(currentDN, entry, modifyDNOperation); |
| | | } |
| | |
| | | } |
| | | finally |
| | | { |
| | | currentContainer.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | * If a problem occurs while processing the search. |
| | | */ |
| | | public void search(SearchOperation searchOperation) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | readerBegin(); |
| | | EntryContainer ec = rootContainer.getEntryContainer( |
| | | searchOperation.getBaseDN()); |
| | | ec.sharedLock.lock(); |
| | | |
| | | try |
| | | { |
| | | EntryContainer ec = rootContainer.getEntryContainer( |
| | | searchOperation.getBaseDN()); |
| | | ec.search(searchOperation); |
| | | } |
| | | catch (DatabaseException e) |
| | |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | readerEnd(); |
| | | } |
| | | } |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public void exportLDIF(LDIFExportConfig exportConfig) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | // If the backend already has the root container open, we must use the same |
| | | // underlying root container |
| | |
| | | |
| | | try |
| | | { |
| | | if (openRootContainer) |
| | | if(openRootContainer) |
| | | { |
| | | // Open the database environment |
| | | rootContainer = new RootContainer(config, this); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | true, false, false, false, true, true); |
| | | rootContainer.openEntryContainers(config.getBaseDNs()); |
| | | 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"); |
| | | |
| | | initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | |
| | | ExportJob exportJob = new ExportJob(exportConfig); |
| | | exportJob.exportLDIF(rootContainer); |
| | | } |
| | |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | catch (InitializationException ie) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ie); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | ie.getMessage(), |
| | | ie.getMessageID()); |
| | | } |
| | | catch (ConfigException ce) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ce); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | ce.getMessage(), |
| | | ce.getMessageID()); |
| | | } |
| | | finally |
| | | { |
| | | //If a root container was opened in this method as read only, close it |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public void importLDIF(LDIFImportConfig importConfig) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | // If the backend already has the root container open, we must use the same |
| | | // underlying root container |
| | | boolean openRootContainer = rootContainer == null; |
| | | |
| | | // If the rootContainer is open, the backend is initialized by something |
| | | // else. |
| | | // We can't do import while the backend is online. |
| | | if(!openRootContainer) |
| | | { |
| | | String message = getMessage(MSGID_JEB_IMPORT_BACKEND_ONLINE); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_IMPORT_BACKEND_ONLINE); |
| | | } |
| | | |
| | | try |
| | | { |
| | | ImportJob importJob = new ImportJob(this, config, importConfig); |
| | | importJob.importLDIF(); |
| | | EnvironmentConfig envConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | /** |
| | | envConfig.setConfigParam("je.env.runCleaner", "false"); |
| | | envConfig.setConfigParam("je.log.numBuffers", "2"); |
| | | envConfig.setConfigParam("je.log.bufferSize", "15000000"); |
| | | envConfig.setConfigParam("je.log.totalBufferBytes", "30000000"); |
| | | envConfig.setConfigParam("je.log.fileMax", "100000000"); |
| | | **/ |
| | | |
| | | if (importConfig.appendToExistingData()) |
| | | { |
| | | envConfig.setReadOnly(false); |
| | | envConfig.setAllowCreate(true); |
| | | envConfig.setTransactional(true); |
| | | envConfig.setTxnNoSync(true); |
| | | envConfig.setConfigParam("je.env.isLocking", "true"); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", "false"); |
| | | } |
| | | else |
| | | { |
| | | // We have the writer lock on the environment, now delete the |
| | | // environment and re-open it. Only do this when we are |
| | | // importing to all the base DNs in the backend. |
| | | |
| | | File backendDirectory = getFileForPath(cfg.getBackendDirectory()); |
| | | EnvManager.removeFiles(backendDirectory.getPath()); |
| | | envConfig.setReadOnly(false); |
| | | envConfig.setAllowCreate(true); |
| | | envConfig.setTransactional(false); |
| | | envConfig.setTxnNoSync(false); |
| | | envConfig.setConfigParam("je.env.isLocking", "false"); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", "false"); |
| | | } |
| | | |
| | | initializeRootContainer(envConfig); |
| | | |
| | | ImportJob importJob = new ImportJob(importConfig); |
| | | importJob.importLDIF(rootContainer); |
| | | } |
| | | catch (IOException ioe) |
| | | { |
| | |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, MSGID_JEB_DATABASE_EXCEPTION); |
| | | } |
| | | catch (InitializationException ie) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ie); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | ie.getMessage(), |
| | | ie.getMessageID()); |
| | | } |
| | | catch (ConfigException ce) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, ce); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | ce.getMessage(), |
| | | ce.getMessageID()); |
| | | } |
| | | finally |
| | | { |
| | | // leave the backend in the same state. |
| | | try |
| | | { |
| | | rootContainer.close(); |
| | | rootContainer = null; |
| | | |
| | | // Sync the environment to disk. |
| | | int msgID = MSGID_JEB_IMPORT_CLOSING_DATABASE; |
| | | String message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | catch (DatabaseException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | */ |
| | | public void verifyBackend(VerifyConfig verifyConfig, Entry statEntry) |
| | | throws InitializationException, ConfigException, DirectoryException |
| | | throws InitializationException, ConfigException, DirectoryException |
| | | { |
| | | // If the backend already has the root container open, we must use the same |
| | | // underlying root container |
| | |
| | | |
| | | try |
| | | { |
| | | if (openRootContainer) |
| | | if(openRootContainer) |
| | | { |
| | | // Open the database environment |
| | | rootContainer = new RootContainer(config, this); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | true, false, false, false, true, true); |
| | | rootContainer.openEntryContainers(config.getBaseDNs()); |
| | | 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"); |
| | | |
| | | initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(config, verifyConfig); |
| | | VerifyJob verifyJob = new VerifyJob(verifyConfig); |
| | | verifyJob.verifyBackend(rootContainer, statEntry); |
| | | } |
| | | catch (DatabaseException e) |
| | |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | */ |
| | | public void rebuildBackend(RebuildConfig rebuildConfig) |
| | | throws InitializationException, ConfigException, DirectoryException |
| | | throws InitializationException, ConfigException, DirectoryException |
| | | { |
| | | // If the backend already has the root container open, we must use the same |
| | | // underlying root container |
| | |
| | | { |
| | | if (openRootContainer) |
| | | { |
| | | // Open the database environment |
| | | rootContainer = new RootContainer(config, this); |
| | | rootContainer.open(); |
| | | rootContainer.openEntryContainers(config.getBaseDNs()); |
| | | EnvironmentConfig envConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | |
| | | initializeRootContainer(envConfig); |
| | | } |
| | | |
| | | RebuildJob rebuildJob = new RebuildJob(rebuildConfig); |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public void createBackup(BackupConfig backupConfig) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | BackupManager backupManager = |
| | | new BackupManager(getBackendID()); |
| | | new BackupManager(getBackendID()); |
| | | backupManager.createBackup(cfg, backupConfig); |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public void removeBackup(BackupDirectory backupDirectory, String backupID) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | BackupManager backupManager = |
| | | new BackupManager(getBackendID()); |
| | | new BackupManager(getBackendID()); |
| | | backupManager.removeBackup(backupDirectory, backupID); |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public void restoreBackup(RestoreConfig restoreConfig) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | BackupManager backupManager = |
| | | new BackupManager(getBackendID()); |
| | | new BackupManager(getBackendID()); |
| | | backupManager.restoreBackup(cfg, restoreConfig); |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isConfigurationChangeAcceptable( |
| | | JEBackendCfg cfg, |
| | | List<String> unacceptableReasons) |
| | | JEBackendCfg cfg, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | // This listener handles only the changes to the base DNs. |
| | | // The base DNs are checked by the backend config manager. |
| | | |
| | | return true; |
| | | } |
| | | |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConfigChangeResult applyConfigurationChange(JEBackendCfg cfg) |
| | | public ConfigChangeResult applyConfigurationChange(JEBackendCfg newCfg) |
| | | { |
| | | ConfigChangeResult ccr; |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | |
| | | try |
| | | { |
| | | DN[] baseDNs = new DN[cfg.getBackendBaseDN().size()]; |
| | | baseDNs = cfg.getBackendBaseDN().toArray(baseDNs); |
| | | |
| | | // Check for changes to the base DNs. |
| | | for (DN baseDN : config.getBaseDNs()) |
| | | if(rootContainer != null) |
| | | { |
| | | boolean found = false; |
| | | for (DN dn : baseDNs) |
| | | { |
| | | if (dn.equals(baseDN)) |
| | | { |
| | | found = true; |
| | | } |
| | | } |
| | | if (!found) |
| | | { |
| | | // The base DN was deleted. |
| | | // FIXME This is not thread-safe. |
| | | // Even though access to the entry container map is safe, there may be |
| | | // operation threads with a handle on the entry container being |
| | | // closed. |
| | | DirectoryServer.deregisterBaseDN(baseDN, false); |
| | | rootContainer.removeEntryContainer(baseDN); |
| | | } |
| | | } |
| | | DN[] baseDNs = new DN[newCfg.getBackendBaseDN().size()]; |
| | | baseDNs = newCfg.getBackendBaseDN().toArray(baseDNs); |
| | | |
| | | for (DN baseDN : baseDNs) |
| | | { |
| | | if (!rootContainer.getBaseDNs().contains(baseDN)) |
| | | // Check for changes to the base DNs. |
| | | for (DN baseDN : cfg.getBackendBaseDN()) |
| | | { |
| | | try |
| | | boolean found = false; |
| | | for (DN dn : baseDNs) |
| | | { |
| | | // The base DN was added. |
| | | rootContainer.openEntryContainer(baseDN); |
| | | DirectoryServer.registerBaseDN(baseDN, this, false, false); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | if (dn.equals(baseDN)) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | found = true; |
| | | } |
| | | } |
| | | if (!found) |
| | | { |
| | | // The base DN was deleted. |
| | | DirectoryServer.deregisterBaseDN(baseDN, false); |
| | | rootContainer.removeEntryContainer(baseDN); |
| | | } |
| | | } |
| | | |
| | | resultCode = DirectoryServer.getServerErrorResultCode(); |
| | | for (DN baseDN : baseDNs) |
| | | { |
| | | if (!rootContainer.getBaseDNs().contains(baseDN)) |
| | | { |
| | | try |
| | | { |
| | | // The base DN was added. |
| | | rootContainer.openEntryContainer(baseDN); |
| | | DirectoryServer.registerBaseDN(baseDN, this, false, false); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN; |
| | | messages.add(getMessage(msgID, String.valueOf(baseDN), |
| | | String.valueOf(e))); |
| | | ccr = new ConfigChangeResult(resultCode, false, messages); |
| | | return ccr; |
| | | resultCode = DirectoryServer.getServerErrorResultCode(); |
| | | |
| | | int msgID = MSGID_BACKEND_CANNOT_REGISTER_BASEDN; |
| | | messages.add(getMessage(msgID, String.valueOf(baseDN), |
| | | String.valueOf(e))); |
| | | ccr = new ConfigChangeResult(resultCode, false, messages); |
| | | return ccr; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Put the new configuration in place. |
| | | this.cfg = cfg; |
| | | this.cfg = newCfg; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | messages.add(e.getMessage()); |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(e)); |
| | | ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(), |
| | | false, messages); |
| | | return ccr; |
| | |
| | | * @throws JebException If an error occurs while removing the data. |
| | | */ |
| | | public void clearBackend() |
| | | throws ConfigException, JebException |
| | | throws ConfigException, JebException |
| | | { |
| | | EnvManager.removeFiles(config.getBackendDirectory().getPath()); |
| | | // Determine the backend database directory. |
| | | File backendDirectory = getFileForPath(cfg.getBackendDirectory()); |
| | | EnvManager.removeFiles(backendDirectory.getPath()); |
| | | } |
| | | |
| | | |
| | | private void initializeRootContainer(EnvironmentConfig envConfig) |
| | | throws ConfigException, InitializationException |
| | | { |
| | | // Open the database environment |
| | | try |
| | | { |
| | | rootContainer = new RootContainer(this, cfg); |
| | | rootContainer.open(envConfig); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | String message = getMessage(MSGID_JEB_OPEN_ENV_FAIL, |
| | | e.getMessage()); |
| | | throw new InitializationException(MSGID_JEB_OPEN_ENV_FAIL, message, e); |
| | | } |
| | | } |
| | | } |
| | |
| | | public void createBackup(JEBackendCfg cfg, BackupConfig backupConfig) |
| | | throws DirectoryException |
| | | { |
| | | // Parse our backend configuration. |
| | | Config config = new Config(); |
| | | try |
| | | { |
| | | config.initializeConfig(cfg); |
| | | } |
| | | catch (ConfigException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | |
| | | // Get the properties to use for the backup. |
| | | String backupID = backupConfig.getBackupID(); |
| | | BackupDirectory backupDir = backupConfig.getBackupDirectory(); |
| | |
| | | // If this is an incremental, determine the base backup for this backup. |
| | | HashSet<String> dependencies = new HashSet<String>(); |
| | | BackupInfo baseBackup = null; |
| | | File backendDir = config.getBackendDirectory(); |
| | | File backendDir = getFileForPath(cfg.getBackendDirectory()); |
| | | /* |
| | | FilenameFilter backupTagFilter = new FilenameFilter() |
| | | { |
| | |
| | | |
| | | BackupInfo backupInfo = getBackupInfo(backupDir, backupID); |
| | | |
| | | // Parse our backend configuration. |
| | | Config config = new Config(); |
| | | try |
| | | { |
| | | config.initializeConfig(cfg); |
| | | } |
| | | catch (ConfigException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | |
| | | // Create a restore directory with a different name to the backend |
| | | // directory. |
| | | File currentDir = config.getBackendDirectory(); |
| | | File currentDir = getFileForPath(cfg.getBackendDirectory()); |
| | | File restoreDir = new File(currentDir.getPath() + "-restore-" + backupID); |
| | | if (!verifyOnly) |
| | | { |
| | |
| | | */ |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import com.sleepycat.je.Cursor; |
| | | import com.sleepycat.je.CursorConfig; |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.DatabaseConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.OperationStatus; |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.*; |
| | | |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.util.StaticUtils; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | |
| | | import java.util.Comparator; |
| | | |
| | | /** |
| | | * This class represents the DN database, or dn2id, which has one record |
| | | * for each entry. The key is the normalized entry DN and the value |
| | | * is the entry ID. |
| | | */ |
| | | public class DN2ID |
| | | public class DN2ID extends DatabaseContainer |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * The database entryContainer. |
| | | * The key comparator used for the DN database. |
| | | */ |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | | */ |
| | | private DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | | /** |
| | | * A cached per-thread JE database handle. |
| | | */ |
| | | private ThreadLocal<Database> threadLocalDatabase = |
| | | new ThreadLocal<Database>(); |
| | | private Comparator<byte[]> dn2idComparator; |
| | | |
| | | /** |
| | | * Create a DN2ID instance for the DN database in a given entryContainer. |
| | | * |
| | | * @param name The name of the DN database. |
| | | * @param env The JE environment. |
| | | * @param entryContainer The entryContainer of the DN database. |
| | | * @param dbConfig The JE database configuration which will be used to |
| | | * open the database. |
| | | * @param name The name of the DN database ("dn2id"). |
| | | */ |
| | | public DN2ID(EntryContainer entryContainer, DatabaseConfig dbConfig, |
| | | String name) |
| | | { |
| | | this.entryContainer = entryContainer; |
| | | this.dbConfig = dbConfig.cloneConfig(); |
| | | this.name = name; |
| | | } |
| | | |
| | | /** |
| | | * Open the DN database. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void open() throws DatabaseException |
| | | DN2ID(String name, Environment env, EntryContainer entryContainer) |
| | | throws DatabaseException |
| | | { |
| | | getDatabase(); |
| | | } |
| | | super(name, env, entryContainer); |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | private Database getDatabase() throws DatabaseException |
| | | { |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | dn2idComparator = new EntryContainer.KeyReverseComparator(); |
| | | DatabaseConfig dn2idConfig = new DatabaseConfig(); |
| | | |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("JE DN2ID database %s opened with %d records.", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | dn2idConfig.setReadOnly(true); |
| | | dn2idConfig.setAllowCreate(false); |
| | | dn2idConfig.setTransactional(false); |
| | | } |
| | | return database; |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | dn2idConfig.setAllowCreate(true); |
| | | dn2idConfig.setTransactional(false); |
| | | dn2idConfig.setDeferredWrite(true); |
| | | } |
| | | else |
| | | { |
| | | dn2idConfig.setAllowCreate(true); |
| | | dn2idConfig.setTransactional(true); |
| | | } |
| | | |
| | | this.dbConfig = dn2idConfig; |
| | | this.dbConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | OperationStatus status; |
| | | |
| | | status = EntryContainer.insert(getDatabase(), txn, key, data); |
| | | status = insert(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = id.getDatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | status = put(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status; |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | status = put(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | { |
| | | DatabaseEntry key = DNdata(dn); |
| | | |
| | | OperationStatus status = EntryContainer.delete(getDatabase(), txn, key); |
| | | OperationStatus status = delete(txn, key); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, |
| | | LockMode.DEFAULT); |
| | | status = read(txn, key, data, LockMode.DEFAULT); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return null; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Open a JE cursor on the DN database. |
| | | * @param txn A JE database transaction to be used by the cursor, |
| | | * or null if none. |
| | | * @param cursorConfig The JE cursor configuration. |
| | | * @return A JE cursor. |
| | | * @throws DatabaseException If an error occurs while attempting to open |
| | | * the cursor. |
| | | */ |
| | | public Cursor openCursor(Transaction txn, CursorConfig cursorConfig) |
| | | throws DatabaseException |
| | | { |
| | | return getDatabase().openCursor(txn, cursorConfig); |
| | | } |
| | | |
| | | /** |
| | | * Removes all records from the database. |
| | | * @param txn A JE database transaction to be used during the clear operation |
| | | * or null if not required. Using transactions increases the chance |
| | | * of lock contention. |
| | | * @return The number of records removed. |
| | | * @throws DatabaseException If an error occurs while cleaning the database. |
| | | */ |
| | | public long clear(Transaction txn) throws DatabaseException |
| | | { |
| | | long deletedCount = 0; |
| | | Cursor cursor = openCursor(txn, null); |
| | | try |
| | | { |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("%d existing records will be deleted from the " + |
| | | "database", getRecordCount()); |
| | | } |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | |
| | | // Step forward until we deleted all records. |
| | | for (status = cursor.getFirst(key, data, LockMode.DEFAULT); |
| | | status == OperationStatus.SUCCESS; |
| | | status = cursor.getNext(key, data, LockMode.DEFAULT)) |
| | | { |
| | | cursor.delete(); |
| | | deletedCount++; |
| | | } |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("%d records deleted", deletedCount); |
| | | } |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | throw de; |
| | | } |
| | | finally |
| | | { |
| | | cursor.close(); |
| | | } |
| | | |
| | | return deletedCount; |
| | | } |
| | | |
| | | /** |
| | | * Get the count of the number of entries stored. |
| | | * Gets the comparator for records stored in this database. |
| | | * |
| | | * @return The number of entries stored. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @return The comparator for records stored in this database. |
| | | */ |
| | | public long getRecordCount() throws DatabaseException |
| | | public Comparator<byte[]> getComparator() |
| | | { |
| | | return EntryContainer.count(getDatabase()); |
| | | return dn2idComparator; |
| | | } |
| | | |
| | | /** |
| | | * Get a string representation of this object. |
| | | * @return return A string representation of this object. |
| | | */ |
| | | public String toString() |
| | | { |
| | | return name; |
| | | } |
| | | |
| | | } |
| | |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | |
| | | import com.sleepycat.je.Cursor; |
| | | import com.sleepycat.je.CursorConfig; |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.DatabaseConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.OperationStatus; |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.*; |
| | | |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.core.SearchOperation; |
| | |
| | | * order is the same as in the DN database so that all referrals in a subtree |
| | | * can be retrieved by cursoring through a range of the records. |
| | | */ |
| | | public class DN2URI |
| | | public class DN2URI extends DatabaseContainer |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * The key comparator used for the DN database. |
| | | */ |
| | | private Comparator<byte[]> dn2uriComparator; |
| | | |
| | | |
| | | /** |
| | | * The standard attribute type that is used to specify the set of referral |
| | |
| | | DirectoryServer.getAttributeType(ATTR_REFERRAL_URL); |
| | | |
| | | /** |
| | | * The database entryContainer. |
| | | */ |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | | */ |
| | | private DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | | /** |
| | | * A custom btree key comparator for the JE database. |
| | | */ |
| | | Comparator<byte[]> comparator = new EntryContainer.KeyReverseComparator(); |
| | | |
| | | /** |
| | | * A cached per-thread JE database handle. |
| | | */ |
| | | private ThreadLocal<Database> threadLocalDatabase = |
| | | new ThreadLocal<Database>(); |
| | | |
| | | /** |
| | | * Create a new object representing a referral database in a given |
| | | * entryContainer. |
| | | * |
| | | * @param entryContainer The entryContainer of the referral database. |
| | | * @param dbConfig The JE database configuration which will be used to |
| | | * open the database. |
| | | * @param name The name of the referral database. |
| | | */ |
| | | public DN2URI(EntryContainer entryContainer, DatabaseConfig dbConfig, |
| | | String name) |
| | | { |
| | | this.entryContainer = entryContainer; |
| | | this.dbConfig = dbConfig.cloneConfig(); |
| | | this.name = name; |
| | | } |
| | | |
| | | /** |
| | | * Open the referral database. |
| | | * |
| | | * @param env The JE environment. |
| | | * @param entryContainer The entryContainer of the DN database. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void open() throws DatabaseException |
| | | DN2URI(String name, Environment env, |
| | | EntryContainer entryContainer) |
| | | throws DatabaseException |
| | | { |
| | | getDatabase(); |
| | | } |
| | | super(name, env, entryContainer); |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | private Database getDatabase() throws DatabaseException |
| | | { |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | dn2uriComparator = new EntryContainer.KeyReverseComparator(); |
| | | DatabaseConfig dn2uriConfig = new DatabaseConfig(); |
| | | |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("JE DN2URI database %s opened with %d records.", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | dn2uriConfig.setReadOnly(true); |
| | | dn2uriConfig.setSortedDuplicates(true); |
| | | dn2uriConfig.setAllowCreate(false); |
| | | dn2uriConfig.setTransactional(false); |
| | | } |
| | | return database; |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | dn2uriConfig.setSortedDuplicates(true); |
| | | dn2uriConfig.setAllowCreate(true); |
| | | dn2uriConfig.setTransactional(false); |
| | | dn2uriConfig.setDeferredWrite(true); |
| | | } |
| | | else |
| | | { |
| | | dn2uriConfig.setSortedDuplicates(true); |
| | | dn2uriConfig.setAllowCreate(true); |
| | | dn2uriConfig.setTransactional(true); |
| | | } |
| | | this.dbConfig = dn2uriConfig; |
| | | this.dbConfig.setBtreeComparator(dn2uriComparator.getClass()); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | // The JE insert method does not permit duplicate keys so we must use the |
| | | // put method. |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | status = put(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry key = new DatabaseEntry(normDN); |
| | | OperationStatus status; |
| | | |
| | | status = EntryContainer.delete(getDatabase(), txn, key); |
| | | status = delete(txn, key); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = new DatabaseEntry(URIBytes); |
| | | OperationStatus status; |
| | | |
| | | Cursor cursor = getDatabase().openCursor(txn, cursorConfig); |
| | | Cursor cursor = openCursor(txn, cursorConfig); |
| | | try |
| | | { |
| | | status = cursor.getSearchBoth(key, data, null); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Open a JE cursor on the DN database. |
| | | * @param txn A JE database transaction to be used by the cursor, |
| | | * or null if none. |
| | | * @param cursorConfig The JE cursor configuration. |
| | | * @return A JE cursor. |
| | | * @throws DatabaseException If an error occurs while attempting to open |
| | | * the cursor. |
| | | */ |
| | | public Cursor openCursor(Transaction txn, CursorConfig cursorConfig) |
| | | throws DatabaseException |
| | | { |
| | | return getDatabase().openCursor(txn, cursorConfig); |
| | | } |
| | | |
| | | /** |
| | | * Removes all records from the database. |
| | | * @param txn A JE database transaction to be used during the clear operation |
| | | * or null if not required. Using transactions increases the chance |
| | | * of lock contention. |
| | | * @return The number of records removed. |
| | | * @throws DatabaseException If an error occurs while cleaning the database. |
| | | */ |
| | | public long clear(Transaction txn) throws DatabaseException |
| | | { |
| | | long deletedCount = 0; |
| | | Cursor cursor = openCursor(txn, null); |
| | | try |
| | | { |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("%d existing records will be deleted from the " + |
| | | "database", getRecordCount()); |
| | | } |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | |
| | | // Step forward until we deleted all records. |
| | | for (status = cursor.getFirst(key, data, LockMode.DEFAULT); |
| | | status == OperationStatus.SUCCESS; |
| | | status = cursor.getNext(key, data, LockMode.DEFAULT)) |
| | | { |
| | | cursor.delete(); |
| | | deletedCount++; |
| | | } |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("%d records deleted", deletedCount); |
| | | } |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | throw de; |
| | | } |
| | | finally |
| | | { |
| | | cursor.close(); |
| | | } |
| | | |
| | | return deletedCount; |
| | | } |
| | | |
| | | /** |
| | | * Checks whether the target of an operation is a referral entry and throws |
| | | * a Directory referral exception if it is. |
| | | * @param entry The target entry of the operation, or the base entry of a |
| | |
| | | |
| | | try |
| | | { |
| | | Cursor cursor = getDatabase().openCursor(txn, cursorConfig); |
| | | Cursor cursor = openCursor(txn, cursorConfig); |
| | | try |
| | | { |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | |
| | | |
| | | try |
| | | { |
| | | Cursor cursor = getDatabase().openCursor(txn, cursorConfig); |
| | | Cursor cursor = openCursor(txn, cursorConfig); |
| | | try |
| | | { |
| | | // Initialize the cursor very close to the starting value then |
| | |
| | | end[0] = (byte) (end[0] + 1); |
| | | } |
| | | |
| | | int cmp = comparator.compare(key.getData(), end); |
| | | int cmp = dn2uriComparator.compare(key.getData(), end); |
| | | if (cmp >= 0) |
| | | { |
| | | // We have gone past the ending value. |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the count of the number of entries stored. |
| | | * Gets the comparator for records stored in this database. |
| | | * |
| | | * @return The number of entries stored. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @return The comparator used for records stored in this database. |
| | | */ |
| | | public long getRecordCount() throws DatabaseException |
| | | public Comparator<byte[]> getComparator() |
| | | { |
| | | return EntryContainer.count(getDatabase()); |
| | | } |
| | | |
| | | /** |
| | | * Get a string representation of this object. |
| | | * @return return A string representation of this object. |
| | | */ |
| | | public String toString() |
| | | { |
| | | return name; |
| | | return dn2uriComparator; |
| | | } |
| | | } |
| 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.backends.jeb; |
| | | |
| | | import com.sleepycat.je.*; |
| | | |
| | | import java.util.concurrent.CopyOnWriteArrayList; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | |
| | | /** |
| | | * This class is a wrapper around the JE database object and provides basic |
| | | * read and write methods for entries. |
| | | */ |
| | | public abstract class DatabaseContainer |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * The database entryContainer. |
| | | */ |
| | | protected EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | | */ |
| | | protected DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | protected String name; |
| | | |
| | | /** |
| | | * The reference to the JE Environment. |
| | | */ |
| | | private Environment env; |
| | | |
| | | /** |
| | | * A list of JE database handles opened through this database |
| | | * container. |
| | | */ |
| | | private CopyOnWriteArrayList<Database> databases; |
| | | |
| | | /** |
| | | * A cached per-thread JE database handle. |
| | | */ |
| | | private ThreadLocal<Database> threadLocalDatabase = |
| | | new ThreadLocal<Database>(); |
| | | |
| | | /** |
| | | * Create a new DatabaseContainer object. |
| | | * |
| | | * @param name The name of the entry database. |
| | | * @param env The JE Environment. |
| | | * @param entryContainer The entryContainer of the entry database. |
| | | * @throws DatabaseException if a JE database error occurs. |
| | | */ |
| | | protected DatabaseContainer(String name, Environment env, |
| | | EntryContainer entryContainer) |
| | | throws DatabaseException |
| | | { |
| | | this.env = env; |
| | | this.entryContainer = entryContainer; |
| | | this.databases = new CopyOnWriteArrayList<Database>(); |
| | | |
| | | StringBuilder builder = new StringBuilder(); |
| | | buildDatabaseName(builder, name); |
| | | this.name = builder.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Opens a JE database in this database container. If the provided |
| | | * database configuration is transactional, a transaction will be |
| | | * created and used to perform the open. |
| | | * <p> |
| | | * Note that a database can be opened multiple times and will result in |
| | | * multiple unique handles to the database. This is used for example to |
| | | * give each server thread its own database handle to eliminate contention |
| | | * that could occur on a single handle. |
| | | * |
| | | * @return A new JE database handle. |
| | | * @throws DatabaseException If an error occurs while attempting to open the |
| | | * database. |
| | | */ |
| | | private Database openDatabase() throws DatabaseException |
| | | { |
| | | Database database; |
| | | |
| | | if (dbConfig.getTransactional()) |
| | | { |
| | | // Open the database under a transaction. |
| | | Transaction txn = |
| | | entryContainer.beginTransaction(); |
| | | try |
| | | { |
| | | database = env.openDatabase(txn, name, dbConfig); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("JE database %s opened with %d records. txnid=%d", |
| | | database.getDatabaseName(), database.count(), |
| | | txn.getId()); |
| | | } |
| | | entryContainer.transactionCommit(txn); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | entryContainer.transactionAbort(txn); |
| | | throw e; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | database = env.openDatabase(null, name, dbConfig); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("JE database %s opened with %d records. txnid=none", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | } |
| | | |
| | | return database; |
| | | } |
| | | |
| | | private Database getDatabase() throws DatabaseException |
| | | { |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | { |
| | | database = openDatabase(); |
| | | databases.add(database); |
| | | threadLocalDatabase.set(database); |
| | | } |
| | | return database; |
| | | } |
| | | |
| | | /** |
| | | * Open the database container. |
| | | * |
| | | * @throws DatabaseException if a JE database error occurs while |
| | | * openning the index. |
| | | */ |
| | | public void open() throws DatabaseException |
| | | { |
| | | getDatabase(); |
| | | } |
| | | |
| | | /** |
| | | * Constructs a full JE database name incorporating a entryContainer name. |
| | | * |
| | | * @param builder A string builder to which the full name will be appended. |
| | | * @param name The short database name. |
| | | */ |
| | | private void buildDatabaseName(StringBuilder builder, String name) |
| | | { |
| | | builder.append(entryContainer.getContainerName()); |
| | | builder.append('_'); |
| | | builder.append(name); |
| | | } |
| | | |
| | | /** |
| | | * Flush any cached database information to disk and close the |
| | | * database container. |
| | | * |
| | | * The database container should not be closed while other processes |
| | | * aquired the container. The container should not be closed |
| | | * while cursors handles into the database remain open, or |
| | | * transactions that include operations on the database have not yet |
| | | * been commited or aborted. |
| | | * |
| | | * The container may not be accessed again after this method is |
| | | * called, regardless of the method's success or failure. |
| | | * |
| | | * @throws DatabaseException if an error occurs. |
| | | */ |
| | | synchronized void close() throws DatabaseException |
| | | { |
| | | // Close each database handle that has been opened. |
| | | for (Database database : databases) |
| | | { |
| | | if (database.getConfig().getDeferredWrite()) |
| | | { |
| | | database.sync(); |
| | | } |
| | | |
| | | database.close(); |
| | | } |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("Closed database %s (%d handles)", name, |
| | | databases.size()); |
| | | } |
| | | |
| | | databases.clear(); |
| | | threadLocalDatabase = new ThreadLocal<Database>(); |
| | | } |
| | | |
| | | /** |
| | | * Replace or insert a record into a JE database, with optional debug logging. |
| | | * This is a simple wrapper around the JE Database.put method. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | protected OperationStatus put(Transaction txn, DatabaseEntry key, |
| | | DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | Database database = getDatabase(); |
| | | OperationStatus status = database.put(txn, key, data); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, |
| | | txn, key, data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Read a record from a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.get method. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The key of the record to be read. |
| | | * @param data The record value returned as output. Its byte array does not |
| | | * need to be initialized by the caller. |
| | | * @param lockMode The JE locking mode to be used for the read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | protected OperationStatus read(Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data, |
| | | LockMode lockMode) |
| | | throws DatabaseException |
| | | { |
| | | Database database = getDatabase(); |
| | | OperationStatus status = database.get(txn, key, data, lockMode); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, txn, key, |
| | | data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Insert a record into a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.putNoOverwrite method. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | protected OperationStatus insert(Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | Database database = getDatabase(); |
| | | OperationStatus status = database.putNoOverwrite(txn, key, data); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, txn, key, |
| | | data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Delete a record from a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.delete method. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The key of the record to be read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | protected OperationStatus delete(Transaction txn, |
| | | DatabaseEntry key) |
| | | throws DatabaseException |
| | | { |
| | | Database database = getDatabase(); |
| | | OperationStatus status = database.delete(txn, key); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, txn, |
| | | key, null); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Open a JE cursor on the DN database. |
| | | * @param txn A JE database transaction to be used by the cursor, |
| | | * or null if none. |
| | | * @param cursorConfig The JE cursor configuration. |
| | | * @return A JE cursor. |
| | | * @throws DatabaseException If an error occurs while attempting to open |
| | | * the cursor. |
| | | */ |
| | | public Cursor openCursor(Transaction txn, CursorConfig cursorConfig) |
| | | throws DatabaseException |
| | | { |
| | | Database database = getDatabase(); |
| | | return database.openCursor(txn, cursorConfig); |
| | | } |
| | | |
| | | /** |
| | | * Get the count of key/data pairs in the database in a JE database. |
| | | * This is a simple wrapper around the JE Database.count method. |
| | | * @return The count of key/data pairs in the database. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public long getRecordCount() throws DatabaseException |
| | | { |
| | | Database database = getDatabase(); |
| | | long count = database.count(); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, OperationStatus.SUCCESS, |
| | | database, null, null, null); |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | /** |
| | | * Get a string representation of this object. |
| | | * @return return A string representation of this object. |
| | | */ |
| | | public String toString() |
| | | { |
| | | return name; |
| | | } |
| | | |
| | | /** |
| | | * Get the JE database name for this database container. |
| | | * |
| | | * @return JE database name for this database container. |
| | | */ |
| | | public String getName() |
| | | { |
| | | return name; |
| | | } |
| | | |
| | | /** |
| | | * Preload the database into cache. |
| | | * |
| | | * @param config The preload configuration. |
| | | * @return Statistics about the preload process. |
| | | * @throws DatabaseException If an JE database error occurs |
| | | * during the preload. |
| | | */ |
| | | public PreloadStats preload(PreloadConfig config) |
| | | throws DatabaseException |
| | | { |
| | | return getDatabase().preload(config); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | |
| | | import java.util.Comparator; |
| | | |
| | |
| | | * This comparator is used to sort databases in order of priority |
| | | * for preloading into the cache. |
| | | */ |
| | | public class DbPreloadComparator implements Comparator<Database> |
| | | public class DbPreloadComparator |
| | | implements Comparator<DatabaseContainer> |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | |
| | | * @param database A handle to the database. |
| | | * @return 1 for id2entry database, 2 for dn2id database, 3 for all others. |
| | | */ |
| | | static private int priority(Database database) |
| | | static private int priority(DatabaseContainer database) |
| | | { |
| | | try |
| | | String name = database.getName(); |
| | | if (name.endsWith(EntryContainer.ID2ENTRY_DATABASE_NAME)) |
| | | { |
| | | String name = database.getDatabaseName(); |
| | | if (name.endsWith(EntryContainer.ID2ENTRY_DATABASE_NAME)) |
| | | { |
| | | return 1; |
| | | } |
| | | else if (name.endsWith(EntryContainer.DN2ID_DATABASE_NAME)) |
| | | { |
| | | return 2; |
| | | } |
| | | else |
| | | { |
| | | return 3; |
| | | } |
| | | return 1; |
| | | } |
| | | catch (DatabaseException e) |
| | | else if (name.endsWith(EntryContainer.DN2ID_DATABASE_NAME)) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | return 2; |
| | | } |
| | | else |
| | | { |
| | | return 3; |
| | | } |
| | | } |
| | |
| | | * first argument is less than, equal to, or greater than the |
| | | * second. |
| | | **/ |
| | | public int compare(Database database1, Database database2) |
| | | public int compare(DatabaseContainer database1, DatabaseContainer database2) |
| | | { |
| | | return priority(database1) - priority(database2); |
| | | } |
| | |
| | | import org.opends.server.util.StaticUtils; |
| | | import org.opends.server.util.ServerConstants; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Comparator; |
| | | import java.util.Iterator; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.*; |
| | | import java.util.concurrent.locks.Lock; |
| | | import java.util.concurrent.locks.ReentrantReadWriteLock; |
| | | |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | import org.opends.server.admin.std.server.JEIndexCfg; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.admin.server.ConfigurationAddListener; |
| | | import org.opends.server.admin.server.ConfigurationDeleteListener; |
| | | import org.opends.server.config.ConfigException; |
| | | |
| | | /** |
| | | * Storage container for LDAP entries. Each base DN of a JE backend is given |
| | |
| | | * the guts of the backend API methods for LDAP operations. |
| | | */ |
| | | public class EntryContainer |
| | | implements ConfigurationChangeListener<JEBackendCfg>, |
| | | ConfigurationAddListener<JEIndexCfg>, |
| | | ConfigurationDeleteListener<JEIndexCfg> |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | |
| | | public static final String REFERRAL_DATABASE_NAME = "referral"; |
| | | |
| | | /** |
| | | * The name of the state database. |
| | | */ |
| | | public static final String STATE_DATABASE_NAME = "state"; |
| | | |
| | | /** |
| | | * The attribute used to return a search index debug string to the client. |
| | | */ |
| | | public static final String ATTR_DEBUG_SEARCH_INDEX = "debugsearchindex"; |
| | |
| | | /** |
| | | * The backend configuration. |
| | | */ |
| | | private Config config; |
| | | |
| | | /** |
| | | * A list of JE database handles opened through this entryContainer. |
| | | * They will be closed by the entryContainer. |
| | | */ |
| | | private ArrayList<Database> databases; |
| | | private JEBackendCfg config; |
| | | |
| | | /** |
| | | * The JE database environment. |
| | |
| | | private DN2ID dn2id; |
| | | |
| | | /** |
| | | * The key comparator used for the DN database. |
| | | */ |
| | | private Comparator<byte[]> dn2idComparator; |
| | | |
| | | /** |
| | | * The entry database maps an entry ID (8 bytes) to a complete encoded entry. |
| | | */ |
| | | private ID2Entry id2entry; |
| | | |
| | | /** |
| | | * Compression and cryptographic options for the entry database. |
| | | */ |
| | | private DataConfig entryDataConfig; |
| | | |
| | | /** |
| | | * Index maps entry ID to an entry ID list containing its children. |
| | | */ |
| | | private Index id2children; |
| | |
| | | private DN2URI dn2uri; |
| | | |
| | | /** |
| | | * The state database maps a config DN to config entries. |
| | | */ |
| | | private State state; |
| | | |
| | | /** |
| | | * The set of attribute indexes. |
| | | */ |
| | | private HashMap<AttributeType, AttributeIndex> attrIndexMap; |
| | | |
| | | /** |
| | | * Cached value from config so they don't have to be retrieved per operation. |
| | | */ |
| | | private int deadlockRetryLimit; |
| | | |
| | | private int subtreeDeleteSizeLimit; |
| | | |
| | | private int indexEntryLimit; |
| | | |
| | | /** |
| | | * A read write lock to handle schema changes and bulk changes. |
| | | */ |
| | | private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); |
| | | final Lock sharedLock = lock.readLock(); |
| | | final Lock exclusiveLock = lock.writeLock(); |
| | | |
| | | /** |
| | | * Create a new entry entryContainer object. |
| | | * |
| | | * @param baseDN The baseDN this entry container will be responsible for |
| | |
| | | * @param config The configuration of the JE backend. |
| | | * @param env The JE environment to create this entryContainer in. |
| | | * @param rootContainer The root container this entry container is in. |
| | | * @throws ConfigException if a configuration related error occurs. |
| | | */ |
| | | public EntryContainer(DN baseDN, Backend backend, Config config, |
| | | public EntryContainer(DN baseDN, Backend backend, JEBackendCfg config, |
| | | Environment env, RootContainer rootContainer) |
| | | throws ConfigException |
| | | { |
| | | this.backend = backend; |
| | | this.baseDN = baseDN; |
| | | this.config = config; |
| | | this.env = env; |
| | | this.rootContainer = rootContainer; |
| | | |
| | | // Instantiate the list of database handles. |
| | | databases = new ArrayList<Database>(); |
| | | |
| | | // Instantiate indexes for id2children and id2subtree. |
| | | id2children = new Index(this, ID2CHILDREN_DATABASE_NAME, |
| | | new ID2CIndexer(), |
| | | config.getBackendIndexEntryLimit(), |
| | | 0); |
| | | id2subtree = new Index(this, ID2SUBTREE_DATABASE_NAME, |
| | | new ID2SIndexer(), |
| | | config.getBackendIndexEntryLimit(), |
| | | 0); |
| | | this.deadlockRetryLimit = config.getBackendDeadlockRetryLimit(); |
| | | this.subtreeDeleteSizeLimit = config.getBackendSubtreeDeleteSizeLimit(); |
| | | this.indexEntryLimit = config.getBackendIndexEntryLimit(); |
| | | |
| | | // Instantiate the attribute indexes. |
| | | attrIndexMap = new HashMap<AttributeType, AttributeIndex>(); |
| | | if (config != null && config.getIndexConfigMap() != null) |
| | | { |
| | | for (IndexConfig indexConfig : config.getIndexConfigMap().values()) |
| | | { |
| | | AttributeIndex index = new AttributeIndex(this, indexConfig); |
| | | attrIndexMap.put(indexConfig.getAttributeType(), index); |
| | | } |
| | | } |
| | | |
| | | entryDataConfig = new DataConfig(); |
| | | entryDataConfig.setCompressed(config.isEntriesCompressed()); |
| | | config.addJEChangeListener(this); |
| | | config.addJEIndexAddListener(this); |
| | | config.addJEIndexDeleteListener(this); |
| | | } |
| | | |
| | | /** |
| | | * Opens the entryContainer for reading and writing. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws ConfigException if a configuration related error occurs. |
| | | */ |
| | | public void open() |
| | | throws DatabaseException |
| | | throws DatabaseException, ConfigException |
| | | { |
| | | // Use this database config, duplicates are not allowed. |
| | | DatabaseConfig dbNodupsConfig = new DatabaseConfig(); |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(true); |
| | | |
| | | try |
| | | { |
| | | id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig, |
| | | ID2ENTRY_DATABASE_NAME); |
| | | DataConfig entryDataConfig = new DataConfig(); |
| | | entryDataConfig.setCompressed(config.isBackendEntriesCompressed()); |
| | | |
| | | id2entry = new ID2Entry(ID2ENTRY_DATABASE_NAME, entryDataConfig, |
| | | env, this); |
| | | id2entry.open(); |
| | | |
| | | // Set the dn2id ordering so that we can iterate through a subtree. |
| | | dn2idComparator = new KeyReverseComparator(); |
| | | DatabaseConfig dn2idConfig = new DatabaseConfig(); |
| | | dn2idConfig.setAllowCreate(true); |
| | | dn2idConfig.setTransactional(true); |
| | | dn2idConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id = new DN2ID(DN2ID_DATABASE_NAME, env, this); |
| | | dn2id.open(); |
| | | |
| | | id2children.open(dbNodupsConfig); |
| | | id2subtree.open(dbNodupsConfig); |
| | | state = new State(STATE_DATABASE_NAME, env, this); |
| | | state.open(); |
| | | |
| | | DatabaseConfig dn2uriConfig = new DatabaseConfig(); |
| | | dn2uriConfig.setSortedDuplicates(true); |
| | | dn2uriConfig.setAllowCreate(true); |
| | | dn2uriConfig.setTransactional(true); |
| | | dn2uriConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | id2children = new Index(ID2CHILDREN_DATABASE_NAME, |
| | | new ID2CIndexer(), state, |
| | | indexEntryLimit, 0, |
| | | env,this); |
| | | id2children.open(); |
| | | id2subtree = new Index(ID2SUBTREE_DATABASE_NAME, |
| | | new ID2SIndexer(), state, |
| | | indexEntryLimit, 0, |
| | | env, this); |
| | | id2subtree.open(); |
| | | |
| | | dn2uri = new DN2URI(REFERRAL_DATABASE_NAME, env, this); |
| | | dn2uri.open(); |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | for (String idx : config.listJEIndexes()) |
| | | { |
| | | index.open(dbNodupsConfig); |
| | | JEIndexCfg indexCfg = config.getJEIndex(idx); |
| | | |
| | | //TODO: When issue 1793 is fixed, use inherited default values in |
| | | //admin framework instead for the entry limit. |
| | | AttributeIndex index = |
| | | new AttributeIndex(indexCfg, state, |
| | | indexEntryLimit, |
| | | env, this); |
| | | index.open(); |
| | | attrIndexMap.put(indexCfg.getIndexAttribute(), index); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | catch (DatabaseException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | close(); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Opens the entryContainer for reading and writing without transactions. |
| | | * |
| | | * @param deferredWrite Indicates whether to open the entryContainer using |
| | | * the deferred write mode. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void openNonTransactional(boolean deferredWrite) |
| | | throws DatabaseException |
| | | { |
| | | // Use this database config, duplicates are not allowed. |
| | | DatabaseConfig dbNodupsConfig = new DatabaseConfig(); |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(false); |
| | | dbNodupsConfig.setDeferredWrite(deferredWrite); |
| | | |
| | | try |
| | | { |
| | | id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig, |
| | | ID2ENTRY_DATABASE_NAME); |
| | | id2entry.open(); |
| | | |
| | | // Set the dn2id ordering so that we can iterate through a subtree. |
| | | dn2idComparator = new KeyReverseComparator(); |
| | | DatabaseConfig dn2idConfig = new DatabaseConfig(); |
| | | dn2idConfig.setAllowCreate(true); |
| | | dn2idConfig.setTransactional(false); |
| | | dn2idConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2idConfig.setDeferredWrite(deferredWrite); |
| | | dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id.open(); |
| | | |
| | | id2children.open(dbNodupsConfig); |
| | | id2subtree.open(dbNodupsConfig); |
| | | |
| | | DatabaseConfig dn2uriConfig = new DatabaseConfig(); |
| | | dn2uriConfig.setSortedDuplicates(true); |
| | | dn2uriConfig.setAllowCreate(true); |
| | | dn2uriConfig.setTransactional(false); |
| | | dn2uriConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2uriConfig.setDeferredWrite(deferredWrite); |
| | | dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri.open(); |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | | index.open(dbNodupsConfig); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | close(); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Opens the entryContainer for reading only. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void openReadOnly() |
| | | throws DatabaseException |
| | | { |
| | | // Use this database config, duplicates are not allowed. |
| | | DatabaseConfig dbNodupsConfig = new DatabaseConfig(); |
| | | dbNodupsConfig.setReadOnly(true); |
| | | dbNodupsConfig.setAllowCreate(false); |
| | | dbNodupsConfig.setTransactional(false); |
| | | |
| | | try |
| | | { |
| | | id2entry = new ID2Entry(this, dbNodupsConfig, entryDataConfig, |
| | | ID2ENTRY_DATABASE_NAME); |
| | | id2entry.open(); |
| | | |
| | | // Set the dn2id ordering so that we can iterate through a subtree. |
| | | dn2idComparator = new KeyReverseComparator(); |
| | | DatabaseConfig dn2idConfig = new DatabaseConfig(); |
| | | dn2idConfig.setReadOnly(true); |
| | | dn2idConfig.setAllowCreate(false); |
| | | dn2idConfig.setTransactional(false); |
| | | dn2idConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2id = new DN2ID(this, dn2idConfig, DN2ID_DATABASE_NAME); |
| | | dn2id.open(); |
| | | |
| | | id2children.open(dbNodupsConfig); |
| | | id2subtree.open(dbNodupsConfig); |
| | | |
| | | DatabaseConfig dn2uriConfig = new DatabaseConfig(); |
| | | dn2uriConfig.setReadOnly(true); |
| | | dn2uriConfig.setSortedDuplicates(true); |
| | | dn2uriConfig.setAllowCreate(false); |
| | | dn2uriConfig.setTransactional(false); |
| | | dn2uriConfig.setBtreeComparator(dn2idComparator.getClass()); |
| | | dn2uri = new DN2URI(this, dn2uriConfig, REFERRAL_DATABASE_NAME); |
| | | dn2uri.open(); |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | | index.open(dbNodupsConfig); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | close(); |
| | | throw e; |
| | | throw de; |
| | | } |
| | | } |
| | | |
| | |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void close() |
| | | throws DatabaseException |
| | | throws DatabaseException |
| | | { |
| | | // Close each database handle that has been opened. |
| | | for (Database database : databases) |
| | | try |
| | | { |
| | | if (database.getConfig().getDeferredWrite()) |
| | | dn2id.close(); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | database.sync(); |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | database.close(); |
| | | } |
| | | try |
| | | { |
| | | id2entry.close(); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | try |
| | | { |
| | | id2children.close(); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | try |
| | | { |
| | | id2subtree.close(); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | try |
| | | { |
| | | state.close(); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | try |
| | | { |
| | | dn2uri.close(); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | | index.close(); |
| | | try |
| | | { |
| | | index.close(); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | config.removeJEChangeListener(this); |
| | | config.removeJEIndexAddListener(this); |
| | | config.removeJEIndexDeleteListener(this); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Retrieve all attribute indexes. |
| | | * |
| | | * @return All attribute indexes defined in this entry container. |
| | | */ |
| | | public Collection<AttributeIndex> getAttributeIndexes() |
| | | { |
| | | return attrIndexMap.values(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Determine the highest entryID in the entryContainer. |
| | | * The entryContainer must already be open. |
| | | * |
| | |
| | | lookthroughLimit)); |
| | | return; |
| | | } |
| | | int cmp = dn2idComparator.compare(key.getData(), end); |
| | | int cmp = dn2id.getComparator().compare(key.getData(), end); |
| | | if (cmp >= 0) |
| | | { |
| | | // We have gone past the ending value. |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void addEntry(Entry entry, AddOperation addOperation) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | TransactedOperation operation = |
| | | new AddEntryTransaction(entry); |
| | | new AddEntryTransaction(entry); |
| | | |
| | | invokeTransactedOperation(operation); |
| | | } |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | private void invokeTransactedOperation(TransactedOperation operation) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | // Attempt the operation under a transaction until it fails or completes. |
| | | boolean completed = false; |
| | | int retryRemaining = config.getDeadlockRetryLimit(); |
| | | int retryRemaining = deadlockRetryLimit; |
| | | while (!completed) |
| | | { |
| | | // Start a transaction. |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public abstract void invokeOperation(Transaction txn) |
| | | throws DatabaseException, DirectoryException, JebException; |
| | | throws DatabaseException, DirectoryException, JebException; |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void invokeOperation(Transaction txn) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | // Check whether the entry already exists. |
| | | if (dn2id.get(txn, entry.getDN()) != null) |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void deleteEntry(DN entryDN, DeleteOperation deleteOperation) |
| | | throws DirectoryException, DatabaseException, JebException |
| | | throws DirectoryException, DatabaseException, JebException |
| | | { |
| | | DeleteEntryTransaction operation = |
| | | new DeleteEntryTransaction(entryDN, deleteOperation); |
| | | new DeleteEntryTransaction(entryDN, deleteOperation); |
| | | |
| | | invokeTransactedOperation(operation); |
| | | |
| | |
| | | String message = getMessage(MSGID_JEB_SUBTREE_DELETE_SIZE_LIMIT_EXCEEDED, |
| | | operation.getDeletedEntryCount()); |
| | | throw new DirectoryException( |
| | | ResultCode.ADMIN_LIMIT_EXCEEDED, |
| | | message, |
| | | MSGID_JEB_SUBTREE_DELETE_SIZE_LIMIT_EXCEEDED); |
| | | ResultCode.ADMIN_LIMIT_EXCEEDED, |
| | | message, |
| | | MSGID_JEB_SUBTREE_DELETE_SIZE_LIMIT_EXCEEDED); |
| | | } |
| | | |
| | | String message = getMessage(MSGID_JEB_DELETED_ENTRY_COUNT, |
| | |
| | | Transaction txn, |
| | | DN leafDN, |
| | | EntryID leafID) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | // Check that the entry exists in id2entry and read its contents. |
| | | Entry entry = id2entry.get(txn, leafID); |
| | |
| | | BufferedIndex id2sBuffered, |
| | | Transaction txn, |
| | | DN leafDN) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | // Read the entry ID from dn2id. |
| | | EntryID leafID = dn2id.get(txn, leafDN); |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void invokeOperation(Transaction txn) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | // Check for referral entries above the target entry. |
| | | dn2uri.targetEntryReferrals(entryDN, null); |
| | | |
| | | // Determine whether this is a subtree delete. |
| | | int adminSizeLimit = config.getSubtreeDeleteSizeLimit(); |
| | | int adminSizeLimit = subtreeDeleteSizeLimit; |
| | | boolean isSubtreeDelete = false; |
| | | List<Control> controls = deleteOperation.getRequestControls(); |
| | | if (controls != null) |
| | |
| | | |
| | | // Step back until the key is less than the beginning value |
| | | while (status == OperationStatus.SUCCESS && |
| | | dn2idComparator.compare(key.getData(), begin) >= 0) |
| | | dn2id.getComparator().compare(key.getData(), begin) >= 0) |
| | | { |
| | | status = cursor.getPrev(key, data, LockMode.DEFAULT); |
| | | } |
| | |
| | | // Step back until we pass the ending value. |
| | | while (status == OperationStatus.SUCCESS) |
| | | { |
| | | int cmp = dn2idComparator.compare(key.getData(), end); |
| | | int cmp = dn2id.getComparator().compare(key.getData(), end); |
| | | if (cmp < 0) |
| | | { |
| | | // We have gone past the ending value. |
| | |
| | | * determination. |
| | | */ |
| | | public boolean entryExists(DN entryDN) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | |
| | |
| | | * @throws DatabaseException An error occurred during a database operation. |
| | | */ |
| | | public Entry getEntry(DN entryDN) |
| | | throws JebException, DatabaseException, DirectoryException |
| | | throws JebException, DatabaseException, DirectoryException |
| | | { |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | Entry entry = null; |
| | |
| | | */ |
| | | public void renameEntry(DN currentDN, Entry entry, |
| | | ModifyDNOperation modifyDNOperation) |
| | | throws DatabaseException, JebException, DirectoryException, |
| | | CancelledOperationException |
| | | throws DatabaseException, JebException, DirectoryException, |
| | | CancelledOperationException |
| | | { |
| | | TransactedOperation operation = |
| | | new RenameEntryTransaction(currentDN, entry, modifyDNOperation); |
| | | new RenameEntryTransaction(currentDN, entry, modifyDNOperation); |
| | | |
| | | invokeTransactedOperation(operation); |
| | | } |
| | |
| | | * @param modifyDNOperation The Modify DN operation to be performed. |
| | | */ |
| | | public RenameEntryTransaction(DN currentDN, Entry entry, |
| | | ModifyDNOperation modifyDNOperation) |
| | | ModifyDNOperation modifyDNOperation) |
| | | { |
| | | this.oldApexDN = currentDN; |
| | | this.oldSuperiorDN = getParentWithinBase(currentDN); |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void invokeOperation(Transaction txn) throws DatabaseException, |
| | | DirectoryException, |
| | | JebException |
| | | DirectoryException, |
| | | JebException |
| | | { |
| | | DN requestedNewSuperiorDN = null; |
| | | |
| | |
| | | * downwards. |
| | | */ |
| | | byte[] suffix = StaticUtils.getBytes("," + |
| | | oldApexDN.toNormalizedString()); |
| | | oldApexDN.toNormalizedString()); |
| | | |
| | | /* |
| | | * Set the ending value to a value of equal length but slightly |
| | |
| | | |
| | | // Step forward until the key is greater than the starting value. |
| | | while (status == OperationStatus.SUCCESS && |
| | | dn2idComparator.compare(key.getData(), begin) <= 0) |
| | | dn2id.getComparator().compare(key.getData(), begin) <= 0) |
| | | { |
| | | status = cursor.getNext(key, data, LockMode.RMW); |
| | | } |
| | |
| | | // Step forward until we pass the ending value. |
| | | while (status == OperationStatus.SUCCESS) |
| | | { |
| | | int cmp = dn2idComparator.compare(key.getData(), end); |
| | | int cmp = dn2id.getComparator().compare(key.getData(), end); |
| | | if (cmp >= 0) |
| | | { |
| | | // We have gone past the ending value. |
| | |
| | | private void moveApexEntry(Transaction txn, |
| | | EntryID oldID, EntryID newID, |
| | | Entry oldEntry, Entry newEntry) |
| | | throws JebException, DirectoryException, DatabaseException |
| | | throws JebException, DirectoryException, DatabaseException |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | DN newDN = newEntry.getDN(); |
| | |
| | | if (newParentDN != null) |
| | | { |
| | | EntryID parentID = dn2id.get(txn, newParentDN); |
| | | id2cBuffered.insertID(config.getBackendIndexEntryLimit(), |
| | | id2cBuffered.insertID(indexEntryLimit, |
| | | parentID.getDatabaseEntry().getData(), |
| | | newID); |
| | | } |
| | |
| | | for (DN dn = newParentDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | EntryID nodeID = dn2id.get(txn, dn); |
| | | id2sBuffered.insertID(config.getBackendIndexEntryLimit(), |
| | | id2sBuffered.insertID(indexEntryLimit, |
| | | nodeID.getDatabaseEntry().getData(), newID); |
| | | } |
| | | |
| | |
| | | */ |
| | | private void renameApexEntry(Transaction txn, EntryID entryID, |
| | | Entry oldEntry, Entry newEntry) |
| | | throws DirectoryException, DatabaseException, JebException |
| | | throws DirectoryException, DatabaseException, JebException |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | DN newDN = newEntry.getDN(); |
| | |
| | | private void moveSubordinateEntry(Transaction txn, |
| | | EntryID oldID, EntryID newID, |
| | | Entry oldEntry, DN newDN) |
| | | throws JebException, DirectoryException, DatabaseException |
| | | throws JebException, DirectoryException, DatabaseException |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | DN newParentDN = getParentWithinBase(newDN); |
| | |
| | | if (newParentDN != null) |
| | | { |
| | | EntryID parentID = dn2id.get(txn, newParentDN); |
| | | id2cBuffered.insertID(config.getBackendIndexEntryLimit(), |
| | | id2cBuffered.insertID(indexEntryLimit, |
| | | parentID.getDatabaseEntry().getData(), |
| | | newID); |
| | | } |
| | |
| | | if (!newID.equals(oldID) || dn.isAncestorOf(newSuperiorDN)) |
| | | { |
| | | EntryID nodeID = dn2id.get(txn, dn); |
| | | id2sBuffered.insertID(config.getBackendIndexEntryLimit(), |
| | | id2sBuffered.insertID(indexEntryLimit, |
| | | nodeID.getDatabaseEntry().getData(), newID); |
| | | } |
| | | } |
| | |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | private void renameSubordinateEntry(Transaction txn, EntryID entryID, |
| | | Entry oldEntry, DN newDN) |
| | | throws DirectoryException, DatabaseException |
| | | Entry oldEntry, DN newDN) |
| | | throws DirectoryException, DatabaseException |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | private void indexInsertEntry(Transaction txn, Entry entry, EntryID entryID) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | private void indexRemoveEntry(Transaction txn, Entry entry, EntryID entryID) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | |
| | | private void indexModifications(Transaction txn, Entry oldEntry, |
| | | Entry newEntry, |
| | | EntryID entryID, List<Modification> mods) |
| | | throws DatabaseException |
| | | throws DatabaseException |
| | | { |
| | | // Process in index configuration order. |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | |
| | | { |
| | | try |
| | | { |
| | | removeDatabase(DN2ID_DATABASE_NAME); |
| | | removeDatabase(dn2id); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | } |
| | | try |
| | | { |
| | | removeDatabase(ID2ENTRY_DATABASE_NAME); |
| | | removeDatabase(id2entry); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | } |
| | | try |
| | | { |
| | | removeDatabase(ID2CHILDREN_DATABASE_NAME); |
| | | removeDatabase(id2children); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | } |
| | | try |
| | | { |
| | | removeDatabase(ID2SUBTREE_DATABASE_NAME); |
| | | removeDatabase(id2subtree); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | try |
| | | { |
| | | removeDatabase(state); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | { |
| | | try |
| | | { |
| | | index.removeIndex(); |
| | | removeAttributeIndex(index); |
| | | } |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | |
| | | } |
| | | } |
| | | } |
| | | attrIndexMap.clear(); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get a list of the databases opened by this entryContainer. There will be |
| | | * only one handle in the list for each database, regardless of the number |
| | | * of handles open for a given database. |
| | | * @param dbList A list of JE database handles. |
| | | * Get a list of the databases opened by this entryContainer. |
| | | * @param dbList A list of database containers. |
| | | */ |
| | | public void listDatabases(List<Database> dbList) |
| | | public void listDatabases(List<DatabaseContainer> dbList) |
| | | { |
| | | // There may be more than one handle open for a given database |
| | | // so we eliminate duplicates here. |
| | | HashSet<String> set = new HashSet<String>(); |
| | | for (Database db : databases) |
| | | dbList.add(dn2id); |
| | | dbList.add(id2entry); |
| | | dbList.add(dn2uri); |
| | | dbList.add(id2children); |
| | | dbList.add(id2subtree); |
| | | |
| | | for(AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | | try |
| | | { |
| | | if (!set.contains(db.getDatabaseName())) |
| | | { |
| | | set.add(db.getDatabaseName()); |
| | | dbList.add(db); |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | index.listDatabases(dbList); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Opens a JE database in this entryContainer. The resulting database handle |
| | | * must not be closed by the caller, as it will be closed by the |
| | | * entryContainer. If the provided database configuration is transactional, |
| | | * a transaction will be created and used to perform the open. |
| | | * <p> |
| | | * Note that a database can be opened multiple times and will result in |
| | | * multiple unique handles to the database. This is used for example to |
| | | * give each server thread its own database handle to eliminate contention |
| | | * that could occur on a single handle. |
| | | * |
| | | * @param dbConfig The JE database configuration to be used to open the |
| | | * database. |
| | | * @param name The short database name, to which the entryContainer name |
| | | * will be added. |
| | | * @return A new JE database handle. |
| | | * @throws DatabaseException If an error occurs while attempting to open the |
| | | * database. |
| | | */ |
| | | public synchronized Database openDatabase(DatabaseConfig dbConfig, |
| | | String name) |
| | | throws DatabaseException |
| | | { |
| | | Database database; |
| | | |
| | | StringBuilder builder = new StringBuilder(); |
| | | buildDatabaseName(builder, name); |
| | | String fullName = builder.toString(); |
| | | |
| | | if (dbConfig.getTransactional()) |
| | | { |
| | | // Open the database under a transaction. |
| | | Transaction txn = beginTransaction(); |
| | | try |
| | | { |
| | | database = env.openDatabase(txn, fullName, dbConfig); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("open db=%s txnid=%d", |
| | | database.getDatabaseName(), |
| | | txn.getId()); |
| | | } |
| | | transactionCommit(txn); |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | transactionAbort(txn); |
| | | throw e; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | database = env.openDatabase(null, fullName, dbConfig); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("open db=%s txnid=none", |
| | | database.getDatabaseName()); |
| | | } |
| | | } |
| | | |
| | | // Insert into the list of database handles. |
| | | databases.add(database); |
| | | |
| | | return database; |
| | | } |
| | | |
| | | /** |
| | | * Begin a leaf transaction using the default configuration. |
| | | * Provides assertion debug logging. |
| | | * @return A JE transaction handle. |
| | |
| | | * a new transaction. |
| | | */ |
| | | public Transaction beginTransaction() |
| | | throws DatabaseException |
| | | throws DatabaseException |
| | | { |
| | | Transaction parentTxn = null; |
| | | TransactionConfig txnConfig = null; |
| | |
| | | * the transaction. |
| | | */ |
| | | public static void transactionCommit(Transaction txn) |
| | | throws DatabaseException |
| | | throws DatabaseException |
| | | { |
| | | if (txn != null) |
| | | { |
| | |
| | | * transaction. |
| | | */ |
| | | public static void transactionAbort(Transaction txn) |
| | | throws DatabaseException |
| | | throws DatabaseException |
| | | { |
| | | if (txn != null) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Insert a record into a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.putNoOverwrite method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus insert(Database database, Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.putNoOverwrite(txn, key, data); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, txn, key, |
| | | data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Insert a record into a JE database through a cursor, with optional debug |
| | | * logging. This is a simple wrapper around the JE Cursor.putNoOverwrite |
| | | * method. |
| | | * @param cursor The JE cursor handle. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus cursorInsert(Cursor cursor, |
| | | DatabaseEntry key, |
| | | DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = cursor.putNoOverwrite(key, data); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, |
| | | cursor.getDatabase(), null, key, data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Replace or insert a record into a JE database, with optional debug logging. |
| | | * This is a simple wrapper around the JE Database.put method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus put(Database database, Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.put(txn, key, data); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, txn, key, |
| | | data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Replace or insert a record into a JE database through a cursor, with |
| | | * optional debug logging. This is a simple wrapper around the JE Cursor.put |
| | | * method. |
| | | * @param cursor The JE cursor handle. |
| | | * @param key The record key. |
| | | * @param data The record value. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus cursorPut(Cursor cursor, |
| | | DatabaseEntry key, |
| | | DatabaseEntry data) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = cursor.put(key, data); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, |
| | | cursor.getDatabase(), null, key, data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Read a record from a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.get method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The key of the record to be read. |
| | | * @param data The record value returned as output. Its byte array does not |
| | | * need to be initialized by the caller. |
| | | * @param lockMode The JE locking mode to be used for the read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus read(Database database, Transaction txn, |
| | | DatabaseEntry key, DatabaseEntry data, |
| | | LockMode lockMode) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.get(txn, key, data, lockMode); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, txn, key, |
| | | data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Read a record from a JE database through a cursor, with optional debug |
| | | * logging. This is a simple wrapper around the JE Cursor.getSearchKey method. |
| | | * @param cursor The JE cursor handle. |
| | | * @param key The key of the record to be read. |
| | | * @param data The record value returned as output. Its byte array does not |
| | | * need to be initialized by the caller. |
| | | * @param lockMode The JE locking mode to be used for the read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus cursorRead(Cursor cursor, |
| | | DatabaseEntry key, |
| | | DatabaseEntry data, |
| | | LockMode lockMode) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = cursor.getSearchKey(key, data, lockMode); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, |
| | | cursor.getDatabase(), null, key, data); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Delete a record from a JE database, with optional debug logging. This is a |
| | | * simple wrapper around the JE Database.delete method. |
| | | * @param database The JE database handle. |
| | | * @param txn The JE transaction handle, or null if none. |
| | | * @param key The key of the record to be read. |
| | | * @return The operation status. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static OperationStatus delete(Database database, Transaction txn, |
| | | DatabaseEntry key) |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status = database.delete(txn, key); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, status, database, txn, key, |
| | | null); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /** |
| | | * Get the count of key/data pairs in the database in a JE database. |
| | | * This is a simple wrapper around the JE Database.count method. |
| | | * @param database the JE database handle. |
| | | * @return The count of key/data pairs in the database. |
| | | * @throws DatabaseException If an error occurs in the JE operation. |
| | | */ |
| | | public static long count(Database database) throws DatabaseException |
| | | { |
| | | long count = database.count(); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugJEAccess(DebugLogLevel.VERBOSE, OperationStatus.SUCCESS, |
| | | database, null, null, null); |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | /** |
| | | * Remove a database from disk. |
| | | * |
| | | * @param name The short database name, to which the entryContainer name will |
| | | * be added. |
| | | * @param database The database container to remove. |
| | | * @throws DatabaseException If an error occurs while attempting to delete the |
| | | * database. |
| | | */ |
| | | public void removeDatabase(String name) throws DatabaseException |
| | | public void removeDatabase(DatabaseContainer database) |
| | | throws DatabaseException |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | buildDatabaseName(builder, name); |
| | | String fullName = builder.toString(); |
| | | env.removeDatabase(null, fullName); |
| | | database.close(); |
| | | env.removeDatabase(null, database.getName()); |
| | | if(database instanceof Index) |
| | | { |
| | | state.removeIndexTrustState(null, (Index)database); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Removes a attribute index from disk. |
| | | * |
| | | * @param index The attribute index to remove. |
| | | * @throws DatabaseException If an JE database error occurs while attempting |
| | | * to delete the index. |
| | | */ |
| | | public void removeAttributeIndex(AttributeIndex index) |
| | | throws DatabaseException |
| | | { |
| | | index.close(); |
| | | if(index.equalityIndex != null) |
| | | { |
| | | env.removeDatabase(null, index.equalityIndex.getName()); |
| | | state.removeIndexTrustState(null, index.equalityIndex); |
| | | } |
| | | if(index.presenceIndex != null) |
| | | { |
| | | env.removeDatabase(null, index.presenceIndex.getName()); |
| | | state.removeIndexTrustState(null, index.presenceIndex); |
| | | } |
| | | if(index.substringIndex != null) |
| | | { |
| | | env.removeDatabase(null, index.substringIndex.getName()); |
| | | state.removeIndexTrustState(null, index.substringIndex); |
| | | } |
| | | if(index.orderingIndex != null) |
| | | { |
| | | env.removeDatabase(null, index.orderingIndex.getName()); |
| | | state.removeIndexTrustState(null, index.orderingIndex); |
| | | } |
| | | if(index.approximateIndex != null) |
| | | { |
| | | env.removeDatabase(null, index.approximateIndex.getName()); |
| | | state.removeIndexTrustState(null, index.approximateIndex); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | return dn.getParent(); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized boolean isConfigurationChangeAcceptable( |
| | | JEBackendCfg cfg, List<String> unacceptableReasons) |
| | | { |
| | | // This is always true because only all config attributes used |
| | | // by the entry container should be validated by the admin framework. |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized ConfigChangeResult applyConfigurationChange( |
| | | JEBackendCfg cfg) |
| | | { |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | if(config.getBackendIndexEntryLimit() != cfg.getBackendIndexEntryLimit()) |
| | | { |
| | | for(AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | | if(index.setBackendIndexEntryLimit(cfg.getBackendIndexEntryLimit())) |
| | | { |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, |
| | | index.getAttributeType().getNameOrOID()); |
| | | messages.add(message); |
| | | } |
| | | index.setBackendIndexEntryLimit(cfg.getBackendIndexEntryLimit()); |
| | | } |
| | | |
| | | if(id2children.setIndexEntryLimit(cfg.getBackendIndexEntryLimit())) |
| | | { |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, id2children.getName()); |
| | | messages.add(message); |
| | | } |
| | | |
| | | if(id2subtree.setIndexEntryLimit(cfg.getBackendIndexEntryLimit())) |
| | | { |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD; |
| | | String message = getMessage(msgID, id2subtree.getName()); |
| | | messages.add(message); |
| | | } |
| | | } |
| | | |
| | | DataConfig entryDataConfig = new DataConfig(); |
| | | entryDataConfig.setCompressed(cfg.isBackendEntriesCompressed()); |
| | | id2entry.setDataConfig(entryDataConfig); |
| | | |
| | | this.config = cfg; |
| | | this.deadlockRetryLimit = config.getBackendDeadlockRetryLimit(); |
| | | this.subtreeDeleteSizeLimit = config.getBackendSubtreeDeleteSizeLimit(); |
| | | this.indexEntryLimit = config.getBackendIndexEntryLimit(); |
| | | return new ConfigChangeResult(ResultCode.SUCCESS, |
| | | adminActionRequired, messages); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized boolean isConfigurationAddAcceptable( |
| | | JEIndexCfg cfg, List<String> unacceptableReasons) |
| | | { |
| | | // TODO: validate more before returning true? |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized ConfigChangeResult applyConfigurationAdd(JEIndexCfg cfg) |
| | | { |
| | | ConfigChangeResult ccr; |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | try |
| | | { |
| | | AttributeIndex index = |
| | | new AttributeIndex(cfg, state, |
| | | indexEntryLimit, |
| | | env, this); |
| | | index.open(); |
| | | attrIndexMap.put(cfg.getIndexAttribute(), index); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(e)); |
| | | ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(), |
| | | adminActionRequired, |
| | | messages); |
| | | return ccr; |
| | | } |
| | | |
| | | adminActionRequired = true; |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | messages.add(getMessage(msgID, cfg.getIndexAttribute().getNameOrOID())); |
| | | return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired, |
| | | messages); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized boolean isConfigurationDeleteAcceptable( |
| | | JEIndexCfg cfg, List<String> unacceptableReasons) |
| | | { |
| | | // TODO: validate more before returning true? |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public synchronized ConfigChangeResult applyConfigurationDelete( |
| | | JEIndexCfg cfg) |
| | | { |
| | | ConfigChangeResult ccr; |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | exclusiveLock.lock(); |
| | | try |
| | | { |
| | | AttributeIndex index = attrIndexMap.get(cfg.getIndexAttribute()); |
| | | removeAttributeIndex(index); |
| | | attrIndexMap.remove(cfg.getIndexAttribute()); |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(de)); |
| | | ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(), |
| | | adminActionRequired, |
| | | messages); |
| | | return ccr; |
| | | } |
| | | finally |
| | | { |
| | | exclusiveLock.unlock(); |
| | | } |
| | | |
| | | return new ConfigChangeResult(ResultCode.SUCCESS, adminActionRequired, |
| | | messages); |
| | | } |
| | | |
| | | /** |
| | | * Get the environment config of the JE environment used in this entry |
| | | * container. |
| | | * |
| | | * @return The environment config of the JE environment. |
| | | * @throws DatabaseException If an error occurs while retriving the |
| | | * configuration object. |
| | | */ |
| | | public EnvironmentConfig getEnvironmentConfig() |
| | | throws DatabaseException |
| | | { |
| | | return env.getConfig(); |
| | | } |
| | | |
| | | /** |
| | | * Clear the contents for a database from disk. |
| | | * |
| | | * @param txn A transaction object or null if its not required. |
| | | * @param database The database to clear. |
| | | * @return The number of records deleted. |
| | | * @throws DatabaseException if a JE database error occurs. |
| | | */ |
| | | public long clearDatabase(Transaction txn, DatabaseContainer database) |
| | | throws DatabaseException |
| | | { |
| | | database.close(); |
| | | long count = env.truncateDatabase(txn, database.getName(), true); |
| | | database.open(); |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("Cleared %d existing records from the " + |
| | | "database %s", count, database.getName()); |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | /** |
| | | * Clear the contents for a attribute index from disk. |
| | | * |
| | | * @param txn A transaction object or null if its not required. |
| | | * @param index The attribute index to clear. |
| | | * @return The number of records deleted. |
| | | * @throws DatabaseException if a JE database error occurs. |
| | | */ |
| | | public long clearAttributeIndex(Transaction txn, AttributeIndex index) |
| | | throws DatabaseException |
| | | { |
| | | long count = 0; |
| | | |
| | | index.close(); |
| | | if(index.equalityIndex != null) |
| | | { |
| | | count += env.truncateDatabase(txn, index.equalityIndex.getName(), true); |
| | | } |
| | | if(index.presenceIndex != null) |
| | | { |
| | | count += env.truncateDatabase(txn, index.presenceIndex.getName(), true); |
| | | } |
| | | if(index.substringIndex != null) |
| | | { |
| | | count += env.truncateDatabase(txn, index.substringIndex.getName(), true); |
| | | } |
| | | if(index.orderingIndex != null) |
| | | { |
| | | count += env.truncateDatabase(txn, index.orderingIndex.getName(), true); |
| | | } |
| | | if(index.approximateIndex != null) |
| | | { |
| | | count += env.truncateDatabase(txn, index.approximateIndex.getName(), |
| | | true); |
| | | } |
| | | index.open(); |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("Cleared %d existing records from the " + |
| | | "index %s", count, index.getAttributeType().getNameOrOID()); |
| | | } |
| | | return count; |
| | | } |
| | | } |
| | |
| | | new AttributeIndex.KeyComparator(); |
| | | |
| | | /** |
| | | * The attribute index configuration for which this instance will |
| | | * The attribute type for which this instance will |
| | | * generate index keys. |
| | | */ |
| | | private IndexConfig indexConfig; |
| | | private AttributeType attributeType; |
| | | |
| | | |
| | | /** |
| | | * Create a new attribute equality indexer for the given index configuration. |
| | | * @param indexConfig The index configuration for which an indexer is |
| | | * Create a new attribute equality indexer for the given attribute type. |
| | | * @param attributeType The attribute type for which an indexer is |
| | | * required. |
| | | */ |
| | | public EqualityIndexer(IndexConfig indexConfig) |
| | | public EqualityIndexer(AttributeType attributeType) |
| | | { |
| | | this.indexConfig = indexConfig; |
| | | this.attributeType = attributeType; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public String toString() |
| | | { |
| | | return indexConfig.getAttributeType().getNameOrOID() + ".equality"; |
| | | return attributeType.getNameOrOID() + ".equality"; |
| | | } |
| | | |
| | | /** |
| | |
| | | Set<ASN1OctetString> keys) throws DatabaseException |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.getAttribute(indexConfig.getAttributeType()); |
| | | entry.getAttribute(attributeType); |
| | | if (attrList != null) |
| | | { |
| | | indexAttribute(attrList, keys); |
| | |
| | | { |
| | | List<Attribute> attrList; |
| | | |
| | | attrList = oldEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = oldEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> oldSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, oldSet); |
| | | |
| | | attrList = newEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = newEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> newSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, newSet); |
| | | |
| | |
| | | |
| | | List<Attribute> beforeList; |
| | | List<Attribute> afterList; |
| | | beforeList = oldEntry.getAttribute(indexConfig.getAttributeType()); |
| | | afterList = newEntry.getAttribute(indexConfig.getAttributeType()); |
| | | beforeList = oldEntry.getAttribute(attributeType); |
| | | afterList = newEntry.getAttribute(attributeType); |
| | | |
| | | boolean hasOptions = false; |
| | | |
| | |
| | | { |
| | | Attribute modAttr = mod.getAttribute(); |
| | | AttributeType modAttrType = modAttr.getAttributeType(); |
| | | if (modAttrType.equals(indexConfig.getAttributeType())) |
| | | if (modAttrType.equals(attributeType)) |
| | | { |
| | | if (modAttr.hasOptions()) |
| | | { |
| | |
| | | { |
| | | for (EntryContainer exportContainer : exportContainers) |
| | | { |
| | | exportContainer(exportContainer); |
| | | exportContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | exportContainer(exportContainer); |
| | | } |
| | | finally |
| | | { |
| | | exportContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | finally |
| | |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | |
| | | import com.sleepycat.je.Cursor; |
| | | import com.sleepycat.je.CursorConfig; |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.DatabaseConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.OperationStatus; |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.*; |
| | | |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | |
| | | * the entry ID and the value is the entry contents. |
| | | * |
| | | */ |
| | | public class ID2Entry |
| | | public class ID2Entry extends DatabaseContainer |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * The database entryContainer. |
| | | */ |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | | */ |
| | | private DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * Parameters for compression and encryption. |
| | | */ |
| | | private DataConfig dataConfig; |
| | | |
| | | /** |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | | /** |
| | | * A cached per-thread JE database handle. |
| | | */ |
| | | private ThreadLocal<Database> threadLocalDatabase = |
| | | new ThreadLocal<Database>(); |
| | | |
| | | /** |
| | | * Create a new ID2Entry object. |
| | | * @param entryContainer The entryContainer of the entry database. |
| | | * @param dbConfig The JE database configuration to be used to open the |
| | | * underlying JE database. |
| | | * |
| | | * @param name The name of the entry database. |
| | | * @param dataConfig The desired compression and encryption options for data |
| | | * stored in the entry database. |
| | | * @param name The name of the entry database. |
| | | * @param env The JE Environment. |
| | | * @param entryContainer The entryContainer of the entry database. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * |
| | | */ |
| | | public ID2Entry(EntryContainer entryContainer, DatabaseConfig dbConfig, |
| | | DataConfig dataConfig, String name) |
| | | ID2Entry(String name, DataConfig dataConfig, |
| | | Environment env, EntryContainer entryContainer) |
| | | throws DatabaseException |
| | | { |
| | | this.entryContainer = entryContainer; |
| | | this.dbConfig = dbConfig.cloneConfig(); |
| | | this.name = name; |
| | | super(name, env, entryContainer); |
| | | this.dataConfig = dataConfig; |
| | | } |
| | | |
| | | /** |
| | | * Open the entry database. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void open() throws DatabaseException |
| | | { |
| | | getDatabase(); |
| | | } |
| | | DatabaseConfig dbNodupsConfig = new DatabaseConfig(); |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | private Database getDatabase() throws DatabaseException |
| | | { |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("JE ID2Entry database %s opened with %d records.", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | dbNodupsConfig.setReadOnly(true); |
| | | dbNodupsConfig.setAllowCreate(false); |
| | | dbNodupsConfig.setTransactional(false); |
| | | } |
| | | return database; |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(false); |
| | | dbNodupsConfig.setDeferredWrite(true); |
| | | } |
| | | else |
| | | { |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(true); |
| | | } |
| | | |
| | | this.dbConfig = dbNodupsConfig; |
| | | } |
| | | |
| | | /** |
| | |
| | | DatabaseEntry data = entryData(entry); |
| | | |
| | | OperationStatus status; |
| | | status = EntryContainer.insert(getDatabase(), txn, key, data); |
| | | status = insert(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = entryData(entry); |
| | | |
| | | OperationStatus status; |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | status = put(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | throws DatabaseException |
| | | { |
| | | OperationStatus status; |
| | | status = EntryContainer.put(getDatabase(), txn, key, data); |
| | | status = put(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | { |
| | | DatabaseEntry key = id.getDatabaseEntry(); |
| | | |
| | | OperationStatus status = EntryContainer.delete(getDatabase(), txn, key); |
| | | OperationStatus status = delete(txn, key); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, |
| | | status = read(txn, key, data, |
| | | LockMode.DEFAULT); |
| | | |
| | | if (status != OperationStatus.SUCCESS) |
| | |
| | | } |
| | | |
| | | /** |
| | | * Open a database cursor on the entry database. |
| | | * Set the desired compression and encryption options for data |
| | | * stored in the entry database. |
| | | * |
| | | * @param txn The database transaction, or null if none. |
| | | * @param cursorConfig The JE cursor configuration. |
| | | * @return A new cursor. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @param dataConfig The desired compression and encryption options for data |
| | | * stored in the entry database. |
| | | */ |
| | | public Cursor openCursor(Transaction txn, CursorConfig cursorConfig) |
| | | throws DatabaseException |
| | | public void setDataConfig(DataConfig dataConfig) |
| | | { |
| | | Database database = getDatabase(); |
| | | return database.openCursor(txn, cursorConfig); |
| | | } |
| | | |
| | | /** |
| | | * Get the count of the number of entries stored. |
| | | * |
| | | * @return The number of entries stored. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public long getRecordCount() throws DatabaseException |
| | | { |
| | | return EntryContainer.count(getDatabase()); |
| | | } |
| | | |
| | | /** |
| | | * Get a string representation of this object. |
| | | * @return return A string representation of this object. |
| | | */ |
| | | public String toString() |
| | | { |
| | | return name; |
| | | this.dataConfig = dataConfig; |
| | | } |
| | | } |
| | |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.LDIFImportConfig; |
| | | import org.opends.server.util.LDIFReader; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | |
| | | import java.util.concurrent.BlockingQueue; |
| | | import java.util.concurrent.atomic.AtomicLong; |
| | |
| | | /** |
| | | * The configuration of the destination backend. |
| | | */ |
| | | private Config config; |
| | | private JEBackendCfg config; |
| | | |
| | | /** |
| | | * The requested LDIF import configuration. |
| | |
| | | * Set the configuration of the destination backend. |
| | | * @param config The destination backend configuration. |
| | | */ |
| | | public void setConfig(Config config) |
| | | public void setConfig(JEBackendCfg config) |
| | | { |
| | | this.config = config; |
| | | } |
| | |
| | | * Get the configuration of the destination backend. |
| | | * @return The destination backend configuration. |
| | | */ |
| | | public Config getConfig() |
| | | public JEBackendCfg getConfig() |
| | | { |
| | | return config; |
| | | } |
| | |
| | | import com.sleepycat.je.StatsConfig; |
| | | import com.sleepycat.je.Transaction; |
| | | |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.messages.JebMessages; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Entry; |
| | |
| | | import org.opends.server.types.ResultCode; |
| | | import org.opends.server.util.LDIFException; |
| | | import org.opends.server.util.LDIFReader; |
| | | import org.opends.server.util.StaticUtils; |
| | | import static org.opends.server.util.StaticUtils.getFileForPath; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.Timer; |
| | | import java.util.TimerTask; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | import java.util.concurrent.CopyOnWriteArrayList; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | import static org.opends.server.messages.JebMessages. |
| | | MSGID_JEB_IMPORT_ENTRY_EXISTS; |
| | | MSGID_JEB_IMPORT_ENTRY_EXISTS; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages. |
| | | MSGID_JEB_IMPORT_PARENT_NOT_FOUND; |
| | | MSGID_JEB_IMPORT_PARENT_NOT_FOUND; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | |
| | | /** |
| | | * Import from LDIF to a JE backend. |
| | |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | /** |
| | | * The backend instance we are importing into. |
| | | */ |
| | | private Backend backend; |
| | | |
| | | /** |
| | | * The JE backend configuration. |
| | | */ |
| | | private Config config; |
| | | private JEBackendCfg config; |
| | | |
| | | /** |
| | | * The root container used for this import job. |
| | |
| | | * Map of base DNs to their import context. |
| | | */ |
| | | private HashMap<DN,ImportContext> importMap = |
| | | new HashMap<DN, ImportContext>(); |
| | | new HashMap<DN, ImportContext>(); |
| | | |
| | | /** |
| | | * The number of entries imported. |
| | |
| | | */ |
| | | private long progressInterval = 10000; |
| | | |
| | | |
| | | /** |
| | | * The import worker threads. |
| | | */ |
| | | private CopyOnWriteArrayList<ImportThread> threads; |
| | | |
| | | /** |
| | | * Create a new import job. |
| | | * |
| | | * @param backend The backend performing the import. |
| | | * @param config The backend configuration. |
| | | * @param ldifImportConfig The LDIF import configuration. |
| | | */ |
| | | public ImportJob(Backend backend, Config config, |
| | | LDIFImportConfig ldifImportConfig) |
| | | public ImportJob(LDIFImportConfig ldifImportConfig) |
| | | { |
| | | this.backend = backend; |
| | | this.config = config; |
| | | this.ldifImportConfig = ldifImportConfig; |
| | | this.threads = new CopyOnWriteArrayList<ImportThread>(); |
| | | } |
| | | |
| | | /** |
| | |
| | | * processes the LDIF file, then merges the resulting intermediate |
| | | * files to load the index databases. |
| | | * |
| | | * @param rootContainer The root container to import into. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws IOException If a problem occurs while opening the LDIF file for |
| | | * reading, or while reading from the LDIF file. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void importLDIF() |
| | | public void importLDIF(RootContainer rootContainer) |
| | | throws DatabaseException, IOException, JebException |
| | | { |
| | | /* |
| | | envConfig.setConfigParam("je.env.runCleaner", "false"); |
| | | envConfig.setConfigParam("je.log.numBuffers", "2"); |
| | | envConfig.setConfigParam("je.log.bufferSize", "15000000"); |
| | | envConfig.setConfigParam("je.log.totalBufferBytes", "30000000"); |
| | | envConfig.setConfigParam("je.log.fileMax", "100000000"); |
| | | */ |
| | | |
| | | // Create an LDIF reader. Throws an exception if the file does not exist. |
| | | reader = new LDIFReader(ldifImportConfig); |
| | | this.rootContainer = rootContainer; |
| | | this.config = rootContainer.getConfiguration(); |
| | | |
| | | int msgID; |
| | | String message; |
| | | long startTime; |
| | | |
| | | try |
| | | { |
| | | rootContainer = new RootContainer(config, backend); |
| | | if (ldifImportConfig.appendToExistingData()) |
| | | { |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | false, true, true, true, true, false); |
| | | } |
| | | else |
| | | { |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | false, true, false, false, false, false); |
| | | } |
| | | |
| | | if (!ldifImportConfig.appendToExistingData()) |
| | | { |
| | | // We have the writer lock on the environment, now delete the |
| | | // environment and re-open it. Only do this when we are |
| | | // importing to all the base DNs in the backend. |
| | | rootContainer.close(); |
| | | EnvManager.removeFiles(config.getBackendDirectory().getPath()); |
| | | rootContainer.open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | false, true, false, false, false, false); |
| | | } |
| | | |
| | | // Divide the total buffer size by the number of threads |
| | | // and give that much to each thread. |
| | | int importThreadCount = config.getImportThreadCount(); |
| | | long bufferSize = config.getImportBufferSize() / |
| | | (importThreadCount*config.getBaseDNs().length); |
| | | int importThreadCount = config.getBackendImportThreadCount(); |
| | | long bufferSize = config.getBackendImportBufferSize() / |
| | | (importThreadCount*rootContainer.getBaseDNs().size()); |
| | | |
| | | msgID = MSGID_JEB_IMPORT_THREAD_COUNT; |
| | | message = getMessage(msgID, importThreadCount); |
| | |
| | | message, msgID); |
| | | |
| | | TRACER.debugInfo( |
| | | rootContainer.getEnvironmentConfig().toString()); |
| | | |
| | | |
| | | rootContainer.openEntryContainers(config.getBaseDNs()); |
| | | rootContainer.getEnvironmentConfig().toString()); |
| | | |
| | | // Create the import contexts for each base DN. |
| | | DN baseDN; |
| | |
| | | |
| | | // Create an entry queue. |
| | | LinkedBlockingQueue<Entry> queue = |
| | | new LinkedBlockingQueue<Entry>(config.getImportQueueSize()); |
| | | new LinkedBlockingQueue<Entry>(config.getBackendImportQueueSize()); |
| | | importContext.setQueue(queue); |
| | | |
| | | importMap.put(baseDN, importContext); |
| | |
| | | // Make a note of the time we started. |
| | | startTime = System.currentTimeMillis(); |
| | | |
| | | // Create a temporary work directory. |
| | | File tempDir = getFileForPath(config.getBackendImportTempDirectory()); |
| | | if(!tempDir.exists() && !tempDir.mkdir()) |
| | | { |
| | | msgID = MSGID_JEB_IMPORT_CREATE_TMPDIR_ERROR; |
| | | String msg = getMessage(msgID, tempDir); |
| | | throw new IOException(msg); |
| | | } |
| | | |
| | | if (tempDir.listFiles() != null) |
| | | { |
| | | for (File f : tempDir.listFiles()) |
| | | { |
| | | f.delete(); |
| | | } |
| | | } |
| | | |
| | | try |
| | | { |
| | | // Create a temporary work directory. |
| | | File tempDir = new File(config.getImportTempDirectory()); |
| | | tempDir.mkdir(); |
| | | if (tempDir.listFiles() != null) |
| | | importedCount = 0; |
| | | int passNumber = 1; |
| | | boolean moreData = true; |
| | | while (moreData) |
| | | { |
| | | for (File f : tempDir.listFiles()) |
| | | moreData = processLDIF(); |
| | | if (moreData) |
| | | { |
| | | f.delete(); |
| | | msgID = MSGID_JEB_IMPORT_BEGINNING_INTERMEDIATE_MERGE; |
| | | message = getMessage(msgID, passNumber++); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | try |
| | | { |
| | | importedCount = 0; |
| | | int passNumber = 1; |
| | | boolean moreData = true; |
| | | while (moreData) |
| | | else |
| | | { |
| | | moreData = processLDIF(); |
| | | if (moreData) |
| | | { |
| | | msgID = MSGID_JEB_IMPORT_BEGINNING_INTERMEDIATE_MERGE; |
| | | message = getMessage(msgID, passNumber++); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | msgID = MSGID_JEB_IMPORT_BEGINNING_FINAL_MERGE; |
| | | message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | long mergeStartTime = System.currentTimeMillis(); |
| | | merge(); |
| | | long mergeEndTime = System.currentTimeMillis(); |
| | | |
| | | if (moreData) |
| | | { |
| | | msgID = MSGID_JEB_IMPORT_RESUMING_LDIF_PROCESSING; |
| | | message = getMessage(msgID, ((mergeEndTime-mergeStartTime)/1000)); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | msgID = MSGID_JEB_IMPORT_FINAL_MERGE_COMPLETED; |
| | | message = getMessage(msgID, ((mergeEndTime-mergeStartTime)/1000)); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | msgID = MSGID_JEB_IMPORT_BEGINNING_FINAL_MERGE; |
| | | message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | tempDir.delete(); |
| | | |
| | | |
| | | long mergeStartTime = System.currentTimeMillis(); |
| | | merge(); |
| | | long mergeEndTime = System.currentTimeMillis(); |
| | | |
| | | if (moreData) |
| | | { |
| | | msgID = MSGID_JEB_IMPORT_RESUMING_LDIF_PROCESSING; |
| | | message = getMessage(msgID, ((mergeEndTime-mergeStartTime)/1000)); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | msgID = MSGID_JEB_IMPORT_FINAL_MERGE_COMPLETED; |
| | | message = getMessage(msgID, ((mergeEndTime-mergeStartTime)/1000)); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | rootContainer.close(); |
| | | |
| | | // Sync the environment to disk. |
| | | msgID = MSGID_JEB_IMPORT_CLOSING_DATABASE; |
| | | message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | tempDir.delete(); |
| | | } |
| | | } |
| | | finally |
| | |
| | | */ |
| | | public void merge() |
| | | { |
| | | Map<AttributeType,IndexConfig> |
| | | indexConfigs = config.getIndexConfigMap(); |
| | | ArrayList<IndexMergeThread> mergers = new ArrayList<IndexMergeThread>(); |
| | | |
| | | // Create merge threads for each base DN. |
| | |
| | | EntryContainer entryContainer = importContext.getEntryContainer(); |
| | | |
| | | // For each configured attribute index. |
| | | for (IndexConfig indexConfig : indexConfigs.values()) |
| | | for (AttributeIndex attrIndex : entryContainer.getAttributeIndexes()) |
| | | { |
| | | AttributeIndex attrIndex = |
| | | entryContainer.getAttributeIndex(indexConfig.getAttributeType()); |
| | | if (indexConfig.isEqualityIndex()) |
| | | int indexEntryLimit = config.getBackendIndexEntryLimit(); |
| | | if(attrIndex.getConfiguration().getIndexEntryLimit() != null) |
| | | { |
| | | indexEntryLimit = attrIndex.getConfiguration().getIndexEntryLimit(); |
| | | } |
| | | |
| | | if (attrIndex.equalityIndex != null) |
| | | { |
| | | Index index = attrIndex.equalityIndex; |
| | | String name = containerName + "_" + index.toString(); |
| | | IndexMergeThread indexMergeThread = |
| | | new IndexMergeThread(name, config, ldifImportConfig, index, |
| | | indexConfig.getEqualityEntryLimit()); |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, index, |
| | | indexEntryLimit); |
| | | mergers.add(indexMergeThread); |
| | | } |
| | | if (indexConfig.isPresenceIndex()) |
| | | if (attrIndex.presenceIndex != null) |
| | | { |
| | | Index index = attrIndex.presenceIndex; |
| | | String name = containerName + "_" + index.toString(); |
| | | IndexMergeThread indexMergeThread = |
| | | new IndexMergeThread(name, config, ldifImportConfig, index, |
| | | indexConfig.getPresenceEntryLimit()); |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, index, |
| | | indexEntryLimit); |
| | | mergers.add(indexMergeThread); |
| | | } |
| | | if (indexConfig.isSubstringIndex()) |
| | | if (attrIndex.substringIndex != null) |
| | | { |
| | | Index index = attrIndex.substringIndex; |
| | | String name = containerName + "_" + index.toString(); |
| | | IndexMergeThread indexMergeThread = |
| | | new IndexMergeThread(name, config, ldifImportConfig, index, |
| | | indexConfig.getSubstringEntryLimit()); |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, index, |
| | | indexEntryLimit); |
| | | mergers.add(indexMergeThread); |
| | | } |
| | | if (indexConfig.isOrderingIndex()) |
| | | if (attrIndex.orderingIndex != null) |
| | | { |
| | | Index index = attrIndex.orderingIndex; |
| | | String name = containerName + "_" + index.toString(); |
| | | IndexMergeThread indexMergeThread = |
| | | new IndexMergeThread(name, config, ldifImportConfig, index, |
| | | indexConfig.getEqualityEntryLimit()); |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, index, |
| | | indexEntryLimit); |
| | | mergers.add(indexMergeThread); |
| | | } |
| | | if (indexConfig.isApproximateIndex()) |
| | | if (attrIndex.approximateIndex != null) |
| | | { |
| | | Index index = attrIndex.approximateIndex; |
| | | String name = containerName + "_" + index.toString(); |
| | | IndexMergeThread indexMergeThread = |
| | | new IndexMergeThread(name, config, ldifImportConfig, index, |
| | | indexConfig.getEqualityEntryLimit()); |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, index, |
| | | indexEntryLimit); |
| | | mergers.add(indexMergeThread); |
| | | } |
| | | } |
| | | |
| | | // Id2Children index. |
| | | Index id2Children = entryContainer.getID2Children(); |
| | | String name = containerName + "_" + id2Children.toString(); |
| | | IndexMergeThread indexMergeThread = |
| | | new IndexMergeThread(name, config, ldifImportConfig, |
| | | id2Children, |
| | | config.getBackendIndexEntryLimit()); |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, |
| | | id2Children, |
| | | config.getBackendIndexEntryLimit()); |
| | | mergers.add(indexMergeThread); |
| | | |
| | | // Id2Subtree index. |
| | | Index id2Subtree = entryContainer.getID2Subtree(); |
| | | name = containerName + "_" + id2Subtree.toString(); |
| | | indexMergeThread = |
| | | new IndexMergeThread(name, config, ldifImportConfig, |
| | | id2Subtree, |
| | | config.getBackendIndexEntryLimit()); |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, |
| | | id2Subtree, |
| | | config.getBackendIndexEntryLimit()); |
| | | mergers.add(indexMergeThread); |
| | | } |
| | | |
| | |
| | | * reading, or while reading from the LDIF file. |
| | | */ |
| | | private boolean processLDIF() |
| | | throws JebException, DatabaseException, IOException |
| | | throws JebException, DatabaseException, IOException |
| | | { |
| | | boolean moreData = false; |
| | | |
| | | ArrayList<ImportThread> threads; |
| | | |
| | | // Create one set of worker threads for each base DN. |
| | | int importThreadCount = config.getImportThreadCount(); |
| | | threads = new ArrayList<ImportThread>(importThreadCount*importMap.size()); |
| | | int importThreadCount = config.getBackendImportThreadCount(); |
| | | for (ImportContext ic : importMap.values()) |
| | | { |
| | | for (int i = 0; i < importThreadCount; i++) |
| | |
| | | // Create a counter to use to determine whether we've hit the import |
| | | // pass size. |
| | | int entriesProcessed = 0; |
| | | int importPassSize = config.getImportPassSize(); |
| | | int importPassSize = config.getBackendImportPassSize(); |
| | | if (importPassSize <= 0) |
| | | { |
| | | importPassSize = Integer.MAX_VALUE; |
| | |
| | | { |
| | | do |
| | | { |
| | | if(threads.size() <= 0) |
| | | { |
| | | int msgID = MSGID_JEB_IMPORT_NO_WORKER_THREADS; |
| | | String msg = getMessage(msgID); |
| | | throw new JebException(msgID, msg); |
| | | } |
| | | |
| | | try |
| | | { |
| | | // Read the next entry. |
| | |
| | | } |
| | | } while (true); |
| | | |
| | | // Wait for the queues to be drained. |
| | | for (ImportContext ic : importMap.values()) |
| | | if(threads.size() > 0) |
| | | { |
| | | while (ic.getQueue().size() > 0) |
| | | // Wait for the queues to be drained. |
| | | for (ImportContext ic : importMap.values()) |
| | | { |
| | | try |
| | | while (ic.getQueue().size() > 0) |
| | | { |
| | | Thread.sleep(100); |
| | | } catch (Exception e) |
| | | { |
| | | // No action needed. |
| | | try |
| | | { |
| | | Thread.sleep(100); |
| | | } catch (Exception e) |
| | | { |
| | | // No action needed. |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void processEntry(ImportContext importContext, Entry entry) |
| | | throws JebException, DatabaseException |
| | | throws JebException, DatabaseException |
| | | { |
| | | DN entryDN = entry.getDN(); |
| | | LDIFImportConfig ldifImportConfig = importContext.getLDIFImportConfig(); |
| | |
| | | { |
| | | // See if we are allowed to replace the entry that exists. |
| | | if (ldifImportConfig.appendToExistingData() && |
| | | ldifImportConfig.replaceExistingEntries()) |
| | | ldifImportConfig.replaceExistingEntries()) |
| | | { |
| | | // Read the existing entry contents. |
| | | Entry oldEntry = id2entry.get(txn, entryID); |
| | |
| | | // Make sure the parent entry exists, unless this entry is a base DN. |
| | | EntryID parentID = null; |
| | | DN parentDN = importContext.getEntryContainer(). |
| | | getParentWithinBase(entryDN); |
| | | getParentWithinBase(entryDN); |
| | | if (parentDN != null) |
| | | { |
| | | parentID = dn2id.get(txn, parentDN); |
| | |
| | | // Put the entry on the queue. |
| | | try |
| | | { |
| | | importContext.getQueue().put(entry); |
| | | while(!importContext.getQueue().offer(entry, 1000, |
| | | TimeUnit.MILLISECONDS)) |
| | | { |
| | | if(threads.size() <= 0) |
| | | { |
| | | // All worker threads died. We must stop now. |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | catch (InterruptedException e) |
| | | { |
| | |
| | | */ |
| | | public void uncaughtException(Thread t, Throwable e) |
| | | { |
| | | e.printStackTrace(); |
| | | threads.remove(t); |
| | | int msgID = MSGID_JEB_IMPORT_THREAD_EXCEPTION; |
| | | String msg = getMessage(msgID, t.getName(), |
| | | StaticUtils.stackTraceToSingleLineString(e)); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.SEVERE_ERROR, msg, |
| | | msgID); |
| | | } |
| | | |
| | | /** |
| | |
| | | EnvironmentStats envStats = |
| | | rootContainer.getEnvironmentStats(new StatsConfig()); |
| | | long nCacheMiss = |
| | | envStats.getNCacheMiss() - prevEnvStats.getNCacheMiss(); |
| | | envStats.getNCacheMiss() - prevEnvStats.getNCacheMiss(); |
| | | |
| | | float cacheMissRate = 0; |
| | | if (deltaCount > 0) |
| | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import org.opends.server.api.DirectoryThread; |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | | |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.DatabaseException; |
| | | |
| | | import java.util.concurrent.BlockingQueue; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.Map; |
| | | import java.util.ArrayList; |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * A thread to process import entries from a queue. Multiple instances of |
| | |
| | | { |
| | | Entry entry; |
| | | |
| | | Map<AttributeType,IndexConfig> |
| | | indexConfigs = importContext.getConfig().getIndexConfigMap(); |
| | | |
| | | // Figure out how many indexes there will be. |
| | | int nIndexes = 0; |
| | | |
| | | nIndexes += 2; // For id2children and id2subtree. |
| | | |
| | | for (IndexConfig indexConfig : indexConfigs.values()) |
| | | for (AttributeIndex attrIndex : entryContainer.getAttributeIndexes()) |
| | | { |
| | | if (indexConfig.isEqualityIndex()) |
| | | if (attrIndex.equalityIndex != null) |
| | | { |
| | | nIndexes++; |
| | | } |
| | | if (indexConfig.isPresenceIndex()) |
| | | if (attrIndex.presenceIndex != null) |
| | | { |
| | | nIndexes++; |
| | | } |
| | | if (indexConfig.isSubstringIndex()) |
| | | if (attrIndex.substringIndex != null) |
| | | { |
| | | nIndexes++; |
| | | } |
| | | if (indexConfig.isOrderingIndex()) |
| | | if (attrIndex.orderingIndex != null) |
| | | { |
| | | nIndexes++; |
| | | } |
| | | if (indexConfig.isApproximateIndex()) |
| | | if (attrIndex.approximateIndex != null) |
| | | { |
| | | nIndexes++; |
| | | } |
| | |
| | | long indexBufferSize = importContext.getBufferSize() / nIndexes; |
| | | |
| | | // Create an index builder for each attribute index database. |
| | | for (IndexConfig indexConfig : indexConfigs.values()) |
| | | for (AttributeIndex attrIndex : entryContainer.getAttributeIndexes()) |
| | | { |
| | | AttributeIndex attrIndex = |
| | | entryContainer.getAttributeIndex(indexConfig.getAttributeType()); |
| | | if (indexConfig.isEqualityIndex()) |
| | | int indexEntryLimit = |
| | | importContext.getConfig().getBackendIndexEntryLimit(); |
| | | if(attrIndex.getConfiguration().getIndexEntryLimit() != null) |
| | | { |
| | | indexEntryLimit = attrIndex.getConfiguration().getIndexEntryLimit(); |
| | | } |
| | | |
| | | if (attrIndex.equalityIndex != null) |
| | | { |
| | | IndexBuilder indexBuilder = |
| | | new IndexBuilder(importContext, |
| | | attrIndex.equalityIndex, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexEntryLimit, |
| | | indexBufferSize); |
| | | builders.add(indexBuilder); |
| | | } |
| | | if (indexConfig.isPresenceIndex()) |
| | | if (attrIndex.presenceIndex != null) |
| | | { |
| | | IndexBuilder indexBuilder = |
| | | new IndexBuilder(importContext, |
| | | attrIndex.presenceIndex, |
| | | indexConfig.getPresenceEntryLimit(), |
| | | indexEntryLimit, |
| | | indexBufferSize); |
| | | builders.add(indexBuilder); |
| | | } |
| | | if (indexConfig.isSubstringIndex()) |
| | | if (attrIndex.substringIndex != null) |
| | | { |
| | | IndexBuilder indexBuilder = |
| | | new IndexBuilder(importContext, |
| | | attrIndex.substringIndex, |
| | | indexConfig.getSubstringEntryLimit(), |
| | | indexEntryLimit, |
| | | indexBufferSize); |
| | | builders.add(indexBuilder); |
| | | } |
| | | if (indexConfig.isOrderingIndex()) |
| | | if (attrIndex.orderingIndex != null) |
| | | { |
| | | IndexBuilder indexBuilder = |
| | | new IndexBuilder(importContext, attrIndex.orderingIndex, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexEntryLimit, |
| | | indexBufferSize); |
| | | builders.add(indexBuilder); |
| | | } |
| | | if (indexConfig.isApproximateIndex()) |
| | | if (attrIndex.approximateIndex != null) |
| | | { |
| | | IndexBuilder indexBuilder = |
| | | new IndexBuilder(importContext, attrIndex.approximateIndex, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexEntryLimit, |
| | | indexBufferSize); |
| | | builders.add(indexBuilder); |
| | | } |
| | |
| | | // Increment the entry count. |
| | | importContext.incrEntryInsertCount(entryInsertCount); |
| | | } |
| | | catch (DatabaseException e) |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | catch (DirectoryException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | catch (IOException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | 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.loggers.ErrorLogger; |
| | | |
| | | import com.sleepycat.je.Cursor; |
| | | import com.sleepycat.je.CursorConfig; |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.DatabaseConfig; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.LockMode; |
| | | import com.sleepycat.je.OperationStatus; |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.*; |
| | | |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.ConditionResult; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.StaticUtils; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | |
| | | import java.util.*; |
| | | |
| | |
| | | * normalized form of an attribute value (or fragment of a value) appearing |
| | | * in the entry. |
| | | */ |
| | | public class Index |
| | | public class Index extends DatabaseContainer |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | |
| | | |
| | | /** |
| | | * The database entryContainer holding this index database. |
| | | */ |
| | | private EntryContainer entryContainer; |
| | | |
| | | /** |
| | | * The JE database configuration. |
| | | */ |
| | | private DatabaseConfig dbConfig; |
| | | |
| | | /** |
| | | * The name of the database within the entryContainer. |
| | | */ |
| | | private String name; |
| | | |
| | | /** |
| | | * A cached per-thread JE database handle. |
| | | */ |
| | | private ThreadLocal<Database> threadLocalDatabase = |
| | | new ThreadLocal<Database>(); |
| | | |
| | | /** |
| | | * The indexer object to construct index keys from LDAP attribute values. |
| | | */ |
| | |
| | | */ |
| | | private int entryLimitExceededCount; |
| | | |
| | | private State state; |
| | | |
| | | /** |
| | | * A flag to indicate if this index should be trusted to be consistent |
| | | * with the entries database. If not trusted, we assume that existing |
| | | * entryIDSets for a key is still accurate. However, keys that do not |
| | | * exist are undefined instead of an empty entryIDSet. The following |
| | | * rules will be observed when the index is not trusted: |
| | | * |
| | | * - no entryIDs will be added to a non-existing key. |
| | | * - undefined entryIdSet will be returned whenever a key is not found. |
| | | */ |
| | | private boolean trusted = false; |
| | | |
| | | /** |
| | | * A flag to indicate if a rebuild process is running on this index. |
| | | * During the rebuild process, we assume that no entryIDSets are |
| | | * accurate and return an undefined set on all read operations. |
| | | * However all write opeations will succeed. The rebuildRunning |
| | | * flag overrides all behaviours of the trusted flag. |
| | | */ |
| | | private boolean rebuildRunning = false; |
| | | |
| | | /** |
| | | * Create a new index object. |
| | | * @param entryContainer The database entryContainer holding this index. |
| | | * @param name The name of the index database within the entryContainer. |
| | | * @param indexer The indexer object to construct index keys from LDAP |
| | | * attribute values. |
| | | * @param state The state database to persist index state info. |
| | | * @param indexEntryLimit The configured limit on the number of entry IDs |
| | | * that may be indexed by one key. |
| | | * @param cursorEntryLimit The configured limit on the number of entry IDs |
| | | * that may be retrieved by cursoring through an index. |
| | | * @param env The JE Environemnt |
| | | * @param entryContainer The database entryContainer holding this index. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Index(EntryContainer entryContainer, String name, Indexer indexer, |
| | | int indexEntryLimit, int cursorEntryLimit) |
| | | public Index(String name, Indexer indexer, State state, |
| | | int indexEntryLimit, int cursorEntryLimit, Environment env, |
| | | EntryContainer entryContainer) |
| | | throws DatabaseException |
| | | { |
| | | this.entryContainer = entryContainer; |
| | | this.name = name; |
| | | super(name, env, entryContainer); |
| | | this.indexer = indexer; |
| | | this.comparator = indexer.getComparator(); |
| | | this.indexEntryLimit = indexEntryLimit; |
| | | this.cursorEntryLimit = cursorEntryLimit; |
| | | } |
| | | |
| | | /** |
| | | * Open this index database. |
| | | * @param dbConfig The requested JE database configuration. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void open(DatabaseConfig dbConfig) throws DatabaseException |
| | | { |
| | | this.dbConfig = dbConfig.cloneConfig(); |
| | | DatabaseConfig dbNodupsConfig = new DatabaseConfig(); |
| | | |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | dbNodupsConfig.setReadOnly(true); |
| | | dbNodupsConfig.setAllowCreate(false); |
| | | dbNodupsConfig.setTransactional(false); |
| | | } |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(false); |
| | | dbNodupsConfig.setDeferredWrite(true); |
| | | } |
| | | else |
| | | { |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(true); |
| | | } |
| | | |
| | | this.dbConfig = dbNodupsConfig; |
| | | this.dbConfig.setOverrideBtreeComparator(true); |
| | | this.dbConfig.setBtreeComparator(comparator.getClass()); |
| | | getDatabase(); |
| | | } |
| | | |
| | | /** |
| | | * Get a handle to the database. It returns a per-thread handle to avoid |
| | | * any thread contention on the database handle. The entryContainer is |
| | | * responsible for closing all handles. |
| | | * |
| | | * @return A database handle. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Database getDatabase() throws DatabaseException |
| | | { |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | this.state = state; |
| | | |
| | | this.trusted = state.getIndexTrustState(null, this); |
| | | if(!trusted && entryContainer.getEntryCount() <= 0) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("JE Index database %s opened with %d records.", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | // If there are no entries in the entry container then there |
| | | // is no reason why this index can't be upgraded to trusted. |
| | | setTrusted(null, true); |
| | | } |
| | | return database; |
| | | |
| | | // Issue warning if this index is not trusted |
| | | if(!trusted) |
| | | { |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | ErrorLogger.logError(ErrorLogCategory.BACKEND, |
| | | ErrorLogSeverity.NOTICE, msgID, name); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param key The index key. |
| | | * @param entryID The entry ID. |
| | | * @return True if the entry ID is inserted or ignored because the entry limit |
| | | * count is exceeded. False if it alreadly exists in the entry ID set |
| | | * count is exceeded. False if it already exists in the entry ID set |
| | | * for the given key. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | boolean success = true; |
| | | |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | status = read(txn, key, data, lockMode); |
| | | |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | |
| | | { |
| | | entryIDList = new EntryIDSet(); |
| | | entryLimitExceededCount++; |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); |
| | | TRACER.debugInfo("Index entry exceeded in index %s. " + |
| | | "Limit: %d. ID list size: %d.\nKey:", |
| | | name, indexEntryLimit, entryIDList.size(), |
| | | builder); |
| | | |
| | | } |
| | | } |
| | | else |
| | | { |
| | |
| | | |
| | | byte[] after = entryIDList.toDatabase(); |
| | | data.setData(after); |
| | | EntryContainer.put(getDatabase(), txn, key, data); |
| | | put(txn, key, data); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | EntryContainer.put(getDatabase(), txn, key, entryIDData); |
| | | if(rebuildRunning || trusted) |
| | | { |
| | | put(txn, key, entryIDData); |
| | | } |
| | | } |
| | | |
| | | return success; |
| | |
| | | LockMode lockMode = LockMode.RMW; |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | status = read(txn, key, data, lockMode); |
| | | |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | | EntryIDSet entryIDList = new EntryIDSet(key.getData(), data.getData()); |
| | | if (entryIDList.isDefined()) |
| | | { |
| | | if (!entryIDList.remove(entryID)) |
| | | // Ignore failures if rebuild is running since the entry ID is |
| | | // probably already removed. |
| | | if (!entryIDList.remove(entryID) && !rebuildRunning) |
| | | { |
| | | // This shouldn't happen! |
| | | // TODO notify the database needs to be checked |
| | | // Invalidate the key by setting it undefined |
| | | byte[] after = new EntryIDSet().toDatabase(); |
| | | data.setData(after); |
| | | put(txn, key, data); |
| | | |
| | | if(trusted) |
| | | { |
| | | 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()); |
| | | } |
| | | |
| | | int msgID = MSGID_JEB_INDEX_CORRUPT_REQUIRES_REBUILD; |
| | | ErrorLogger.logError(ErrorLogCategory.BACKEND, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | msgID, name); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | byte[] after = entryIDList.toDatabase(); |
| | | if (after == null) |
| | | { |
| | | // No more IDs, so remove the key |
| | | EntryContainer.delete(getDatabase(), txn, key); |
| | | // 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); |
| | | EntryContainer.put(getDatabase(), txn, key, data); |
| | | put(txn, key, data); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // This shouldn't happen! |
| | | // TODO notify the database needs to be checked |
| | | // Ignore failures if rebuild is running since a empty entryIDset |
| | | // will probably not be rebuilt. |
| | | if(trusted && !rebuildRunning) |
| | | { |
| | | setTrusted(txn, false); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.getData(), 4); |
| | | TRACER.debugError("The expected key does not exist in the " + |
| | | "index %s.\nKey:%s", name, builder.toString()); |
| | | } |
| | | |
| | | int msgID = MSGID_JEB_INDEX_CORRUPT_REQUIRES_REBUILD; |
| | | ErrorLogger.logError(ErrorLogCategory.BACKEND, |
| | | ErrorLogSeverity.SEVERE_ERROR, |
| | | msgID, name); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | EntryID entryID) |
| | | throws DatabaseException |
| | | { |
| | | if(rebuildRunning) |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | |
| | | OperationStatus status; |
| | | LockMode lockMode = LockMode.DEFAULT; |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | status = read(txn, key, data, lockMode); |
| | | if (status == OperationStatus.SUCCESS) |
| | | { |
| | | EntryIDSet entryIDList = |
| | |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.FALSE; |
| | | if(trusted) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | public EntryIDSet readKey(DatabaseEntry key, Transaction txn, |
| | | LockMode lockMode) |
| | | { |
| | | if(rebuildRunning) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | | |
| | | try |
| | | { |
| | | OperationStatus status; |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | status = EntryContainer.read(getDatabase(), txn, key, data, lockMode); |
| | | status = read( txn, key, data, lockMode); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return new EntryIDSet(key.getData(), null); |
| | | if(trusted) |
| | | { |
| | | return new EntryIDSet(key.getData(), null); |
| | | } |
| | | else |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | | } |
| | | return new EntryIDSet(key.getData(), data.getData()); |
| | | } |
| | |
| | | if (after == null) |
| | | { |
| | | // No more IDs, so remove the key. |
| | | EntryContainer.delete(getDatabase(), txn, key); |
| | | delete(txn, key); |
| | | } |
| | | else |
| | | { |
| | |
| | | entryLimitExceededCount++; |
| | | } |
| | | data.setData(after); |
| | | EntryContainer.put(getDatabase(), txn, key, data); |
| | | put(txn, key, data); |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | LockMode lockMode = LockMode.DEFAULT; |
| | | |
| | | // If this index is not trusted, then just return an undefined |
| | | // id set. |
| | | if(rebuildRunning || !trusted) |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | | |
| | | try |
| | | { |
| | | // Total number of IDs found so far. |
| | |
| | | OperationStatus status; |
| | | Cursor cursor; |
| | | |
| | | cursor = getDatabase().openCursor(null, CursorConfig.READ_COMMITTED); |
| | | cursor = openCursor(null, CursorConfig.READ_COMMITTED); |
| | | |
| | | try |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get a string representation of this object. |
| | | * @return return A string representation of this object. |
| | | */ |
| | | public String toString() |
| | | { |
| | | return name; |
| | | } |
| | | |
| | | /** |
| | | * Open a cursor to the JE database holding this index. |
| | | * @param txn A JE database transaction to be associated with the cursor, |
| | | * or null if none is required. |
| | | * @param cursorConfig The requested JE cursor configuration. |
| | | * @return A new JE cursor. |
| | | * @throws DatabaseException If an error occurs while attempting to open |
| | | * the cursor. |
| | | */ |
| | | public Cursor openCursor(Transaction txn, CursorConfig cursorConfig) |
| | | throws DatabaseException |
| | | { |
| | | return getDatabase().openCursor(txn, cursorConfig); |
| | | } |
| | | |
| | | /** |
| | | * Removes all records from the database. |
| | | * @param txn A JE database transaction to be used during the clear operation |
| | | * or null if not required. Using transactions increases the chance |
| | | * of lock contention. |
| | | * @return The number of records removed. |
| | | * @throws DatabaseException If an error occurs while cleaning the database. |
| | | */ |
| | | public long clear(Transaction txn) throws DatabaseException |
| | | { |
| | | long deletedCount = 0; |
| | | Cursor cursor = openCursor(txn, null); |
| | | try |
| | | { |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("%d existing records will be deleted from the " + |
| | | "database", getRecordCount()); |
| | | } |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | DatabaseEntry key = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | |
| | | // Step forward until we deleted all records. |
| | | for (status = cursor.getFirst(key, data, LockMode.DEFAULT); |
| | | status == OperationStatus.SUCCESS; |
| | | status = cursor.getNext(key, data, LockMode.DEFAULT)) |
| | | { |
| | | cursor.delete(); |
| | | deletedCount++; |
| | | } |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugVerbose("%d records deleted", deletedCount); |
| | | } |
| | | } |
| | | catch(DatabaseException de) |
| | | { |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | throw de; |
| | | } |
| | | finally |
| | | { |
| | | cursor.close(); |
| | | } |
| | | |
| | | return deletedCount; |
| | | } |
| | | |
| | | /** |
| | | * Update the index for a new entry. |
| | | * |
| | | * @param txn A database transaction, or null if none is required. |
| | | * @param entryID The entry ID. |
| | | * @param entry The entry to be indexed. |
| | | * @return True if all the indexType keys for the entry are added. False if |
| | | * the entry ID alreadly exists for some keys. |
| | | * the entry ID already exists for some keys. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the count of the number of entries stored. |
| | | * Set the index entry limit. |
| | | * |
| | | * @return The number of entries stored. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @param indexEntryLimit The index entry limit to set. |
| | | * @return True if a rebuild is required or false otherwise. |
| | | */ |
| | | public long getRecordCount() throws DatabaseException |
| | | public boolean setIndexEntryLimit(int indexEntryLimit) |
| | | { |
| | | return EntryContainer.count(getDatabase()); |
| | | boolean rebuildRequired = false; |
| | | if(this.indexEntryLimit < indexEntryLimit && |
| | | entryLimitExceededCount > 0 ) |
| | | { |
| | | rebuildRequired = true; |
| | | } |
| | | this.indexEntryLimit = indexEntryLimit; |
| | | |
| | | return rebuildRequired; |
| | | } |
| | | |
| | | /** |
| | | * Set the indexer. |
| | | * |
| | | * @param indexer The indexer to set |
| | | */ |
| | | public void setIndexer(Indexer indexer) |
| | | { |
| | | this.indexer = indexer; |
| | | } |
| | | |
| | | /** |
| | | * Set the index trust state. |
| | | * @param txn A database transaction, or null if none is required. |
| | | * @param trusted True if this index should be trusted or false |
| | | * otherwise. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public synchronized void setTrusted(Transaction txn, boolean trusted) |
| | | throws DatabaseException |
| | | { |
| | | this.trusted = trusted; |
| | | state.putIndexTrustState(txn, this, trusted); |
| | | } |
| | | |
| | | /** |
| | | * Set the rebuild status of this index. |
| | | * @param rebuildRunning True if a rebuild process on this index |
| | | * is running or False otherwise. |
| | | */ |
| | | public synchronized void setRebuildStatus(boolean rebuildRunning) |
| | | { |
| | | this.rebuildRunning = rebuildRunning; |
| | | } |
| | | } |
| | |
| | | |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.Entry; |
| | | import static org.opends.server.util.StaticUtils.getFileForPath; |
| | | |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.Transaction; |
| | |
| | | this.entryLimit = entryLimit; |
| | | this.bufferSize = (int)bufferSize/100; |
| | | long tid = Thread.currentThread().getId(); |
| | | fileNamePrefix = importContext.getContainerName() + "_" + |
| | | indexer.toString() + "_" + tid + "_"; |
| | | fileNamePrefix = index.getName() + "_" + tid + "_"; |
| | | replaceExisting = |
| | | importContext.getLDIFImportConfig().appendToExistingData() && |
| | | importContext.getLDIFImportConfig().replaceExistingEntries(); |
| | |
| | | public void startProcessing() |
| | | { |
| | | // Clean up any work files left over from a previous run. |
| | | File tempDir = new File(importContext.getConfig().getImportTempDirectory()); |
| | | File tempDir = getFileForPath( |
| | | importContext.getConfig().getBackendImportTempDirectory()); |
| | | File[] files = tempDir.listFiles(filter); |
| | | if (files != null) |
| | | { |
| | |
| | | // Start a new file. |
| | | fileNumber++; |
| | | String fileName = fileNamePrefix + String.valueOf(fileNumber); |
| | | File file = new File(importContext.getConfig().getImportTempDirectory(), |
| | | File file = new File(getFileForPath( |
| | | importContext.getConfig().getBackendImportTempDirectory()), |
| | | fileName); |
| | | BufferedOutputStream bufferedStream = |
| | | new BufferedOutputStream(new FileOutputStream(file)); |
| | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | import static org.opends.server.util.StaticUtils.getFileForPath; |
| | | |
| | | /** |
| | | * A thread to merge a set of intermediate files from an index builder |
| | |
| | | /** |
| | | * The configuration of the JE backend containing the index. |
| | | */ |
| | | Config config; |
| | | JEBackendCfg config; |
| | | |
| | | /** |
| | | * The LDIF import configuration, which indicates whether we are |
| | |
| | | { |
| | | public boolean accept(File d, String name) |
| | | { |
| | | return name.startsWith(indexName); |
| | | return name.startsWith(index.getName()); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * Create a new index merge thread. |
| | | * @param name The name of the index for use in file names and log messages. |
| | | * @param config The configuration of the JE backend containing the index. |
| | | * @param ldifImportConfig The LDIF import configuration, which indicates |
| | | * whether we are appending to existing data. |
| | | * @param index The index database to be written. |
| | | * @param entryLimit The configured index entry limit. |
| | | */ |
| | | IndexMergeThread(String name, Config config, |
| | | IndexMergeThread(JEBackendCfg config, |
| | | LDIFImportConfig ldifImportConfig, |
| | | Index index, int entryLimit) |
| | | { |
| | | super("Index Merge Thread " + name); |
| | | super("Index Merge Thread " + index.getName()); |
| | | |
| | | this.indexName = name; |
| | | this.config = config; |
| | | this.ldifImportConfig = ldifImportConfig; |
| | | this.indexer = index.indexer; |
| | |
| | | new TreeMap<ASN1OctetString, MergeValue>(comparator); |
| | | |
| | | // Open all the files. |
| | | File tempDir = new File(config.getImportTempDirectory()); |
| | | File tempDir = getFileForPath(config.getBackendImportTempDirectory()); |
| | | File[] files = tempDir.listFiles(filter); |
| | | |
| | | if (files == null || files.length == 0) |
| | | { |
| | | int msgID = MSGID_JEB_INDEX_MERGE_NO_DATA; |
| | | String message = getMessage(msgID, indexName); |
| | | String message = getMessage(msgID, index.getName()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | return; |
| | | } |
| | | |
| | | int msgID = MSGID_JEB_INDEX_MERGE_START; |
| | | String message = getMessage(msgID, files.length, indexName); |
| | | String message = getMessage(msgID, files.length, index.getName()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | |
| | |
| | | |
| | | try |
| | | { |
| | | Database db = index.getDatabase(); |
| | | |
| | | for (int i = 0; i < files.length; i++) |
| | | { |
| | |
| | | merged.clear(); |
| | | if (ldifImportConfig.appendToExistingData()) |
| | | { |
| | | if (db.get(txn, dbKey, dbData, LockMode.RMW) == |
| | | if (index.read(txn, dbKey, dbData, LockMode.RMW) == |
| | | OperationStatus.SUCCESS) |
| | | { |
| | | if (dbData.getSize() == 0) |
| | |
| | | |
| | | dbData.setData(mergedBytes); |
| | | dbData.setSize(merged.encodedSize()); |
| | | db.put(txn, dbKey, dbData); |
| | | index.put(txn, dbKey, dbData); |
| | | } |
| | | |
| | | LinkedList<byte[]> arrayList = arrayMap.get(keyBytes.length); |
| | |
| | | catch (NoSuchElementException e) |
| | | { |
| | | } |
| | | |
| | | if(replaceExisting) |
| | | { |
| | | index.setTrusted(txn, true); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | } |
| | | |
| | | msgID = MSGID_JEB_INDEX_MERGE_COMPLETE; |
| | | message = getMessage(msgID, indexName); |
| | | message = getMessage(msgID, index.getName()); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | |
| | | |
| | | /** |
| | | * The number of entries that were skipped because they were not applicable |
| | | * for the indexType or because an error occured. |
| | | * for the indexType or because an error occurred. |
| | | */ |
| | | long skippedEntries = 0; |
| | | |
| | |
| | | */ |
| | | IndexRebuildThread(EntryContainer ec, Index index) |
| | | { |
| | | super("Index Rebuild Thread " + ec.getContainerName() + "_" + |
| | | index.toString()); |
| | | super("Index Rebuild Thread " + index.getName()); |
| | | this.ec = ec; |
| | | this.indexType = IndexType.INDEX; |
| | | this.index = index; |
| | |
| | | */ |
| | | IndexRebuildThread(EntryContainer ec, AttributeIndex index) |
| | | { |
| | | super("Index Rebuild Thread " + ec.getContainerName() + "_" + |
| | | index.toString()); |
| | | super("Index Rebuild Thread " + index.getName()); |
| | | this.ec = ec; |
| | | this.indexType = IndexType.ATTRIBUTEINDEX; |
| | | this.attrIndex = index; |
| | |
| | | } |
| | | |
| | | /** |
| | | * Clear the database and prep it for the rebuild. |
| | | * |
| | | * @throws DatabaseException if a JE databse error occurs while clearing |
| | | * the database being rebuilt. |
| | | */ |
| | | public void clearDatabase() throws DatabaseException |
| | | { |
| | | if(indexType == null) |
| | | { |
| | | //TODO: throw error |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugError("No index type specified. Rebuild process " + |
| | | "terminated."); |
| | | } |
| | | |
| | | return; |
| | | } |
| | | if(indexType == IndexType.ATTRIBUTEINDEX && attrIndex == null) |
| | | { |
| | | //TODO: throw error |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugError("No attribute index specified. Rebuild process " + |
| | | "terminated."); |
| | | } |
| | | |
| | | return; |
| | | } |
| | | |
| | | if(indexType == IndexType.INDEX && index == null) |
| | | { |
| | | //TODO: throw error |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugError("No index specified. Rebuild process terminated."); |
| | | } |
| | | |
| | | return; |
| | | } |
| | | |
| | | switch(indexType) |
| | | { |
| | | case DN2ID : |
| | | ec.clearDatabase(null, ec.getDN2ID()); |
| | | break; |
| | | case DN2URI : |
| | | ec.clearDatabase(null, ec.getDN2URI()); |
| | | break; |
| | | case ID2CHILDREN : |
| | | ec.clearDatabase(null, ec.getID2Children()); |
| | | ec.getID2Children().setRebuildStatus(true); |
| | | break; |
| | | case ID2SUBTREE : |
| | | ec.clearDatabase(null, ec.getID2Subtree()); |
| | | ec.getID2Subtree().setRebuildStatus(true); |
| | | break; |
| | | case ATTRIBUTEINDEX : |
| | | ec.clearAttributeIndex(null, attrIndex); |
| | | attrIndex.setRebuildStatus(true); |
| | | break; |
| | | case INDEX : |
| | | ec.clearDatabase(null, index); |
| | | index.setRebuildStatus(true); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Start the rebuild process. |
| | | */ |
| | | public void run() |
| | |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | private void rebuildDN2ID() throws DatabaseException |
| | | { |
| | | DN2ID dn2id = ec.getDN2ID(); |
| | | Transaction txn = ec.beginTransaction(); |
| | | try |
| | | { |
| | | //Delete all records in the indexType databases. |
| | | //TODO: Should we do a transactional delete? |
| | | dn2id.clear(txn); |
| | | } |
| | | finally |
| | | { |
| | | EntryContainer.transactionCommit(txn); |
| | | } |
| | | |
| | | //Iterate through the id2entry database and insert associated dn2id |
| | | //records. |
| | |
| | | } |
| | | else |
| | | { |
| | | // The entry ID alreadly exists in the database. |
| | | // The entry ID already exists in the database. |
| | | // This could happen if some other process got to this entry |
| | | // before we did. Since the backend should be offline, this |
| | | // might be a problem. |
| | |
| | | private void rebuildDN2URI() throws DatabaseException |
| | | { |
| | | DN2URI dn2uri = ec.getDN2URI(); |
| | | Transaction txn = ec.beginTransaction(); |
| | | try |
| | | { |
| | | //Delete all records in the indexType databases. |
| | | //TODO: Should we do a transactional delete? |
| | | dn2uri.clear(txn); |
| | | } |
| | | finally |
| | | { |
| | | EntryContainer.transactionCommit(txn); |
| | | } |
| | | |
| | | //Iterate through the id2entry database and insert associated dn2uri |
| | | //records. |
| | |
| | | } |
| | | else |
| | | { |
| | | // The entry DN and URIs alreadly exists in the database. |
| | | // The entry DN and URIs already exists in the database. |
| | | // This could happen if some other process got to this entry |
| | | // before we did. Since the backend should be offline, this |
| | | // might be a problem. |
| | |
| | | private void rebuildID2Children() throws DatabaseException |
| | | { |
| | | Index id2children = ec.getID2Children(); |
| | | Transaction txn = ec.beginTransaction(); |
| | | try |
| | | { |
| | | //Delete all records in the indexType databases. |
| | | //TODO: Should we do a transactional delete? |
| | | id2children.clear(txn); |
| | | } |
| | | finally |
| | | { |
| | | EntryContainer.transactionCommit(txn); |
| | | } |
| | | |
| | | DN2ID dn2id = ec.getDN2ID(); |
| | | DN2URI dn2uri = ec.getDN2URI(); |
| | |
| | | } |
| | | else |
| | | { |
| | | // The entry alreadly exists in the database. |
| | | // The entry already exists in the database. |
| | | // This could happen if some other process got to this entry |
| | | // before we did. Since the backend should be offline, this |
| | | // might be a problem. |
| | |
| | | } |
| | | } |
| | | } |
| | | id2children.setRebuildStatus(false); |
| | | id2children.setTrusted(null, true); |
| | | } |
| | | finally |
| | | { |
| | |
| | | private void rebuildID2Subtree() throws DatabaseException |
| | | { |
| | | Index id2subtree = ec.getID2Subtree(); |
| | | Transaction txn = ec.beginTransaction(); |
| | | try |
| | | { |
| | | //Delete all records in the indexType databases. |
| | | //TODO: Should we do a transactional delete? |
| | | id2subtree.clear(txn); |
| | | } |
| | | finally |
| | | { |
| | | EntryContainer.transactionCommit(txn); |
| | | } |
| | | |
| | | DN2ID dn2id = ec.getDN2ID(); |
| | | DN2URI dn2uri = ec.getDN2URI(); |
| | |
| | | } |
| | | else |
| | | { |
| | | // The entry alreadly exists in the database. |
| | | // The entry already exists in the database. |
| | | // This could happen if some other process got to this entry |
| | | // before we did. Since the backend should be offline, this |
| | | // might be a problem. |
| | |
| | | } |
| | | } |
| | | } |
| | | id2subtree.setRebuildStatus(false); |
| | | id2subtree.setTrusted(null, true); |
| | | } |
| | | finally |
| | | { |
| | |
| | | private void rebuildAttributeIndex(AttributeIndex index) |
| | | throws DatabaseException |
| | | { |
| | | Transaction txn = ec.beginTransaction(); |
| | | try |
| | | { |
| | | //Delete all records in the indexType databases. |
| | | //TODO: Should we do a transactional delete? |
| | | index.clear(txn); |
| | | } |
| | | finally |
| | | { |
| | | EntryContainer.transactionCommit(txn); |
| | | } |
| | | |
| | | //Iterate through the id2entry database and insert associated indexType |
| | | //records. |
| | |
| | | } |
| | | else |
| | | { |
| | | // The entry alreadly exists in one or more entry sets. |
| | | // The entry already exists in one or more entry sets. |
| | | // This could happen if some other process got to this entry |
| | | // before we did. Since the backend should be offline, this |
| | | // might be a problem. |
| | |
| | | } |
| | | } |
| | | } |
| | | index.setRebuildStatus(false); |
| | | index.setTrusted(null, true); |
| | | } |
| | | finally |
| | | { |
| | |
| | | private void rebuildAttributeIndex(Index index) |
| | | throws DatabaseException |
| | | { |
| | | Transaction txn = ec.beginTransaction(); |
| | | try |
| | | { |
| | | //Delete all records in the indexType databases. |
| | | //TODO: Should we do a transactional delete? |
| | | index.clear(txn); |
| | | } |
| | | finally |
| | | { |
| | | EntryContainer.transactionCommit(txn); |
| | | } |
| | | |
| | | //Iterate through the id2entry database and insert associated indexType |
| | | //records. |
| | |
| | | } |
| | | else |
| | | { |
| | | // The entry alreadly exists in one or more entry sets. |
| | | // The entry already exists in one or more entry sets. |
| | | // This could happen if some other process got to this entry |
| | | // before we did. Since the backend should be offline, this |
| | | // might be a problem. |
| | |
| | | } |
| | | } |
| | | } |
| | | index.setRebuildStatus(false); |
| | | index.setTrusted(null, true); |
| | | } |
| | | finally |
| | | { |
| | |
| | | |
| | | /** |
| | | * Get the number of entries skipped because they were either not applicable |
| | | * or an error occured during the process. |
| | | * or an error occurred during the process. |
| | | * |
| | | * @return The number of entries skipped. |
| | | */ |
| | |
| | | |
| | | |
| | | /** |
| | | * The attribute index configuration for which this instance will |
| | | * The attribute type for which this instance will |
| | | * generate index keys. |
| | | */ |
| | | private IndexConfig indexConfig; |
| | | private AttributeType attributeType; |
| | | |
| | | /** |
| | | * The attribute type ordering matching rule which is also the |
| | |
| | | |
| | | /** |
| | | * Create a new attribute ordering indexer for the given index configuration. |
| | | * @param indexConfig The index configuration for which an indexer is |
| | | * @param attributeType The attribute type for which an indexer is |
| | | * required. |
| | | */ |
| | | public OrderingIndexer(IndexConfig indexConfig) |
| | | public OrderingIndexer(AttributeType attributeType) |
| | | { |
| | | this.indexConfig = indexConfig; |
| | | orderingRule = indexConfig.getAttributeType().getOrderingMatchingRule(); |
| | | this.attributeType = attributeType; |
| | | this.orderingRule = attributeType.getOrderingMatchingRule(); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public String toString() |
| | | { |
| | | return indexConfig.getAttributeType().getNameOrOID() + ".ordering"; |
| | | return attributeType.getNameOrOID() + ".ordering"; |
| | | } |
| | | |
| | | /** |
| | |
| | | Set<ASN1OctetString> keys) |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.getAttribute(indexConfig.getAttributeType()); |
| | | entry.getAttribute(attributeType); |
| | | if (attrList != null) |
| | | { |
| | | indexAttribute(attrList, keys); |
| | |
| | | { |
| | | List<Attribute> attrList; |
| | | |
| | | attrList = oldEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = oldEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> oldSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, oldSet); |
| | | |
| | | attrList = newEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = newEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> newSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, newSet); |
| | | |
| | |
| | | throws DatabaseException |
| | | { |
| | | List<Attribute> beforeList; |
| | | beforeList = oldEntry.getAttribute(indexConfig.getAttributeType()); |
| | | beforeList = oldEntry.getAttribute(attributeType); |
| | | |
| | | // Pick out the modifications that apply to this indexed attribute |
| | | |
| | |
| | | { |
| | | Attribute modAttr = mod.getAttribute(); |
| | | AttributeType modAttrType = modAttr.getAttributeType(); |
| | | if (modAttrType.equals(indexConfig.getAttributeType())) |
| | | if (modAttrType.equals(attributeType)) |
| | | { |
| | | switch (mod.getModificationType()) |
| | | { |
| | |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.AttributeType; |
| | | |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.DatabaseException; |
| | |
| | | new AttributeIndex.KeyComparator(); |
| | | |
| | | /** |
| | | * The attribute index configuration for which this instance will |
| | | * The attribute type for which this instance will |
| | | * generate index keys. |
| | | */ |
| | | private IndexConfig indexConfig; |
| | | private AttributeType attributeType; |
| | | |
| | | /** |
| | | * Create a new attribute presence indexer. |
| | | * @param indexConfig The attribute index configuration for which the indexer |
| | | * @param attributeType The attribute type for which the indexer |
| | | * is required. |
| | | */ |
| | | public PresenceIndexer(IndexConfig indexConfig) |
| | | public PresenceIndexer(AttributeType attributeType) |
| | | { |
| | | this.indexConfig = indexConfig; |
| | | this.attributeType = attributeType; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public String toString() |
| | | { |
| | | return indexConfig.getAttributeType().getNameOrOID() + ".presence"; |
| | | return attributeType.getNameOrOID() + ".presence"; |
| | | } |
| | | |
| | | /** |
| | |
| | | Set<ASN1OctetString> keys) throws DatabaseException |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.getAttribute(indexConfig.getAttributeType()); |
| | | entry.getAttribute(attributeType); |
| | | if (attrList != null) |
| | | { |
| | | if (!attrList.isEmpty()) |
| | |
| | | { |
| | | List<Attribute> beforeList, afterList; |
| | | |
| | | beforeList = oldEntry.getAttribute(indexConfig.getAttributeType()); |
| | | afterList = newEntry.getAttribute(indexConfig.getAttributeType()); |
| | | beforeList = oldEntry.getAttribute(attributeType); |
| | | afterList = newEntry.getAttribute(attributeType); |
| | | |
| | | if (beforeList == null || beforeList.isEmpty()) |
| | | { |
| | |
| | | timer.scheduleAtFixedRate(progressTask, progressInterval, |
| | | progressInterval); |
| | | |
| | | entryContainer.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | for(IndexRebuildThread thread : waitingThreads) |
| | | { |
| | | thread.clearDatabase(); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | if(!rebuildConfig.includesSystemIndex()) |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | |
| | | if(!rebuildConfig.includesSystemIndex()) |
| | | { |
| | | entryContainer.sharedLock.lock(); |
| | | } |
| | | try |
| | | { |
| | | while(!waitingThreads.isEmpty()) |
| | |
| | | finally |
| | | { |
| | | timer.cancel(); |
| | | if(rebuildConfig.includesSystemIndex()) |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | else |
| | | { |
| | | entryContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | long totalProcessed = 0; |
| | |
| | | /** |
| | | * Dispatch a set of threads based on their dependency and ordering. |
| | | */ |
| | | private void dispatchThreads() |
| | | private void dispatchThreads() throws DatabaseException |
| | | { |
| | | for(IndexRebuildThread t : waitingThreads) |
| | | { |
| | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | import static org.opends.server.messages.ConfigMessages. |
| | | MSGID_CONFIG_BACKEND_MODE_INVALID; |
| | | import static org.opends.server.messages.ConfigMessages. |
| | | MSGID_CONFIG_BACKEND_INSANE_MODE; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.config.ConfigException; |
| | | import static org.opends.server.util.StaticUtils.getFileForPath; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | /** |
| | | * Wrapper class for the JE environment. Root container holds all the entry |
| | |
| | | /** |
| | | * The backend configuration. |
| | | */ |
| | | private Config config; |
| | | private JEBackendCfg config; |
| | | |
| | | /** |
| | | * The backend to which this entry root container belongs. |
| | |
| | | * @param backend A reference to the JE back end that is creating this |
| | | * root container. |
| | | */ |
| | | public RootContainer(Config config, Backend backend) |
| | | public RootContainer(Backend backend, JEBackendCfg config) |
| | | { |
| | | this.env = null; |
| | | this.monitor = null; |
| | | this.entryContainers = new ConcurrentHashMap<DN, EntryContainer>(); |
| | | this.backend = backend; |
| | | this.config = config; |
| | | |
| | | config.addJEChangeListener(this); |
| | | } |
| | | |
| | | /** |
| | | * Helper method to apply database directory permissions and create a new |
| | | * JE environment. |
| | | * Opens the root container using the JE configuration object provided. |
| | | * |
| | | * @param backendDirectory The environment home directory for JE. |
| | | * @param backendPermission The file permissions for the environment home |
| | | * directory. |
| | | * @param envConfig The JE environment configuration. |
| | | * @throws DatabaseException If an error occurs when creating the environment. |
| | | * @throws ConfigException If an configuration error occurs while creating |
| | | * the enviornment. |
| | | */ |
| | | private void open(File backendDirectory, |
| | | FilePermission backendPermission, |
| | | EnvironmentConfig envConfig) throws DatabaseException |
| | | public void open(EnvironmentConfig envConfig) |
| | | throws DatabaseException, ConfigException |
| | | { |
| | | // Determine the backend database directory. |
| | | File backendDirectory = getFileForPath(config.getBackendDirectory()); |
| | | |
| | | //Make sure the directory is valid. |
| | | if (!backendDirectory.isDirectory()) |
| | | { |
| | | int msgID = MSGID_JEB_DIRECTORY_INVALID; |
| | | String message = getMessage(msgID, backendDirectory.getPath()); |
| | | throw new ConfigException(MSGID_JEB_DIRECTORY_INVALID, message); |
| | | } |
| | | |
| | | FilePermission backendPermission; |
| | | try |
| | | { |
| | | backendPermission = |
| | | FilePermission.decodeUNIXMode(config.getBackendMode()); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | int msgID = MSGID_CONFIG_BACKEND_MODE_INVALID; |
| | | String message = getMessage(msgID, config.dn().toString()); |
| | | throw new ConfigException(msgID, message); |
| | | } |
| | | |
| | | //Make sure the mode will allow the server itself access to |
| | | //the database |
| | | if(!backendPermission.isOwnerWritable() || |
| | | !backendPermission.isOwnerReadable() || |
| | | !backendPermission.isOwnerExecutable()) |
| | | { |
| | | int msgID = MSGID_CONFIG_BACKEND_INSANE_MODE; |
| | | String message = getMessage(msgID); |
| | | throw new ConfigException(msgID, message); |
| | | } |
| | | |
| | | // Get the backend database backendDirectory permissions and apply |
| | | if(FilePermission.canSetPermissions()) |
| | | { |
| | |
| | | "config: %n%s", JEVersion.CURRENT_VERSION.toString(), |
| | | env.getConfig().toString()); |
| | | |
| | | // Get current size of heap in bytes |
| | | long heapSize = Runtime.getRuntime().totalMemory(); |
| | | // Get current size of heap in bytes |
| | | long heapSize = Runtime.getRuntime().totalMemory(); |
| | | |
| | | // Get maximum size of heap in bytes. The heap cannot grow beyond this size. |
| | | // Any attempt will result in an OutOfMemoryException. |
| | | long heapMaxSize = Runtime.getRuntime().maxMemory(); |
| | | // Get maximum size of heap in bytes. The heap cannot grow beyond this |
| | | // size. |
| | | // Any attempt will result in an OutOfMemoryException. |
| | | long heapMaxSize = Runtime.getRuntime().maxMemory(); |
| | | |
| | | // Get amount of free memory within the heap in bytes. This size will |
| | | // Get amount of free memory within the heap in bytes. This size will |
| | | // increase |
| | | // after garbage collection and decrease as new objects are created. |
| | | long heapFreeSize = Runtime.getRuntime().freeMemory(); |
| | | // after garbage collection and decrease as new objects are created. |
| | | long heapFreeSize = Runtime.getRuntime().freeMemory(); |
| | | |
| | | TRACER.debugInfo("Current size of heap: %d bytes", heapSize); |
| | | TRACER.debugInfo("Max size of heap: %d bytes", heapMaxSize); |
| | | TRACER.debugInfo("Free memory in heap: %d bytes", heapFreeSize); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Opens the root container. |
| | | * |
| | | * @throws DatabaseException If an error occurs when opening the container. |
| | | */ |
| | | public void open() throws DatabaseException |
| | | { |
| | | open(config.getBackendDirectory(), |
| | | config.getBackendPermission(), |
| | | config.getEnvironmentConfig()); |
| | | } |
| | | |
| | | /** |
| | | * Opens the root container using the configuration parameters provided. Any |
| | | * configuration parameters provided will override the parameters in the |
| | | * JE configuration object. |
| | | * |
| | | * @param backendDirectory The environment home directory for JE. |
| | | * @param backendPermission he file permissions for the environment home |
| | | * directory. |
| | | * @param readOnly Open the container in read only mode. |
| | | * @param allowCreate Allow creating new entries in the container. |
| | | * @param transactional Use transactions on operations. |
| | | * @param txnNoSync Use asynchronous transactions. |
| | | * @param isLocking Create the environment with locking. |
| | | * @param runCheckPointer Start the checkpointer. |
| | | * @throws DatabaseException If an error occurs when openinng the container. |
| | | */ |
| | | public void open(File backendDirectory, |
| | | FilePermission backendPermission, |
| | | boolean readOnly, |
| | | boolean allowCreate, |
| | | boolean transactional, |
| | | boolean txnNoSync, |
| | | boolean isLocking, |
| | | boolean runCheckPointer) throws DatabaseException |
| | | { |
| | | EnvironmentConfig envConfig; |
| | | if(config.getEnvironmentConfig() != null) |
| | | { |
| | | envConfig = config.getEnvironmentConfig(); |
| | | } |
| | | else |
| | | { |
| | | envConfig = new EnvironmentConfig(); |
| | | } |
| | | envConfig.setReadOnly(readOnly); |
| | | envConfig.setAllowCreate(allowCreate); |
| | | envConfig.setTransactional(transactional); |
| | | envConfig.setTxnNoSync(txnNoSync); |
| | | envConfig.setConfigParam("je.env.isLocking", String.valueOf(isLocking)); |
| | | envConfig.setConfigParam("je.env.runCheckpointer", |
| | | String.valueOf(runCheckPointer)); |
| | | |
| | | open(backendDirectory, backendPermission, envConfig); |
| | | openEntryContainers(config.getBackendBaseDN()); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return The opened entry container. |
| | | * @throws DatabaseException If an error occurs while opening the entry |
| | | * container. |
| | | * @throws ConfigException If an configuration error occurs while opening |
| | | * the entry container. |
| | | */ |
| | | public EntryContainer openEntryContainer(DN baseDN) throws DatabaseException |
| | | public EntryContainer openEntryContainer(DN baseDN) |
| | | throws DatabaseException, ConfigException |
| | | { |
| | | EntryContainer ec = new EntryContainer(baseDN, backend, config, env, this); |
| | | EntryContainer ec1=this.entryContainers.get(baseDN); |
| | | |
| | | //If an entry container for this baseDN is already open we don't allow |
| | | //another to be opened. |
| | | if (ec1 != null) |
| | | throw new DatabaseException("Entry container for baseDN " + |
| | | baseDN.toString() + " already is open."); |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | ec.openReadOnly(); |
| | | } |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | ec.openNonTransactional(true); |
| | | } |
| | | else |
| | | { |
| | | ec.open(); |
| | | } |
| | | if (ec1 != null) |
| | | throw new DatabaseException("Entry container for baseDN " + |
| | | baseDN.toString() + " already is open."); |
| | | |
| | | ec.open(); |
| | | this.entryContainers.put(baseDN, ec); |
| | | |
| | | return ec; |
| | |
| | | * @param baseDNs The base DNs of the entry containers to open. |
| | | * @throws DatabaseException If an error occurs while opening the entry |
| | | * container. |
| | | * @throws ConfigException if a configuration error occurs while opening the |
| | | * container. |
| | | */ |
| | | public void openEntryContainers(DN[] baseDNs) throws DatabaseException |
| | | private void openEntryContainers(Set<DN> baseDNs) |
| | | throws DatabaseException, ConfigException |
| | | { |
| | | EntryID id; |
| | | EntryID highestID = null; |
| | |
| | | */ |
| | | public void closeEntryContainer(DN baseDN) throws DatabaseException |
| | | { |
| | | getEntryContainer(baseDN).close(); |
| | | entryContainers.remove(baseDN); |
| | | EntryContainer ec = getEntryContainer(baseDN); |
| | | ec.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | ec.close(); |
| | | entryContainers.remove(baseDN); |
| | | } |
| | | finally |
| | | { |
| | | ec.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public void removeEntryContainer(DN baseDN) throws DatabaseException |
| | | { |
| | | getEntryContainer(baseDN).close(); |
| | | getEntryContainer(baseDN).removeContainer(); |
| | | entryContainers.remove(baseDN); |
| | | EntryContainer ec = getEntryContainer(baseDN); |
| | | ec.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | ec.close(); |
| | | ec.removeContainer(); |
| | | entryContainers.remove(baseDN); |
| | | } |
| | | finally |
| | | { |
| | | ec.exclusiveLock.unlock(); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | |
| | | if(monitor == null) |
| | | { |
| | | String monitorName = backend.getBackendID() + " Database Environment"; |
| | | monitor = new DatabaseEnvironmentMonitor(monitorName, env); |
| | | monitor = new DatabaseEnvironmentMonitor(monitorName, this); |
| | | } |
| | | |
| | | return monitor; |
| | |
| | | /** |
| | | * Preload the database cache. There is no preload if the configured preload |
| | | * time limit is zero. |
| | | * |
| | | * @param timeLimit The time limit for the preload process. |
| | | */ |
| | | public void preload() |
| | | public void preload(long timeLimit) |
| | | { |
| | | long timeLimit = config.getPreloadTimeLimit(); |
| | | |
| | | if (timeLimit > 0) |
| | | { |
| | | // Get a list of all the databases used by the backend. |
| | | ArrayList<Database> dbList = new ArrayList<Database>(); |
| | | ArrayList<DatabaseContainer> dbList = |
| | | new ArrayList<DatabaseContainer>(); |
| | | for (EntryContainer ec : entryContainers.values()) |
| | | { |
| | | ec.listDatabases(dbList); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | ec.listDatabases(dbList); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | // Sort the list in order of priority. |
| | |
| | | PreloadConfig preloadConfig = new PreloadConfig(); |
| | | preloadConfig.setLoadLNs(true); |
| | | |
| | | for (Database db : dbList) |
| | | for (DatabaseContainer db : dbList) |
| | | { |
| | | // Calculate the remaining time. |
| | | long timeRemaining = timeEnd - System.currentTimeMillis(); |
| | |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("file=" + db.getDatabaseName() + |
| | | TRACER.debugInfo("file=" + db.getName() + |
| | | " LNs=" + preloadStats.getNLNsLoaded()); |
| | | } |
| | | |
| | |
| | | { |
| | | for(DN baseDN : entryContainers.keySet()) |
| | | { |
| | | entryContainers.get(baseDN).close(); |
| | | entryContainers.remove(baseDN); |
| | | closeEntryContainer(baseDN); |
| | | } |
| | | |
| | | if (env != null) env.close(); |
| | | if (env != null) |
| | | { |
| | | env.close(); |
| | | env = null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the environment lock stats of the JE environment used in this |
| | | * root container. |
| | | * |
| | | * @param statsConfig The configuration to use for the EnvironmentStats |
| | | * object. |
| | | * @return The environment status of the JE environment. |
| | | * @throws DatabaseException If an error occurs while retriving the stats |
| | | * object. |
| | | */ |
| | | public LockStats getEnvironmentLockStats(StatsConfig statsConfig) |
| | | throws DatabaseException |
| | | { |
| | | return env.getLockStats(statsConfig); |
| | | } |
| | | |
| | | /** |
| | | * Get the environment transaction stats of the JE environment used |
| | | * in this root container. |
| | | * |
| | | * @param statsConfig The configuration to use for the EnvironmentStats |
| | | * object. |
| | | * @return The environment status of the JE environment. |
| | | * @throws DatabaseException If an error occurs while retriving the stats |
| | | * object. |
| | | */ |
| | | public TransactionStats getEnvironmentTransactionStats( |
| | | StatsConfig statsConfig) throws DatabaseException |
| | | { |
| | | return env.getTransactionStats(statsConfig); |
| | | } |
| | | |
| | | /** |
| | | * Get the environment config of the JE environment used in this root |
| | | * container. |
| | | * |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get the backend configuration used by this root container. |
| | | * |
| | | * @return The JE backend configuration used by this root container. |
| | | */ |
| | | public JEBackendCfg getConfiguration() |
| | | { |
| | | return config; |
| | | } |
| | | |
| | | /** |
| | | * Get the total number of entries in this root container. |
| | | * |
| | | * @return The number of entries in this root container |
| | |
| | | long entryCount = 0; |
| | | for(EntryContainer ec : this.entryContainers.values()) |
| | | { |
| | | entryCount += ec.getEntryCount(); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | entryCount += ec.getEntryCount(); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | return entryCount; |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isConfigurationChangeAcceptable( |
| | | JEBackendCfg cfg, |
| | | List<String> unacceptableReasons) |
| | | JEBackendCfg cfg, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | boolean acceptable = true; |
| | | |
| | | // This listener handles only the changes to JE properties. |
| | | File backendDirectory = getFileForPath(cfg.getBackendDirectory()); |
| | | //Make sure the directory is valid. |
| | | if (!backendDirectory.isDirectory()) |
| | | { |
| | | int msgID = MSGID_JEB_DIRECTORY_INVALID; |
| | | String message = getMessage(msgID, backendDirectory.getPath()); |
| | | unacceptableReasons.add(message); |
| | | acceptable = false; |
| | | } |
| | | |
| | | try |
| | | { |
| | | FilePermission newBackendPermission = |
| | | FilePermission.decodeUNIXMode(cfg.getBackendMode()); |
| | | |
| | | //Make sure the mode will allow the server itself access to |
| | | //the database |
| | | if(!newBackendPermission.isOwnerWritable() || |
| | | !newBackendPermission.isOwnerReadable() || |
| | | !newBackendPermission.isOwnerExecutable()) |
| | | { |
| | | int msgID = MSGID_CONFIG_BACKEND_INSANE_MODE; |
| | | String message = getMessage(msgID); |
| | | unacceptableReasons.add(message); |
| | | acceptable = false; |
| | | } |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | int msgID = MSGID_CONFIG_BACKEND_MODE_INVALID; |
| | | String message = getMessage(msgID, cfg.dn().toString()); |
| | | unacceptableReasons.add(message); |
| | | acceptable = false; |
| | | } |
| | | |
| | | try |
| | | { |
| | |
| | | |
| | | try |
| | | { |
| | | // Check if any JE non-mutable properties were changed. |
| | | EnvironmentConfig oldEnvConfig = env.getConfig(); |
| | | EnvironmentConfig newEnvConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS; |
| | | for (Object o : paramsMap.values()) |
| | | if(env != null) |
| | | { |
| | | ConfigParam param = (ConfigParam) o; |
| | | if (!param.isMutable()) |
| | | // Check if any JE non-mutable properties were changed. |
| | | EnvironmentConfig oldEnvConfig = env.getConfig(); |
| | | EnvironmentConfig newEnvConfig = |
| | | ConfigurableEnvironment.parseConfigEntry(cfg); |
| | | Map paramsMap = EnvironmentParams.SUPPORTED_PARAMS; |
| | | for (Object o : paramsMap.values()) |
| | | { |
| | | String oldValue = oldEnvConfig.getConfigParam(param.getName()); |
| | | String newValue = newEnvConfig.getConfigParam(param.getName()); |
| | | if (!oldValue.equalsIgnoreCase(newValue)) |
| | | ConfigParam param = (ConfigParam) o; |
| | | if (!param.isMutable()) |
| | | { |
| | | adminActionRequired = true; |
| | | String configAttr = ConfigurableEnvironment. |
| | | getAttributeForProperty(param.getName()); |
| | | if (configAttr != null) |
| | | String oldValue = oldEnvConfig.getConfigParam(param.getName()); |
| | | String newValue = newEnvConfig.getConfigParam(param.getName()); |
| | | if (!oldValue.equalsIgnoreCase(newValue)) |
| | | { |
| | | int msgID = MSGID_JEB_CONFIG_ATTR_REQUIRES_RESTART; |
| | | messages.add(getMessage(msgID, configAttr)); |
| | | } |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("The change to the following property will " + |
| | | "take effect when the backend is restarted: " + |
| | | param.getName()); |
| | | adminActionRequired = true; |
| | | String configAttr = ConfigurableEnvironment. |
| | | getAttributeForProperty(param.getName()); |
| | | if (configAttr != null) |
| | | { |
| | | int msgID = MSGID_JEB_CONFIG_ATTR_REQUIRES_RESTART; |
| | | messages.add(getMessage(msgID, configAttr)); |
| | | } |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("The change to the following property will " + |
| | | "take effect when the backend is restarted: " + |
| | | param.getName()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // This takes care of changes to the JE environment for those |
| | | // properties that are mutable at runtime. |
| | | env.setMutableConfig(newEnvConfig); |
| | | // This takes care of changes to the JE environment for those |
| | | // properties that are mutable at runtime. |
| | | env.setMutableConfig(newEnvConfig); |
| | | |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugInfo(env.getConfig().toString()); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugInfo(env.getConfig().toString()); |
| | | } |
| | | } |
| | | this.config = cfg; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | messages.add(e.getMessage()); |
| | | messages.add(StaticUtils.stackTraceToSingleLineString(e)); |
| | | ccr = new ConfigChangeResult(DirectoryServer.getServerErrorResultCode(), |
| | | adminActionRequired, |
| | | messages); |
| 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.backends.jeb; |
| | | |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | | import org.opends.server.util.StaticUtils; |
| | | import com.sleepycat.je.*; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * This class is responsible for storing the configuration state of |
| | | * the JE backend for a particular suffix. |
| | | */ |
| | | public class State extends DatabaseContainer |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | private static final byte[] falseBytes = new byte[]{0x00}; |
| | | private static final byte[] trueBytes = new byte[]{0x01}; |
| | | |
| | | /** |
| | | * Create a new State object. |
| | | * |
| | | * @param name The name of the entry database. |
| | | * @param env The JE Environment. |
| | | * @param entryContainer The entryContainer of the entry database. |
| | | * @throws com.sleepycat.je.DatabaseException If an error occurs in the |
| | | * JE database. |
| | | * |
| | | */ |
| | | State(String name, Environment env, EntryContainer entryContainer) |
| | | throws DatabaseException |
| | | { |
| | | super(name, env, entryContainer); |
| | | |
| | | DatabaseConfig dbNodupsConfig = new DatabaseConfig(); |
| | | |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | dbNodupsConfig.setReadOnly(true); |
| | | dbNodupsConfig.setAllowCreate(false); |
| | | dbNodupsConfig.setTransactional(false); |
| | | } |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(false); |
| | | dbNodupsConfig.setDeferredWrite(true); |
| | | } |
| | | else |
| | | { |
| | | dbNodupsConfig.setAllowCreate(true); |
| | | dbNodupsConfig.setTransactional(true); |
| | | } |
| | | |
| | | this.dbConfig = dbNodupsConfig; |
| | | } |
| | | |
| | | /** |
| | | * Remove a record from the entry database. |
| | | * |
| | | * @param txn The database transaction or null if none. |
| | | * @param index The index storing the trusted state info. |
| | | * @return true if the entry was removed, false if it was not. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public boolean removeIndexTrustState(Transaction txn, Index index) |
| | | throws DatabaseException |
| | | { |
| | | DatabaseEntry key = |
| | | new DatabaseEntry(StaticUtils.getBytes(index.getName())); |
| | | |
| | | OperationStatus status = delete(txn, key); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Fetch index state from the database. |
| | | * @param txn The database transaction or null if none. |
| | | * @param index The index storing the trusted state info. |
| | | * @return The trusted state of the index in the database. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public boolean getIndexTrustState(Transaction txn, Index index) |
| | | throws DatabaseException |
| | | { |
| | | DatabaseEntry key = |
| | | new DatabaseEntry(StaticUtils.getBytes(index.getName())); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | OperationStatus status; |
| | | status = read(txn, key, data, LockMode.DEFAULT); |
| | | |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | byte[] bytes = data.getData(); |
| | | return Arrays.equals(bytes, trueBytes); |
| | | } |
| | | |
| | | /** |
| | | * Put index state to database. |
| | | * @param txn The database transaction or null if none. |
| | | * @param index The index storing the trusted state info. |
| | | * @param trusted The state value to put into the database. |
| | | * @return true if the entry was written, false if it was not. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public boolean putIndexTrustState(Transaction txn, Index index, |
| | | boolean trusted) |
| | | throws DatabaseException |
| | | { |
| | | DatabaseEntry key = |
| | | new DatabaseEntry(StaticUtils.getBytes(index.getName())); |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | |
| | | if(trusted) |
| | | data.setData(trueBytes); |
| | | else |
| | | data.setData(falseBytes); |
| | | |
| | | OperationStatus status; |
| | | status = put(txn, key, data); |
| | | if (status != OperationStatus.SUCCESS) |
| | | { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | } |
| | |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import com.sleepycat.je.Transaction; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.Attribute; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.Entry; |
| | | import org.opends.server.types.Modification; |
| | | import org.opends.server.types.*; |
| | | |
| | | import java.util.Comparator; |
| | | import java.util.HashSet; |
| | |
| | | new AttributeIndex.KeyComparator(); |
| | | |
| | | /** |
| | | * The attribute index configuration for which this instance will |
| | | * The attribute type for which this instance will |
| | | * generate index keys. |
| | | */ |
| | | private IndexConfig indexConfig; |
| | | private AttributeType attributeType; |
| | | |
| | | /** |
| | | * The substring length. |
| | | */ |
| | | private int substrLength; |
| | | |
| | | /** |
| | | * Create a new attribute substring indexer for the given index configuration. |
| | | * @param indexConfig The index configuration for which an indexer is |
| | | * @param attributeType The attribute type for which an indexer is |
| | | * required. |
| | | * @param substringLength The decomposed substring length. |
| | | */ |
| | | public SubstringIndexer(IndexConfig indexConfig) |
| | | public SubstringIndexer(AttributeType attributeType, int substringLength) |
| | | { |
| | | this.indexConfig = indexConfig; |
| | | this.attributeType = attributeType; |
| | | this.substrLength = substringLength; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public String toString() |
| | | { |
| | | return indexConfig.getAttributeType().getNameOrOID() + ".substring"; |
| | | return attributeType.getNameOrOID() + ".substring"; |
| | | } |
| | | |
| | | /** |
| | |
| | | Set<ASN1OctetString> keys) |
| | | { |
| | | List<Attribute> attrList = |
| | | entry.getAttribute(indexConfig.getAttributeType()); |
| | | entry.getAttribute(attributeType); |
| | | if (attrList != null) |
| | | { |
| | | indexAttribute(attrList, keys); |
| | |
| | | { |
| | | List<Attribute> attrList; |
| | | |
| | | attrList = oldEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = oldEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> oldSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, oldSet); |
| | | |
| | | attrList = newEntry.getAttribute(indexConfig.getAttributeType()); |
| | | attrList = newEntry.getAttribute(attributeType); |
| | | Set<ASN1OctetString> newSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(attrList, newSet); |
| | | |
| | |
| | | */ |
| | | private void substringKeys(byte[] value, Set<ASN1OctetString> set) |
| | | { |
| | | int substrLength = indexConfig.getSubstringLength(); |
| | | byte[] keyBytes; |
| | | |
| | | // Example: The value is ABCDE and the substring length is 3. |
| | |
| | | import org.opends.server.types.DebugLogLevel; |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.JebMessages.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.HashMap; |
| | |
| | | private VerifyConfig verifyConfig; |
| | | |
| | | /** |
| | | * The configuration of the JE backend. |
| | | */ |
| | | private Config config; |
| | | |
| | | /** |
| | | * The root container used for the verify job. |
| | | */ |
| | | RootContainer rootContainer; |
| | |
| | | /** |
| | | * Construct a VerifyJob. |
| | | * |
| | | * @param config The backend configuration. |
| | | * @param verifyConfig The verify configuration. |
| | | */ |
| | | public VerifyJob(Config config, VerifyConfig verifyConfig) |
| | | public VerifyJob(VerifyConfig verifyConfig) |
| | | { |
| | | this.verifyConfig = verifyConfig; |
| | | this.config = config; |
| | | } |
| | | |
| | | /** |
| | |
| | | EntryContainer entryContainer = |
| | | rootContainer.getEntryContainer(verifyConfig.getBaseDN()); |
| | | |
| | | ArrayList<String> completeList = verifyConfig.getCompleteList(); |
| | | ArrayList<String> cleanList = verifyConfig.getCleanList(); |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | ArrayList<String> completeList = verifyConfig.getCompleteList(); |
| | | ArrayList<String> cleanList = verifyConfig.getCleanList(); |
| | | |
| | | boolean cleanMode = false; |
| | | if (completeList.isEmpty() && cleanList.isEmpty()) |
| | | { |
| | | verifyDN2ID = true; |
| | | verifyID2Children = true; |
| | | verifyID2Subtree = true; |
| | | Map<AttributeType,IndexConfig> indexMap = config.getIndexConfigMap(); |
| | | for (IndexConfig ic : indexMap.values()) |
| | | boolean cleanMode = false; |
| | | if (completeList.isEmpty() && cleanList.isEmpty()) |
| | | { |
| | | AttributeIndex attrIndex = |
| | | entryContainer.getAttributeIndex(ic.getAttributeType()); |
| | | attrIndexList.add(attrIndex); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | ArrayList<String> list; |
| | | if (!completeList.isEmpty()) |
| | | { |
| | | list = completeList; |
| | | verifyDN2ID = true; |
| | | verifyID2Children = true; |
| | | verifyID2Subtree = true; |
| | | attrIndexList.addAll(entryContainer.getAttributeIndexes()); |
| | | } |
| | | else |
| | | { |
| | | list = cleanList; |
| | | cleanMode = true; |
| | | } |
| | | |
| | | for (String index : list) |
| | | { |
| | | String lowerName = index.toLowerCase(); |
| | | if (lowerName.equals("dn2id")) |
| | | ArrayList<String> list; |
| | | if (!completeList.isEmpty()) |
| | | { |
| | | verifyDN2ID = true; |
| | | } |
| | | else if (lowerName.equals("id2children")) |
| | | { |
| | | verifyID2Children = true; |
| | | } |
| | | else if (lowerName.equals("id2subtree")) |
| | | { |
| | | verifyID2Subtree = true; |
| | | list = completeList; |
| | | } |
| | | else |
| | | { |
| | | AttributeType attrType = DirectoryServer.getAttributeType(lowerName); |
| | | if (attrType == null) |
| | | list = cleanList; |
| | | cleanMode = true; |
| | | } |
| | | |
| | | for (String index : list) |
| | | { |
| | | String lowerName = index.toLowerCase(); |
| | | if (lowerName.equals("dn2id")) |
| | | { |
| | | int msgID = MSGID_JEB_ATTRIBUTE_INDEX_NOT_CONFIGURED; |
| | | String msg = getMessage(msgID, index); |
| | | throw new JebException(msgID, msg); |
| | | verifyDN2ID = true; |
| | | } |
| | | AttributeIndex attrIndex = entryContainer.getAttributeIndex(attrType); |
| | | if (attrIndex == null) |
| | | else if (lowerName.equals("id2children")) |
| | | { |
| | | int msgID = MSGID_JEB_ATTRIBUTE_INDEX_NOT_CONFIGURED; |
| | | String msg = getMessage(msgID, index); |
| | | throw new JebException(msgID, msg); |
| | | verifyID2Children = true; |
| | | } |
| | | attrIndexList.add(attrIndex); |
| | | } |
| | | } |
| | | } |
| | | |
| | | entryLimitMap = |
| | | new IdentityHashMap<Index,HashMap<ByteString,Long>>( |
| | | attrIndexList.size()); |
| | | |
| | | // We will be updating these files independently of the indexes |
| | | // so we need direct access to them rather than going through |
| | | // the entry entryContainer methods. |
| | | id2entry = entryContainer.getID2Entry(); |
| | | dn2id = entryContainer.getDN2ID(); |
| | | id2c = entryContainer.getID2Children(); |
| | | id2s = entryContainer.getID2Subtree(); |
| | | |
| | | // Make a note of the time we started. |
| | | long startTime = System.currentTimeMillis(); |
| | | |
| | | // Start a timer for the progress report. |
| | | Timer timer = new Timer(); |
| | | TimerTask progressTask = new ProgressTask(); |
| | | timer.scheduleAtFixedRate(progressTask, progressInterval, |
| | | progressInterval); |
| | | |
| | | // Iterate through the index keys. |
| | | try |
| | | { |
| | | if (cleanMode) |
| | | { |
| | | iterateIndex(); |
| | | } |
| | | else |
| | | { |
| | | iterateID2Entry(); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | timer.cancel(); |
| | | } |
| | | |
| | | long finishTime = System.currentTimeMillis(); |
| | | long totalTime = (finishTime - startTime); |
| | | |
| | | float rate = 0; |
| | | if (totalTime > 0) |
| | | { |
| | | rate = 1000f*keyCount / totalTime; |
| | | } |
| | | |
| | | addStatEntry(statEntry, "verify-error-count", |
| | | String.valueOf(errorCount)); |
| | | addStatEntry(statEntry, "verify-key-count", |
| | | String.valueOf(keyCount)); |
| | | if (cleanMode) |
| | | { |
| | | int msgID = MSGID_JEB_VERIFY_CLEAN_FINAL_STATUS; |
| | | String message = getMessage(msgID, keyCount, errorCount, |
| | | totalTime/1000, rate); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | |
| | | if (multiReferenceCount > 0) |
| | | { |
| | | float averageEntryReferences = 0; |
| | | if (keyCount > 0) |
| | | { |
| | | averageEntryReferences = (float)entryReferencesCount/keyCount; |
| | | } |
| | | |
| | | msgID = MSGID_JEB_VERIFY_MULTIPLE_REFERENCE_COUNT; |
| | | message = getMessage(msgID, multiReferenceCount); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-multiple-reference-count", |
| | | String.valueOf(multiReferenceCount)); |
| | | |
| | | msgID = MSGID_JEB_VERIFY_ENTRY_LIMIT_EXCEEDED_COUNT; |
| | | message = getMessage(msgID, entryLimitExceededCount); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-entry-limit-exceeded-count", |
| | | String.valueOf(entryLimitExceededCount)); |
| | | |
| | | msgID = MSGID_JEB_VERIFY_AVERAGE_REFERENCE_COUNT; |
| | | message = getMessage(msgID, averageEntryReferences); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-average-reference-count", |
| | | String.valueOf(averageEntryReferences)); |
| | | |
| | | msgID = MSGID_JEB_VERIFY_MAX_REFERENCE_COUNT; |
| | | message = getMessage(msgID, maxEntryPerValue); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-max-reference-count", |
| | | String.valueOf(maxEntryPerValue)); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_JEB_VERIFY_FINAL_STATUS; |
| | | String message = getMessage(msgID, keyCount, errorCount, |
| | | totalTime/1000, rate); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | //TODO add entry-limit-stats to the statEntry |
| | | if (entryLimitMap.size() > 0) |
| | | { |
| | | msgID = MSGID_JEB_VERIFY_ENTRY_LIMIT_STATS_HEADER; |
| | | message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | |
| | | for (Map.Entry<Index,HashMap<ByteString,Long>> mapEntry : |
| | | entryLimitMap.entrySet()) |
| | | { |
| | | Index index = mapEntry.getKey(); |
| | | Long[] values = mapEntry.getValue().values().toArray(new Long[0]); |
| | | |
| | | // Calculate the median value for entry limit exceeded. |
| | | Arrays.sort(values); |
| | | long medianValue; |
| | | int x = values.length / 2; |
| | | if (values.length % 2 == 0) |
| | | else if (lowerName.equals("id2subtree")) |
| | | { |
| | | medianValue = (values[x] + values[x-1]) / 2; |
| | | verifyID2Subtree = true; |
| | | } |
| | | else |
| | | { |
| | | medianValue = values[x]; |
| | | AttributeType attrType = |
| | | DirectoryServer.getAttributeType(lowerName); |
| | | if (attrType == null) |
| | | { |
| | | int msgID = MSGID_JEB_ATTRIBUTE_INDEX_NOT_CONFIGURED; |
| | | String msg = getMessage(msgID, index); |
| | | throw new JebException(msgID, msg); |
| | | } |
| | | AttributeIndex attrIndex = |
| | | entryContainer.getAttributeIndex(attrType); |
| | | if (attrIndex == null) |
| | | { |
| | | int msgID = MSGID_JEB_ATTRIBUTE_INDEX_NOT_CONFIGURED; |
| | | String msg = getMessage(msgID, index); |
| | | throw new JebException(msgID, msg); |
| | | } |
| | | attrIndexList.add(attrIndex); |
| | | } |
| | | |
| | | msgID = MSGID_JEB_VERIFY_ENTRY_LIMIT_STATS_ROW; |
| | | message = getMessage(msgID, index.toString(), values.length, |
| | | values[0], values[values.length-1], medianValue); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | entryLimitMap = |
| | | new IdentityHashMap<Index,HashMap<ByteString,Long>>( |
| | | attrIndexList.size()); |
| | | |
| | | // We will be updating these files independently of the indexes |
| | | // so we need direct access to them rather than going through |
| | | // the entry entryContainer methods. |
| | | id2entry = entryContainer.getID2Entry(); |
| | | dn2id = entryContainer.getDN2ID(); |
| | | id2c = entryContainer.getID2Children(); |
| | | id2s = entryContainer.getID2Subtree(); |
| | | |
| | | // Make a note of the time we started. |
| | | long startTime = System.currentTimeMillis(); |
| | | |
| | | // Start a timer for the progress report. |
| | | Timer timer = new Timer(); |
| | | TimerTask progressTask = new ProgressTask(); |
| | | timer.scheduleAtFixedRate(progressTask, progressInterval, |
| | | progressInterval); |
| | | |
| | | // Iterate through the index keys. |
| | | try |
| | | { |
| | | if (cleanMode) |
| | | { |
| | | iterateIndex(); |
| | | } |
| | | else |
| | | { |
| | | iterateID2Entry(); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | timer.cancel(); |
| | | } |
| | | |
| | | long finishTime = System.currentTimeMillis(); |
| | | long totalTime = (finishTime - startTime); |
| | | |
| | | float rate = 0; |
| | | if (totalTime > 0) |
| | | { |
| | | rate = 1000f*keyCount / totalTime; |
| | | } |
| | | |
| | | addStatEntry(statEntry, "verify-error-count", |
| | | String.valueOf(errorCount)); |
| | | addStatEntry(statEntry, "verify-key-count", |
| | | String.valueOf(keyCount)); |
| | | if (cleanMode) |
| | | { |
| | | int msgID = MSGID_JEB_VERIFY_CLEAN_FINAL_STATUS; |
| | | String message = getMessage(msgID, keyCount, errorCount, |
| | | totalTime/1000, rate); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | |
| | | if (multiReferenceCount > 0) |
| | | { |
| | | float averageEntryReferences = 0; |
| | | if (keyCount > 0) |
| | | { |
| | | averageEntryReferences = (float)entryReferencesCount/keyCount; |
| | | } |
| | | |
| | | msgID = MSGID_JEB_VERIFY_MULTIPLE_REFERENCE_COUNT; |
| | | message = getMessage(msgID, multiReferenceCount); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-multiple-reference-count", |
| | | String.valueOf(multiReferenceCount)); |
| | | |
| | | msgID = MSGID_JEB_VERIFY_ENTRY_LIMIT_EXCEEDED_COUNT; |
| | | message = getMessage(msgID, entryLimitExceededCount); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-entry-limit-exceeded-count", |
| | | String.valueOf(entryLimitExceededCount)); |
| | | |
| | | msgID = MSGID_JEB_VERIFY_AVERAGE_REFERENCE_COUNT; |
| | | message = getMessage(msgID, averageEntryReferences); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-average-reference-count", |
| | | String.valueOf(averageEntryReferences)); |
| | | |
| | | msgID = MSGID_JEB_VERIFY_MAX_REFERENCE_COUNT; |
| | | message = getMessage(msgID, maxEntryPerValue); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | addStatEntry(statEntry, "verify-max-reference-count", |
| | | String.valueOf(maxEntryPerValue)); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_JEB_VERIFY_FINAL_STATUS; |
| | | String message = getMessage(msgID, keyCount, errorCount, |
| | | totalTime/1000, rate); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | //TODO add entry-limit-stats to the statEntry |
| | | if (entryLimitMap.size() > 0) |
| | | { |
| | | msgID = MSGID_JEB_VERIFY_ENTRY_LIMIT_STATS_HEADER; |
| | | message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | |
| | | for (Map.Entry<Index,HashMap<ByteString,Long>> mapEntry : |
| | | entryLimitMap.entrySet()) |
| | | { |
| | | Index index = mapEntry.getKey(); |
| | | Long[] values = mapEntry.getValue().values().toArray(new Long[0]); |
| | | |
| | | // Calculate the median value for entry limit exceeded. |
| | | Arrays.sort(values); |
| | | long medianValue; |
| | | int x = values.length / 2; |
| | | if (values.length % 2 == 0) |
| | | { |
| | | medianValue = (values[x] + values[x-1]) / 2; |
| | | } |
| | | else |
| | | { |
| | | medianValue = values[x]; |
| | | } |
| | | |
| | | msgID = MSGID_JEB_VERIFY_ENTRY_LIMIT_STATS_ROW; |
| | | message = getMessage(msgID, index.toString(), values.length, |
| | | values[0], values[values.length-1], |
| | | medianValue); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | |
| | | * @param keyBytes The bytes of the key. |
| | | * @return A string that may be logged or printed. |
| | | */ |
| | | public String keyDump(Index index, byte[] keyBytes) |
| | | private String keyDump(Index index, byte[] keyBytes) |
| | | { |
| | | /* |
| | | String str; |
| | |
| | | * @param attrList The attribute to be checked. |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | */ |
| | | public void verifyAttribute(AttributeIndex attrIndex, EntryID entryID, |
| | | private void verifyAttribute(AttributeIndex attrIndex, EntryID entryID, |
| | | List<Attribute> attrList) |
| | | throws DirectoryException |
| | | { |
| | |
| | | Index substringIndex = attrIndex.substringIndex; |
| | | Index orderingIndex = attrIndex.orderingIndex; |
| | | Index approximateIndex = attrIndex.approximateIndex; |
| | | IndexConfig indexConfig = attrIndex.indexConfig; |
| | | DatabaseEntry presenceKey = AttributeIndex.presenceKey; |
| | | |
| | | // Presence index. |
| | | if (!attrList.isEmpty() && indexConfig.isPresenceIndex()) |
| | | if (!attrList.isEmpty() && presenceIndex != null) |
| | | { |
| | | try |
| | | { |
| | |
| | | byte[] normalizedBytes = value.getNormalizedValue().value(); |
| | | |
| | | // Equality index. |
| | | if (indexConfig.isEqualityIndex()) |
| | | if (equalityIndex != null) |
| | | { |
| | | DatabaseEntry key = new DatabaseEntry(normalizedBytes); |
| | | try |
| | |
| | | } |
| | | |
| | | // Substring index. |
| | | if (indexConfig.isSubstringIndex()) |
| | | if (substringIndex != null) |
| | | { |
| | | Set<ByteString> keyBytesSet = |
| | | attrIndex.substringKeys(normalizedBytes); |
| | |
| | | } |
| | | |
| | | // Ordering index. |
| | | if (indexConfig.isOrderingIndex()) |
| | | if (orderingIndex != null) |
| | | { |
| | | // Use the ordering matching rule to normalize the value. |
| | | OrderingMatchingRule orderingRule = |
| | |
| | | } |
| | | } |
| | | // Approximate index. |
| | | if (indexConfig.isApproximateIndex()) |
| | | if (approximateIndex != null) |
| | | { |
| | | // Use the approximate matching rule to normalize the value. |
| | | ApproximateMatchingRule approximateRule = |
| | |
| | | * @param dn The DN. |
| | | * @return The parent DN or null if the given DN is a base DN. |
| | | */ |
| | | public DN getParent(DN dn) |
| | | private DN getParent(DN dn) |
| | | { |
| | | if (dn.equals(verifyConfig.getBaseDN())) |
| | | { |
| | |
| | | public class LogPublisherErrorHandler |
| | | { |
| | | private DN publisherConfigDN; |
| | | private boolean writeErrorOccured = false; |
| | | private boolean writeErroroccurred = false; |
| | | |
| | | /** |
| | | * Construct a new log publisher error handler for a log publisher |
| | |
| | | */ |
| | | public void handleWriteError(String record, Throwable ex) |
| | | { |
| | | if(!writeErrorOccured) |
| | | if(!writeErroroccurred) |
| | | { |
| | | int msgID = MSGID_LOGGER_ERROR_WRITING_RECORD; |
| | | String msg = getMessage(msgID, publisherConfigDN.toString(), |
| | | stackTraceToSingleLineString(ex)); |
| | | System.err.println(msg); |
| | | writeErrorOccured = true; |
| | | writeErroroccurred = true; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Handle an exception which occured while trying to open a log |
| | | * Handle an exception which occurred while trying to open a log |
| | | * file. |
| | | * @param file - the file which was being opened. |
| | | * @param ex - the exception occured. |
| | | * @param ex - the exception occurred. |
| | | */ |
| | | public void handleOpenError(File file, Throwable ex) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Handle an exception which occured while trying to close a log |
| | | * Handle an exception which occurred while trying to close a log |
| | | * file. |
| | | * @param ex - the exception occured. |
| | | * @param ex - the exception occurred. |
| | | */ |
| | | public void handleCloseError(Throwable ex) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Handle an exception which occured while trying to flush the |
| | | * Handle an exception which occurred while trying to flush the |
| | | * writer buffer. |
| | | * @param ex - the exception occured. |
| | | * @param ex - the exception occurred. |
| | | */ |
| | | public void handleFlushError(Throwable ex) |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Tries to rotate the log files. If the new log file alreadly exists, it |
| | | * Tries to rotate the log files. If the new log file already exists, it |
| | | * tries to rename the file. On failure, all subsequent log write requests |
| | | * will throw exceptions. |
| | | */ |
| | |
| | | /** |
| | | * Retrieves the last time a log file was rotated in this instance of |
| | | * Directory Server. If a log rotation never |
| | | * occured, this value will be the time the server started. |
| | | * occurred, this value will be the time the server started. |
| | | * |
| | | * @return The last time log rotation occured. |
| | | * @return The last time log rotation occurred. |
| | | */ |
| | | public long getLastRotationTime() |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the total number file rotations occured in this instance of the |
| | | * Retrieves the total number file rotations occurred in this instance of the |
| | | * Directory Server. |
| | | * |
| | | * @return The total number of file rotations. |
| | |
| | | |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("Last rotation occured %ds ago. " + |
| | | TRACER.debugInfo("Last rotation occurred %ds ago. " + |
| | | "Next rotation in %ds", currInterval / 1000, |
| | | (timeInterval - currInterval)/1000); |
| | | } |
| | |
| | | registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_FILES_NOT_RESTORED, |
| | | "An error occrred while attempting to write new " + |
| | | "versions of the server schema files: %s. A problem " + |
| | | "also occured when attempting to restore the original " + |
| | | "also occurred when attempting to restore the original " + |
| | | "schema configuration, so the server may be left in an " + |
| | | "inconsistent state and could require manual cleanup"); |
| | | registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_ATTRIBUTE_TYPE, |
| | |
| | | "Directory Server log retention policy from the " + |
| | | "information in configuration entry %s: %s"); |
| | | registerMessage(MSGID_CONFIG_LOGGING_CANNOT_CREATE_WRITER, |
| | | "An error occured while attempting create a text writer " + |
| | | "An error occurred while attempting create a text writer " + |
| | | "for a Directory Server logger from the information " + |
| | | "in configuration entry %s: %s"); |
| | | |
| | |
| | | "An error occurred while trying to load class %s to use " + |
| | | "as the Directory Server work queue implementation: %s"); |
| | | registerMessage(MSGID_WORKQ_CANNOT_INSTANTIATE, |
| | | "An error occured while trying to create an instance " + |
| | | "An error occurred while trying to create an instance " + |
| | | "of class %s to use as the Directory Server work queue: " + |
| | | "%s"); |
| | | registerMessage(MSGID_BIND_MULTIPLE_USER_LOOKTHROUGH_LIMITS, |
| | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if SASL DIGEST-MD5 |
| | | * authentication fails because an error occured while trying to get the |
| | | * authentication fails because an error occurred while trying to get the |
| | | * clear-text password value(s) from a user's entry. |
| | | */ |
| | | public static final int MSGID_SASLDIGESTMD5_CANNOT_GET_REVERSIBLE_PASSWORDS = |
| | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if SASL CRAM-MD5 |
| | | * authentication fails because an error occured while trying to get the |
| | | * authentication fails because an error occurred while trying to get the |
| | | * clear-text password value(s) from a user's entry. |
| | | */ |
| | | public static final int MSGID_SASLCRAMMD5_CANNOT_GET_REVERSIBLE_PASSWORDS = |
| | |
| | | * The message ID for the message that will be used if an error occurs while |
| | | * attempting to retrieve an entry as a potential member of the static group. |
| | | * This takes three arguments, which are the DN of the target entry, the DN of |
| | | * the static group entry, and a message explaining the problem that occured. |
| | | * the static group entry, and a message explaining the problem that occurred. |
| | | */ |
| | | public static final int MSGID_STATICMEMBERS_CANNOT_GET_ENTRY = |
| | | CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 384; |
| | |
| | | "Unable to retrieve an existing cache entry from the " + |
| | | "file system entry cache"); |
| | | registerMessage(MSGID_FSCACHE_CANNOT_SET_JE_MEMORY_PCT, |
| | | "Internal error occured while trying to set the entry " + |
| | | "Internal error occurred while trying to set the entry " + |
| | | "cache backend internal cache size as percentage. The " + |
| | | "previous or default value will be used instead"); |
| | | registerMessage(MSGID_FSCACHE_CANNOT_SET_JE_MEMORY_SIZE, |
| | | "Internal error occured while trying to set the entry " + |
| | | "Internal error occurred while trying to set the entry " + |
| | | "cache backend internal cache size in bytes. The " + |
| | | "previous or default value will be used instead"); |
| | | registerMessage(MSGID_FSCACHE_OFFLINE_STATE_FAIL, |
| | |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_INFORMATIONAL | 133; |
| | | |
| | | /** |
| | | * The message ID used to indicate that an error occured in an index rebuild |
| | | * The message ID used to indicate that an error occurred in an index rebuild |
| | | * thread and it is terminated. This message takes two arguments: the name |
| | | * of the index rebuild thread that failed and the exception that caused |
| | | * the failure. |
| | |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 134; |
| | | |
| | | /** |
| | | * The message ID used to indicate that an error occured while inserting |
| | | * The message ID used to indicate that an error occurred while inserting |
| | | * an entry into a database/index during the rebuild process. This message |
| | | * takes two arguments: the name of the database/index being inserted into, |
| | | * and the exception that caused the failure. |
| | |
| | | public static final int MSGID_JEB_UNABLE_SET_PERMISSIONS = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_WARNING | 146; |
| | | |
| | | /** |
| | | * The message ID used to indicate a index entry limit has been |
| | | * exceeded and that the index needs to be rebuilt before the |
| | | * new entry limit is used. |
| | | */ |
| | | public static final int MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_NOTICE | 148; |
| | | |
| | | /** |
| | | * The message ID used to indicate the newly created index needs |
| | | * to be rebuilt before it will be used. |
| | | */ |
| | | public static final int MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_NOTICE | 150; |
| | | |
| | | /** |
| | | * The message ID used to indicate an index is corrupt and needs |
| | | * to be rebuilt before it will be used again. |
| | | */ |
| | | public static final int MSGID_JEB_INDEX_CORRUPT_REQUIRES_REBUILD = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 151; |
| | | |
| | | /** |
| | | * The message ID used to indicate an import process can not start |
| | | * while the backend is online. |
| | | */ |
| | | public static final int MSGID_JEB_IMPORT_BACKEND_ONLINE = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 152; |
| | | |
| | | /** |
| | | * The message ID used to indicate an error has occurred during the import |
| | | * process. |
| | | */ |
| | | public static final int MSGID_JEB_IMPORT_THREAD_EXCEPTION = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 153; |
| | | |
| | | /** |
| | | * The message ID used to indicate an error where there are no more worker |
| | | * threads to process the imported entries. |
| | | */ |
| | | public static final int MSGID_JEB_IMPORT_NO_WORKER_THREADS = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 154; |
| | | |
| | | /** |
| | | * The message ID used to indicate an error where the temp import directory |
| | | * can not be created. |
| | | */ |
| | | public static final int MSGID_JEB_IMPORT_CREATE_TMPDIR_ERROR = |
| | | CATEGORY_MASK_JEB | SEVERITY_MASK_SEVERE_ERROR | 155; |
| | | |
| | | |
| | | /** |
| | | * Associates a set of generic messages with the message IDs defined in this |
| | |
| | | "This search operation has checked the maximum of %d " + |
| | | "entries for matches"); |
| | | registerMessage(MSGID_JEB_SET_PERMISSIONS_FAILED, |
| | | "An error occured while setting file permissions for " + |
| | | "An error occurred while setting file permissions for " + |
| | | "the backend database directory %s: %s"); |
| | | registerMessage(MSGID_JEB_GET_ENTRY_COUNT_FAILED, |
| | | "Unable to determine the total number of entries in the " + |
| | |
| | | "Rebuild complete. Processed %d records in %d seconds " + |
| | | "(average rate %.1f/sec)"); |
| | | registerMessage(MSGID_JEB_REBUILD_INDEX_FAILED, |
| | | "An error occured while rebuilding index %s: %s"); |
| | | "An error occurred while rebuilding index %s: %s"); |
| | | registerMessage(MSGID_JEB_REBUILD_INSERT_ENTRY_FAILED, |
| | | "An error occured while inserting entry into the %s " + |
| | | "An error occurred while inserting entry into the %s " + |
| | | "database/index: %s"); |
| | | registerMessage(MSGID_JEB_REBUILD_INDEX_CONFLICT, |
| | | "Another rebuild of index %s is already in progress"); |
| | |
| | | registerMessage(MSGID_JEB_UNABLE_SET_PERMISSIONS, |
| | | "This platform does not support setting file " + |
| | | "permissions %s to the database directory %s"); |
| | | registerMessage(MSGID_JEB_CONFIG_INDEX_ENTRY_LIMIT_REQUIRES_REBUILD, |
| | | "Some index keys have already exceeded the previous " + |
| | | "index entry limit in index %s. This index must be " + |
| | | "rebuilt before it can use the new limit"); |
| | | registerMessage(MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD, |
| | | "Index %s is currently operating in a degraded read-only " + |
| | | "state and must be rebuilt before it can used"); |
| | | registerMessage(MSGID_JEB_INDEX_CORRUPT_REQUIRES_REBUILD, |
| | | "An error occurred while reading from index %s. The " + |
| | | "index seems to be corrupt and is now operating in " + |
| | | "a degraded read-only state. The index must be rebuilt " + |
| | | "before it can return to normal operation"); |
| | | registerMessage(MSGID_JEB_IMPORT_BACKEND_ONLINE, |
| | | "The backend must be disabled before the import process" + |
| | | "can start"); |
| | | registerMessage(MSGID_JEB_IMPORT_THREAD_EXCEPTION, |
| | | "An error occurred in import thread %s: %s. The thread " + |
| | | "can not continue"); |
| | | registerMessage(MSGID_JEB_IMPORT_NO_WORKER_THREADS, |
| | | "There are no more import worker threads to process the " + |
| | | "imported entries"); |
| | | registerMessage(MSGID_JEB_IMPORT_CREATE_TMPDIR_ERROR, |
| | | "Unable to create the temporary directory %s"); |
| | | } |
| | | } |
| | |
| | | public class LoggerMessages |
| | | { |
| | | /** |
| | | * The message ID for the message that will be used if an error occured |
| | | * The message ID for the message that will be used if an error occurred |
| | | * while writing a log record. This takes a two arguments, which |
| | | * are the logger that encountered the error and a string |
| | | * representation of the exception that was caught. |
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an error occured |
| | | * The message ID for the message that will be used if an error occurred |
| | | * while opening a log file. This takes a two arguments, which |
| | | * are the logger that encountered the error and a string |
| | | * representation of the exception that was caught. |
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an error occured |
| | | * The message ID for the message that will be used if an error occurred |
| | | * while closing a log file. This takes a two arguments, which |
| | | * are the logger that encountered the error and a string |
| | | * representation of the exception that was caught. |
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an error occured |
| | | * The message ID for the message that will be used if an error occurred |
| | | * while flushing the writer buffer. This takes a two arguments, which |
| | | * are the logger that encountered the error and a string |
| | | * representation of the exception that was caught. |
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an error occured |
| | | * The message ID for the message that will be used if an error occurred |
| | | * while setting file permissions on a log file. This takes the name of the |
| | | * file as the argument. |
| | | */ |
| | |
| | | public static void registerMessages() |
| | | { |
| | | registerMessage(MSGID_LOGGER_ERROR_WRITING_RECORD, |
| | | "Error occured while writing log record for logger " + |
| | | "Error occurred while writing log record for logger " + |
| | | "%s: %s. Any further write errors will be ignored"); |
| | | registerMessage(MSGID_LOGGER_ERROR_OPENING_FILE, |
| | | "Error occured while opening log file %s for logger %s: " + |
| | | "Error occurred while opening log file %s for logger %s: " + |
| | | "%s"); |
| | | registerMessage(MSGID_LOGGER_ERROR_CLOSING_FILE, |
| | | "Error occured while closing log file for logger %s: %s"); |
| | | "Error occurred while closing log file for logger %s: %s"); |
| | | registerMessage(MSGID_LOGGER_ERROR_FLUSHING_BUFFER, |
| | | "Error occured while flushing writer buffer for " + |
| | | "Error occurred while flushing writer buffer for " + |
| | | "logger %s: %s"); |
| | | registerMessage(MSGID_ERROR_LOGGER_INVALID_SEVERITY, |
| | | "Invalid error log severity \"%s\""); |
| | |
| | | registerMessage(MSGID_ERROR_LOGGER_INVALID_OVERRIDE_SEVERITY, |
| | | "Invalid override of severity level \"%s\""); |
| | | registerMessage(MSGID_LOGGER_SET_PERMISSION_FAILED, |
| | | "Error occured while setting file permissions for the " + |
| | | "Error occurred while setting file permissions for the " + |
| | | "log file %s: %s"); |
| | | registerMessage(MSGID_LOGGER_UNABLE_SET_PERMISSIONS, |
| | | "This platform does not support setting file " + |
| | |
| | | import org.opends.server.types.AttributeType; |
| | | import org.opends.server.types.AttributeValue; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.backends.jeb.RootContainer; |
| | | |
| | | import com.sleepycat.je.DatabaseException; |
| | | import com.sleepycat.je.Environment; |
| | | import com.sleepycat.je.EnvironmentStats; |
| | | import com.sleepycat.je.JEVersion; |
| | | import com.sleepycat.je.LockStats; |
| | |
| | | private String name; |
| | | |
| | | /** |
| | | * The JE environment handle to be monitored. |
| | | * The root container to be monitored. |
| | | */ |
| | | private Environment environment; |
| | | private RootContainer rootContainer; |
| | | |
| | | /** |
| | | * Creates a new database environment monitor. |
| | | * @param name The monitor instance name. |
| | | * @param environment A JE environment handle for the database to be |
| | | * @param rootContainer A root container handle for the database to be |
| | | * monitored. |
| | | */ |
| | | public DatabaseEnvironmentMonitor(String name, Environment environment) |
| | | public DatabaseEnvironmentMonitor(String name, RootContainer rootContainer) |
| | | { |
| | | super(name + " Monitor Provider"); |
| | | |
| | | |
| | | this.name = name; |
| | | this.environment = environment; |
| | | this.rootContainer = rootContainer; |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | try |
| | | { |
| | | environmentStats = environment.getStats(statsConfig); |
| | | lockStats = environment.getLockStats(statsConfig); |
| | | transactionStats = environment.getTransactionStats(statsConfig); |
| | | environmentStats = rootContainer.getEnvironmentStats(statsConfig); |
| | | lockStats = rootContainer.getEnvironmentLockStats(statsConfig); |
| | | transactionStats = |
| | | rootContainer.getEnvironmentTransactionStats(statsConfig); |
| | | } catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | |
| | | |
| | | /** |
| | | * Export the entries. |
| | | * @throws DirectoryException when an error occured |
| | | * @throws DirectoryException when an error occurred |
| | | */ |
| | | protected void exportBackend() throws DirectoryException |
| | | { |
| | |
| | | * Sends lDIFEntry entry lines to the export target currently set. |
| | | * |
| | | * @param lDIFEntry The lines for the LDIF entry. |
| | | * @throws IOException when an error occured. |
| | | * @throws IOException when an error occurred. |
| | | */ |
| | | public void sendEntryLines(String lDIFEntry) throws IOException |
| | | { |
| | |
| | | * |
| | | * @param baseDN The baseDN of the domain to retrieve |
| | | * @return The domain retrieved |
| | | * @throws DirectoryException When an error occured or no domain |
| | | * @throws DirectoryException When an error occurred or no domain |
| | | * match the provided baseDN. |
| | | */ |
| | | public static ReplicationDomain retrievesReplicationDomain(DN baseDN) |
| | |
| | | * @param path Path where the backing files must be created. |
| | | * @param replicationServer the ReplicationServer that creates this |
| | | * ReplicationDbEnv. |
| | | * @throws DatabaseException If a DatabaseException occured that prevented |
| | | * @throws DatabaseException If a DatabaseException occurred that prevented |
| | | * the initialization to happen. |
| | | * @throws ReplicationDBException If a replicationServer internal error caused |
| | | * a failure of the replicationServer processing. |
| | |
| | | ds-cfg-index-type: ordering |
| | | ds-cfg-index-type: approximate |
| | | |
| | | dn: ds-cfg-backend-id=importRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | | objectClass: ds-cfg-backend |
| | | objectClass: ds-cfg-je-backend |
| | | ds-cfg-backend-enabled: true |
| | | ds-cfg-backend-class: org.opends.server.backends.jeb.BackendImpl |
| | | ds-cfg-backend-id: importRoot |
| | | ds-cfg-backend-writability-mode: enabled |
| | | ds-cfg-backend-base-dn: dc=importtest, dc=com |
| | | ds-cfg-backend-base-dn: dc=importtest1, dc=com |
| | | ds-cfg-backend-directory: db_import_test |
| | | ds-cfg-backend-mode: 700 |
| | | ds-cfg-backend-index-entry-limit: 10 |
| | | ds-cfg-backend-subtree-delete-size-limit: 100000 |
| | | ds-cfg-backend-preload-time-limit: 0 seconds |
| | | ds-cfg-backend-import-temp-directory: importTmp |
| | | ds-cfg-backend-import-buffer-size: 256 megabytes |
| | | ds-cfg-backend-import-queue-size: 100 |
| | | ds-cfg-backend-import-pass-size: 0 |
| | | ds-cfg-backend-import-thread-count: 8 |
| | | ds-cfg-backend-entries-compressed: false |
| | | ds-cfg-backend-deadlock-retry-limit: 10 |
| | | |
| | | dn: ds-cfg-backend-id=verifyRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | |
| | | ds-cfg-index-type: presence |
| | | ds-cfg-index-type: equality |
| | | ds-cfg-index-type: substring |
| | | ds-cfg-index-type: ordering |
| | | |
| | | dn: ds-cfg-index-attribute=mail,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config |
| | | changetype: add |
| | |
| | | ds-cfg-index-type: equality |
| | | ds-cfg-index-type: substring |
| | | ds-cfg-index-type: ordering |
| | | ds-cfg-index-type: approximate |
| | | ds-cfg-index-entry-limit: 1 |
| | | |
| | | dn: ds-cfg-index-attribute=member,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config |
| | | changetype: add |
| | |
| | | String[] subDirectories = { "bak", "bin", "changelogDb", "classes", |
| | | "config", "db", "db_verify", "ldif", "lib", |
| | | "locks", "logs", "db_rebuild", "db_unindexed", |
| | | "db_index_test" }; |
| | | "db_index_test", "db_import_test"}; |
| | | for (String s : subDirectories) |
| | | { |
| | | new File(testRoot, s).mkdir(); |
| | |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | import org.opends.server.admin.std.server.JEIndexCfg; |
| | | import org.opends.server.admin.std.meta.JEBackendCfgDefn; |
| | | import org.opends.server.admin.std.meta.JEIndexCfgDefn; |
| | | import org.opends.server.admin.server.AdminTestCaseUtils; |
| | | import org.opends.server.core.DeleteOperation; |
| | | import org.opends.server.core.ModifyOperation; |
| | |
| | | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("dc=test1,dc=com")); |
| | | assertFalse(ec.entryExists(DN.decode("dc=test1,dc=com"))); |
| | | assertFalse(ec.entryExists(DN.decode("uid=user.362,dc=test1,dc=com"))); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | assertFalse(ec.entryExists(DN.decode("dc=test1,dc=com"))); |
| | | assertFalse(ec.entryExists(DN.decode("uid=user.362,dc=test1,dc=com"))); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testAdd", "testSearchIndex", |
| | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("ou=People,dc=test,dc=com")); |
| | | |
| | | entry = ec.getEntry(DN.decode("uid=user.539,ou=People,dc=test,dc=com")); |
| | | entryID = ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com")); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | entry = ec.getEntry(DN.decode("uid=user.539,ou=People,dc=test,dc=com")); |
| | | entryID = ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com")); |
| | | |
| | | DeleteOperation delete = new DeleteOperation(conn, |
| | | conn.nextOperationID(), |
| | | conn.nextMessageID(), |
| | | noControls, |
| | | DeleteOperation delete = new DeleteOperation(conn, |
| | | conn.nextOperationID(), |
| | | conn.nextMessageID(), |
| | | noControls, |
| | | |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com")); |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com")); |
| | | |
| | | |
| | | backend.deleteEntry(DN.decode("uid=user.539,ou=People,dc=test,dc=com"), |
| | | delete); |
| | | backend.deleteEntry(DN.decode("uid=user.539,ou=People,dc=test,dc=com"), |
| | | delete); |
| | | |
| | | |
| | | assertFalse(ec.entryExists(DN.decode("uid=user.539,ou=People,dc=test,dc=com"))); |
| | | assertNull(ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com"))); |
| | | assertFalse(ec.getDN2URI().delete(null, |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com"))); |
| | | assertFalse(ec.entryExists(DN.decode("uid=user.539,ou=People,dc=test,dc=com"))); |
| | | assertNull(ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com"))); |
| | | assertFalse(ec.getDN2URI().delete(null, |
| | | DN.decode("uid=user.539,ou=People,dc=test,dc=com"))); |
| | | |
| | | attribute = entries.get(0).getAttribute("cn").get(0).getAttributeType(); |
| | | index = ec.getAttributeIndex(attribute); |
| | | attribute = entries.get(0).getAttribute("cn").get(0).getAttributeType(); |
| | | index = ec.getAttributeIndex(attribute); |
| | | |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | presenceIndexer = new PresenceIndexer(index.indexConfig); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | presenceIndexer = new PresenceIndexer(index.getAttributeType()); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.getAttributeType(), |
| | | index.getConfiguration().getIndexSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testSearchNotIndexed", "testAdd", "testSearchIndex", "testSearchScope"}) |
| | |
| | | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("dc=test,dc=com")); |
| | | entry = ec.getEntry(DN.decode("uid=user.0,ou=People,dc=test,dc=com")); |
| | | oldEntry = entries.get(0); |
| | | entryID = ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.0,ou=People,dc=test,dc=com")); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | entry = ec.getEntry(DN.decode("uid=user.0,ou=People,dc=test,dc=com")); |
| | | oldEntry = entries.get(0); |
| | | entryID = ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.0,ou=People,dc=test,dc=com")); |
| | | |
| | | assertNotNull(entry); |
| | | LinkedHashSet<AttributeValue> values = |
| | | entry.getAttribute("cn").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "Testing Test"); |
| | | assertNotNull(entry); |
| | | LinkedHashSet<AttributeValue> values = |
| | | entry.getAttribute("cn").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "Testing Test"); |
| | | } |
| | | values = entry.getAttribute("sn").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "Test"); |
| | | } |
| | | values = entry.getAttribute("givenname").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "Testing"); |
| | | } |
| | | values = entry.getAttribute("employeenumber").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "777"); |
| | | } |
| | | |
| | | attribute = entry.getAttribute("cn").get(0).getAttributeType(); |
| | | index = ec.getAttributeIndex(attribute); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.getAttributeType(), |
| | | index.getConfiguration().getIndexSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | } |
| | | values = entry.getAttribute("sn").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "Test"); |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | values = entry.getAttribute("givenname").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "Testing"); |
| | | } |
| | | values = entry.getAttribute("employeenumber").get(0).getValues(); |
| | | for (AttributeValue value : values) { |
| | | assertEquals(value.getStringValue(), "777"); |
| | | } |
| | | |
| | | attribute = entry.getAttribute("cn").get(0).getAttributeType(); |
| | | index = ec.getAttributeIndex(attribute); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | } |
| | | |
| | |
| | | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("dc=test,dc=com")); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | ArrayList<Modification> modifications = new ArrayList<Modification>(); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("title", "debugger"))); |
| | | modifications.add(new Modification(ModificationType.DELETE, new |
| | | Attribute("cn", "Aaren Atp"))); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("cn", "Aaren Rigor"))); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("cn", "Aarenister Rigor"))); |
| | | modifications.add(new Modification(ModificationType.REPLACE, new |
| | | Attribute("employeenumber", "222"))); |
| | | |
| | | ArrayList<Modification> modifications = new ArrayList<Modification>(); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("title", "debugger"))); |
| | | modifications.add(new Modification(ModificationType.DELETE, new |
| | | Attribute("cn", "Aaren Atp"))); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("cn", "Aaren Rigor"))); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("cn", "Aarenister Rigor"))); |
| | | modifications.add(new Modification(ModificationType.REPLACE, new |
| | | Attribute("employeenumber", "222"))); |
| | | newEntry = entries.get(1); |
| | | newEntry.applyModifications(modifications); |
| | | entry = ec.getEntry(DN.decode("uid=user.1,ou=People,dc=test,dc=com")); |
| | | entryID = ec.getDN2ID().get(null, DN.decode("uid=user.1,ou=People,dc=test,dc=com")); |
| | | |
| | | newEntry = entries.get(1); |
| | | newEntry.applyModifications(modifications); |
| | | entry = ec.getEntry(DN.decode("uid=user.1,ou=People,dc=test,dc=com")); |
| | | entryID = ec.getDN2ID().get(null, DN.decode("uid=user.1,ou=People,dc=test,dc=com")); |
| | | |
| | | assertNotNull(entryID); |
| | | assertNotNull(entryID); |
| | | |
| | | |
| | | attribute = newEntry.getAttribute("title").get(0).getAttributeType(); |
| | | index = ec.getAttributeIndex(attribute); |
| | | attribute = newEntry.getAttribute("title").get(0).getAttributeType(); |
| | | index = ec.getAttributeIndex(attribute); |
| | | |
| | | //This current entry in the DB shouldn't be in the presence index. |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData())); |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | //This current entry in the DB shouldn't be in the presence index. |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData())); |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | ArrayList<Control> noControls = new ArrayList<Control>(0); |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | |
| | | ModifyOperation modifyOp = new ModifyOperation(conn, |
| | | conn.nextOperationID(), |
| | | conn.nextMessageID(), |
| | | noControls, |
| | | DN.decode("uid=user.1,ou=People,dc=test,dc=com"), |
| | | modifications); |
| | | |
| | | |
| | | backend.replaceEntry(newEntry, modifyOp); |
| | | |
| | | entry = ec.getEntry(DN.decode("uid=user.1,ou=People,dc=test,dc=com")); |
| | | |
| | | assertTrue(entry.getAttribute("title").contains(new |
| | | Attribute("title", "debugger"))); |
| | | |
| | | assertTrue(entry.getAttribute("cn").get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("cn").get(0).getAttributeType(), |
| | | "Aaren Rigor"))); |
| | | assertTrue(entry.getAttribute("cn").get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("cn").get(0).getAttributeType(), |
| | | "Aarenister Rigor"))); |
| | | assertFalse(entry.getAttribute("cn").get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("cn").get(0).getAttributeType(), |
| | | "Aaren Atp"))); |
| | | |
| | | assertTrue(entry.getAttribute("employeenumber").contains(new |
| | | Attribute("employeenumber", "222"))); |
| | | assertFalse(entry.getAttribute("employeenumber").contains(new |
| | | Attribute("employeenumber", "1"))); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | presenceIndexer = new PresenceIndexer(index.getAttributeType()); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.getAttributeType(), |
| | | index.getConfiguration().getIndexSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | ArrayList<Control> noControls = new ArrayList<Control>(0); |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | |
| | | ModifyOperation modifyOp = new ModifyOperation(conn, |
| | | conn.nextOperationID(), |
| | | conn.nextMessageID(), |
| | | noControls, |
| | | DN.decode("uid=user.1,ou=People,dc=test,dc=com"), |
| | | modifications); |
| | | |
| | | |
| | | backend.replaceEntry(newEntry, modifyOp); |
| | | |
| | | entry = ec.getEntry(DN.decode("uid=user.1,ou=People,dc=test,dc=com")); |
| | | |
| | | assertTrue(entry.getAttribute("title").contains(new |
| | | Attribute("title", "debugger"))); |
| | | |
| | | assertTrue(entry.getAttribute("cn").get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("cn").get(0).getAttributeType(), |
| | | "Aaren Rigor"))); |
| | | assertTrue(entry.getAttribute("cn").get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("cn").get(0).getAttributeType(), |
| | | "Aarenister Rigor"))); |
| | | assertFalse(entry.getAttribute("cn").get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("cn").get(0).getAttributeType(), |
| | | "Aaren Atp"))); |
| | | |
| | | assertTrue(entry.getAttribute("employeenumber").contains(new |
| | | Attribute("employeenumber", "222"))); |
| | | assertFalse(entry.getAttribute("employeenumber").contains(new |
| | | Attribute("employeenumber", "1"))); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | presenceIndexer = new PresenceIndexer(index.indexConfig); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | } |
| | | |
| | |
| | | public void testModifyDN() throws Exception { |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("dc=test,dc=com")); |
| | | Entry entry = |
| | | ec.getEntry(DN.decode("uid=user.2,ou=People,dc=test,dc=com")); |
| | | entry.setDN(DN.decode("cn=Abbey Abbie,ou=People,dc=test,dc=com")); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | Entry entry = |
| | | ec.getEntry(DN.decode("uid=user.2,ou=People,dc=test,dc=com")); |
| | | entry.setDN(DN.decode("cn=Abbey Abbie,ou=People,dc=test,dc=com")); |
| | | |
| | | |
| | | backend.renameEntry(DN.decode("uid=user.2,ou=People,dc=test,dc=com"), |
| | | entry, null); |
| | | backend.renameEntry(DN.decode("uid=user.2,ou=People,dc=test,dc=com"), |
| | | entry, null); |
| | | |
| | | assertNotNull(backend.getEntry(DN.decode("cn=Abbey Abbie,ou=People,dc=test,dc=com"))); |
| | | assertNotNull(ec.getDN2ID().get(null, DN.decode("cn=Abbey Abbie,ou=People,dc=test,dc=com"))); |
| | | assertNotNull(backend.getEntry(DN.decode("cn=Abbey Abbie,ou=People,dc=test,dc=com"))); |
| | | assertNotNull(ec.getDN2ID().get(null, DN.decode("cn=Abbey Abbie,ou=People,dc=test,dc=com"))); |
| | | |
| | | |
| | | assertNull(backend.getEntry(DN.decode("uid=user.2,ou=People,dc=test,dc=com"))); |
| | | assertNull(ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.2,ou=People,dc=test,dc=com"))); |
| | | assertNull(backend.getEntry(DN.decode("uid=user.2,ou=People,dc=test,dc=com"))); |
| | | assertNull(ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.2,ou=People,dc=test,dc=com"))); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testSearchNotIndexed", "testAdd", "testSearchIndex", |
| | |
| | | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("dc=test,dc=com")); |
| | | EntryID newSuperiorID = ec.getDN2ID().get(null, DN.decode("ou=JEB Testers,dc=test,dc=com")); |
| | | EntryID oldID = ec.getDN2ID().get(null, |
| | | DN.decode("ou=People,dc=test,dc=com")); |
| | | assertTrue(newSuperiorID.compareTo(oldID) > 0); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | EntryID newSuperiorID = ec.getDN2ID().get(null, DN.decode("ou=JEB Testers,dc=test,dc=com")); |
| | | EntryID oldID = ec.getDN2ID().get(null, |
| | | DN.decode("ou=People,dc=test,dc=com")); |
| | | assertTrue(newSuperiorID.compareTo(oldID) > 0); |
| | | |
| | | ArrayList<Control> noControls = new ArrayList<Control>(0); |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | ArrayList<Control> noControls = new ArrayList<Control>(0); |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | |
| | | ModifyDNOperation modifyDN = new ModifyDNOperation(conn, |
| | | conn.nextOperationID(), |
| | | conn.nextMessageID(), |
| | | noControls, |
| | | DN.decode("ou=People,dc=test,dc=com"), |
| | | RDN.decode("ou=Good People"), |
| | | false, |
| | | DN.decode("ou=JEB Testers,dc=test,dc=com")); |
| | | ModifyDNOperation modifyDN = new ModifyDNOperation(conn, |
| | | conn.nextOperationID(), |
| | | conn.nextMessageID(), |
| | | noControls, |
| | | DN.decode("ou=People,dc=test,dc=com"), |
| | | RDN.decode("ou=Good People"), |
| | | false, |
| | | DN.decode("ou=JEB Testers,dc=test,dc=com")); |
| | | |
| | | modifyDN.run(); |
| | | modifyDN.run(); |
| | | |
| | | assertNotNull(backend.getEntry(DN.decode("ou=Good People,ou=JEB Testers,dc=test,dc=com"))); |
| | | EntryID newID = ec.getDN2ID().get(null, DN.decode("ou=Good People,ou=JEB Testers,dc=test,dc=com")); |
| | | assertNotNull(newID); |
| | | assertTrue(newID.compareTo(newSuperiorID) > 0); |
| | | assertNotNull(backend.getEntry(DN.decode("uid=user.0,ou=Good People,ou=JEB Testers,dc=test,dc=com"))); |
| | | EntryID newSubordinateID = ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.0,ou=Good People,ou=JEB Testers,dc=test,dc=com")); |
| | | assertTrue(newSubordinateID.compareTo(newID) > 0); |
| | | assertNotNull(backend.getEntry(DN.decode("ou=Good People,ou=JEB Testers,dc=test,dc=com"))); |
| | | EntryID newID = ec.getDN2ID().get(null, DN.decode("ou=Good People,ou=JEB Testers,dc=test,dc=com")); |
| | | assertNotNull(newID); |
| | | assertTrue(newID.compareTo(newSuperiorID) > 0); |
| | | assertNotNull(backend.getEntry(DN.decode("uid=user.0,ou=Good People,ou=JEB Testers,dc=test,dc=com"))); |
| | | EntryID newSubordinateID = ec.getDN2ID().get(null, |
| | | DN.decode("uid=user.0,ou=Good People,ou=JEB Testers,dc=test,dc=com")); |
| | | assertTrue(newSubordinateID.compareTo(newID) > 0); |
| | | |
| | | assertNull(backend.getEntry(DN.decode("ou=People,dc=test,dc=com"))); |
| | | assertNull(ec.getDN2ID().get(null, |
| | | DN.decode("ou=People,dc=test,dc=com"))); |
| | | |
| | | assertNull(backend.getEntry(DN.decode("ou=People,dc=test,dc=com"))); |
| | | assertNull(ec.getDN2ID().get(null, |
| | | DN.decode("ou=People,dc=test,dc=com"))); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testModifyDN", |
| | |
| | | "testModifyEntry", "testModifyDN", "testDeleteSubtree", |
| | | "testDeleteEntry", "testAddNoParent", "testAdd", |
| | | "testSearchNotIndexed", |
| | | "testModifyDNNewSuperior"}) |
| | | "testModifyDNNewSuperior", "testApplyIndexConfig"}) |
| | | public void testApplyConfig() throws Exception { |
| | | Entry configEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-backend-id=indexRoot,cn=Backends,cn=config", |
| | |
| | | assertNotNull(rootContainer.getEntryContainer(DN.decode("dc=newsuffix,dc=com"))); |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testModifyDN", |
| | | "testSearchScope", "testSearchIndex", "testReplaceEntry", |
| | | "testModifyEntry", "testModifyDN", "testDeleteSubtree", |
| | | "testDeleteEntry", "testAddNoParent", "testAdd", |
| | | "testSearchNotIndexed", |
| | | "testModifyDNNewSuperior"}) |
| | | public void testApplyIndexConfig() throws Exception { |
| | | Entry configEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-index-attribute=givenName,cn=Index," + |
| | | "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config", |
| | | "objectClass: top", |
| | | "objectClass: ds-cfg-je-index", |
| | | "ds-cfg-index-attribute: givenName", |
| | | "ds-cfg-index-type: approximate"); |
| | | |
| | | JEIndexCfg cfg = AdminTestCaseUtils.getConfiguration( |
| | | JEIndexCfgDefn.getInstance(), configEntry); |
| | | |
| | | RootContainer rootContainer = backend.getRootContainer(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(DN.decode("dc=test,dc=com")); |
| | | |
| | | AttributeIndex index = |
| | | ec.getAttributeIndex(DirectoryServer.getAttributeType("givenname")); |
| | | ConfigChangeResult ccr = index.applyConfigurationChange(cfg); |
| | | assertTrue(ccr.getResultCode().equals(ResultCode.SUCCESS)); |
| | | assertTrue(ccr.adminActionRequired()); |
| | | assertFalse(ccr.getMessages().isEmpty()); |
| | | assertNull(index.equalityIndex); |
| | | assertNull(index.presenceIndex); |
| | | assertNull(index.substringIndex); |
| | | assertNull(index.orderingIndex); |
| | | assertNotNull(index.approximateIndex); |
| | | ArrayList<DatabaseContainer> databases = new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databases); |
| | | boolean eqfound = false; |
| | | boolean prfound = false; |
| | | boolean subfound = false; |
| | | boolean orfound = false; |
| | | boolean apfound = false; |
| | | for(DatabaseContainer dc : databases) |
| | | { |
| | | if(dc.getName().toLowerCase().contains("givenname.approximate")) |
| | | { |
| | | apfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.presence")) |
| | | { |
| | | prfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.substring")) |
| | | { |
| | | subfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.ordering")) |
| | | { |
| | | orfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.equality")) |
| | | { |
| | | eqfound = true; |
| | | } |
| | | } |
| | | assertFalse(eqfound); |
| | | assertFalse(prfound); |
| | | assertFalse(subfound); |
| | | assertFalse(orfound); |
| | | assertTrue(apfound); |
| | | |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | LinkedHashSet<String> attribs = new LinkedHashSet<String>(); |
| | | attribs.add(ATTR_DEBUG_SEARCH_INDEX); |
| | | |
| | | InternalSearchOperation search = |
| | | conn.processSearch(DN.decode("dc=test,dc=com"), |
| | | |
| | | SearchScope.SUBORDINATE_SUBTREE, |
| | | |
| | | DereferencePolicy.NEVER_DEREF_ALIASES, |
| | | 0, |
| | | 0, |
| | | false, |
| | | |
| | | LDAPFilter.decode("(givenName~=Aaccf)"). |
| | | toSearchFilter(), |
| | | attribs); |
| | | |
| | | LinkedList<SearchResultEntry> result = search.getSearchEntries(); |
| | | |
| | | //No indexes should be used. |
| | | String debugString = |
| | | result.get(0).getAttribute("debugsearchindex").get(0).getValues().toString(); |
| | | assertTrue(debugString.contains("NOT-INDEXED")); |
| | | |
| | | configEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-index-attribute=givenName,cn=Index," + |
| | | "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config", |
| | | "objectClass: top", |
| | | "objectClass: ds-cfg-je-index", |
| | | "ds-cfg-index-attribute: givenName", |
| | | "ds-cfg-index-type: equality", |
| | | "ds-cfg-index-type: presence", |
| | | "ds-cfg-index-type: ordering", |
| | | "ds-cfg-index-type: substring"); |
| | | |
| | | cfg = AdminTestCaseUtils.getConfiguration( |
| | | JEIndexCfgDefn.getInstance(), configEntry); |
| | | |
| | | ccr = index.applyConfigurationChange(cfg); |
| | | assertTrue(ccr.getResultCode().equals(ResultCode.SUCCESS)); |
| | | assertTrue(ccr.adminActionRequired()); |
| | | assertFalse(ccr.getMessages().isEmpty()); |
| | | assertNotNull(index.equalityIndex); |
| | | assertNotNull(index.presenceIndex); |
| | | assertNotNull(index.substringIndex); |
| | | assertNotNull(index.orderingIndex); |
| | | assertNull(index.approximateIndex); |
| | | databases = new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databases); |
| | | eqfound = false; |
| | | prfound = false; |
| | | subfound = false; |
| | | orfound = false; |
| | | apfound = false; |
| | | for(DatabaseContainer dc : databases) |
| | | { |
| | | if(dc.getName().toLowerCase().contains("givenname.approximate")) |
| | | { |
| | | apfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.presence")) |
| | | { |
| | | prfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.substring")) |
| | | { |
| | | subfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.ordering")) |
| | | { |
| | | orfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.equality")) |
| | | { |
| | | eqfound = true; |
| | | } |
| | | } |
| | | assertTrue(eqfound); |
| | | assertTrue(prfound); |
| | | assertTrue(subfound); |
| | | assertTrue(orfound); |
| | | assertFalse(apfound); |
| | | |
| | | // Delete the entries attribute index. |
| | | ccr = ec.applyConfigurationDelete(cfg); |
| | | assertTrue(ccr.getResultCode().equals(ResultCode.SUCCESS)); |
| | | assertNull(ec.getAttributeIndex( |
| | | DirectoryServer.getAttributeType("givenname"))); |
| | | databases = new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databases); |
| | | for(DatabaseContainer dc : databases) |
| | | { |
| | | assertFalse(dc.getName().toLowerCase().contains("givenname")); |
| | | } |
| | | |
| | | // Add it back |
| | | ccr = ec.applyConfigurationAdd(cfg); |
| | | assertTrue(ccr.getResultCode().equals(ResultCode.SUCCESS)); |
| | | assertTrue(ccr.adminActionRequired()); |
| | | assertFalse(ccr.getMessages().isEmpty()); |
| | | assertNotNull(ec.getAttributeIndex( |
| | | DirectoryServer.getAttributeType("givenname"))); |
| | | databases = new ArrayList<DatabaseContainer>(); |
| | | ec.listDatabases(databases); |
| | | eqfound = false; |
| | | prfound = false; |
| | | subfound = false; |
| | | orfound = false; |
| | | apfound = false; |
| | | for(DatabaseContainer dc : databases) |
| | | { |
| | | if(dc.getName().toLowerCase().contains("givenname.approximate")) |
| | | { |
| | | apfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.presence")) |
| | | { |
| | | prfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.substring")) |
| | | { |
| | | subfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.ordering")) |
| | | { |
| | | orfound = true; |
| | | } |
| | | if(dc.getName().toLowerCase().contains("givenname.equality")) |
| | | { |
| | | eqfound = true; |
| | | } |
| | | } |
| | | assertTrue(eqfound); |
| | | assertTrue(prfound); |
| | | assertTrue(subfound); |
| | | assertTrue(orfound); |
| | | assertFalse(apfound); |
| | | |
| | | // Make sure changing the index entry limit on an index where the limit |
| | | // is already exceeded causes warnings. |
| | | configEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-index-attribute=mail,cn=Index," + |
| | | "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config", |
| | | "objectClass: top", |
| | | "objectClass: ds-cfg-je-index", |
| | | "ds-cfg-index-attribute: mail", |
| | | "ds-cfg-index-type: presence", |
| | | "ds-cfg-index-type: equality", |
| | | "ds-cfg-index-type: ordering", |
| | | "ds-cfg-index-type: substring", |
| | | "ds-cfg-index-type: approximate", |
| | | "ds-cfg-index-entry-limit: 30"); |
| | | |
| | | cfg = AdminTestCaseUtils.getConfiguration( |
| | | JEIndexCfgDefn.getInstance(), configEntry); |
| | | |
| | | // Make sure removing a index entry limit for an index makes it use the |
| | | // backend wide setting. |
| | | index = |
| | | ec.getAttributeIndex(DirectoryServer.getAttributeType("mail")); |
| | | ccr = index.applyConfigurationChange(cfg); |
| | | assertTrue(ccr.getResultCode().equals(ResultCode.SUCCESS)); |
| | | assertTrue(ccr.adminActionRequired()); |
| | | assertFalse(ccr.getMessages().isEmpty()); |
| | | |
| | | configEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-index-attribute=mail,cn=Index," + |
| | | "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config", |
| | | "objectClass: top", |
| | | "objectClass: ds-cfg-je-index", |
| | | "ds-cfg-index-attribute: mail", |
| | | "ds-cfg-index-type: presence", |
| | | "ds-cfg-index-type: equality", |
| | | "ds-cfg-index-type: ordering", |
| | | "ds-cfg-index-type: substring", |
| | | "ds-cfg-index-type: approximate"); |
| | | |
| | | cfg = AdminTestCaseUtils.getConfiguration( |
| | | JEIndexCfgDefn.getInstance(), configEntry); |
| | | |
| | | index = |
| | | ec.getAttributeIndex(DirectoryServer.getAttributeType("mail")); |
| | | ccr = index.applyConfigurationChange(cfg); |
| | | assertTrue(ccr.getResultCode().equals(ResultCode.SUCCESS)); |
| | | assertFalse(ccr.adminActionRequired()); |
| | | assertTrue(ccr.getMessages().isEmpty()); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | @Test(dependsOnMethods = {"testDeleteEntry", "testSearchScope", |
| | | "testSearchIndex"}) |
| | | public void testSearchNotIndexed() throws Exception { |
| | |
| | | import java.util.List; |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | import org.opends.server.admin.std.meta.JEBackendCfgDefn; |
| | | import org.opends.server.admin.server.AdminTestCaseUtils; |
| | |
| | | * EntryContainer tester. |
| | | */ |
| | | public class TestEntryContainer extends JebTestCase { |
| | | private String beID="userRoot"; |
| | | private BackendImpl be; |
| | | |
| | | private static final String ldifString = "dn: dc=com\n" |
| | | + "objectClass: top\n" + "objectClass: domain\n" + "\n" |
| | | + "dn: dc=example,dc=com\n" + "objectClass: top\n" |
| | |
| | | + "cn;lang-en: Rodney Ogasawara\n" |
| | | + "title;lang-en: Sales, Director\n" + "\n" + ""; |
| | | |
| | | private File tempDir; |
| | | private String homeDirName; |
| | | |
| | | private List<Entry> entryList; |
| | | |
| | | private long calculatedHighestID = 0; |
| | |
| | | // sure the server is started. |
| | | TestCaseUtils.startServer(); |
| | | |
| | | tempDir = TestCaseUtils.createTemporaryDirectory("jebtest"); |
| | | homeDirName = tempDir.getAbsolutePath(); |
| | | |
| | | // Create a set of entries |
| | | entryList = TestCaseUtils.entriesFromLdifString(ldifString); |
| | | |
| | |
| | | */ |
| | | @AfterClass |
| | | public void tearDown() throws Exception { |
| | | TestCaseUtils.deleteDirectory(tempDir); |
| | | TestCaseUtils.clearJEBackend(false, beID, "dc=example,dc=com"); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Test() |
| | | public void test1() throws Exception { |
| | | EnvManager.createHomeDir(homeDirName); |
| | | Entry configEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-backend-id=userRoot,cn=Backends,cn=config", |
| | | "objectClass: top", |
| | | "objectClass: ds-cfg-backend", |
| | | "objectClass: ds-cfg-je-backend", |
| | | "ds-cfg-backend-enabled: true", |
| | | "ds-cfg-backend-class: " + |
| | | "org.opends.server.backends.jeb.BackendImpl", |
| | | "ds-cfg-backend-id: userRoot", |
| | | "ds-cfg-backend-writability-mode: enabled", |
| | | "ds-cfg-backend-base-dn: dc=com", |
| | | "ds-cfg-backend-directory:: " + |
| | | Base64.encode(homeDirName.getBytes()), |
| | | "ds-cfg-backend-import-temp-directory: importTmp"); |
| | | JEBackendCfg cfg = AdminTestCaseUtils.getConfiguration( |
| | | JEBackendCfgDefn.getInstance(), configEntry); |
| | | Config backendConfig = new Config(); |
| | | backendConfig.initializeConfig(cfg); |
| | | RootContainer rootContainer = new RootContainer(backendConfig, null); |
| | | rootContainer.open(new File(homeDirName), |
| | | new FilePermission(true, true, true), |
| | | false, true, true, false, true, true); |
| | | |
| | | TestCaseUtils.clearJEBackend(false, beID, null); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | RootContainer rootContainer = be.getRootContainer(); |
| | | EntryContainer entryContainer = |
| | | rootContainer.openEntryContainer(DN.decode("dc=com")); |
| | | rootContainer.getEntryContainer(DN.decode("dc=example,dc=com")); |
| | | |
| | | EntryID actualHighestID = entryContainer.getHighestEntryID(); |
| | | assertTrue(actualHighestID.equals(new EntryID(0))); |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | EntryID actualHighestID = entryContainer.getHighestEntryID(); |
| | | assertTrue(actualHighestID.equals(new EntryID(0))); |
| | | |
| | | for (Entry entry : entryList) { |
| | | entryContainer.addEntry(entry, null); |
| | | Entry afterEntry = entryContainer.getEntry(entry.getDN()); |
| | | assertTrue(afterEntry != null); |
| | | for (Entry entry : entryList) { |
| | | entryContainer.addEntry(entry, null); |
| | | Entry afterEntry = entryContainer.getEntry(entry.getDN()); |
| | | assertTrue(afterEntry != null); |
| | | } |
| | | actualHighestID = entryContainer.getHighestEntryID(); |
| | | assertTrue(actualHighestID.equals(new EntryID(calculatedHighestID))); |
| | | } |
| | | actualHighestID = entryContainer.getHighestEntryID(); |
| | | assertTrue(actualHighestID.equals(new EntryID(calculatedHighestID))); |
| | | |
| | | |
| | | rootContainer.close(); |
| | | EnvManager.removeFiles(homeDirName); |
| | | finally |
| | | { |
| | | entryContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.tasks.TaskUtils; |
| | | import org.opends.server.admin.std.server.JEBackendCfg; |
| | | import org.opends.server.admin.std.meta.JEBackendCfgDefn; |
| | | import org.opends.server.admin.server.AdminTestCaseUtils; |
| | |
| | | |
| | | public class TestImportJob extends JebTestCase |
| | | { |
| | | private String beID="importRoot"; |
| | | private File tempDir; |
| | | private String homeDirName; |
| | | private String importDirName; |
| | | |
| | | private LDIFImportConfig ldifImportConfig; |
| | | private Config backendConfig; |
| | | |
| | | private DN[] baseDNs; |
| | | private BackendImpl be; |
| | | |
| | | private String errorCount="verify-error-count"; |
| | | |
| | | private String top = "dn: dc=example,dc=com\n" + |
| | | private String top = "dn: dc=importtest,dc=com\n" + |
| | | "objectclass: top\n" + |
| | | "objectclass: domain\n" + |
| | | "dc: example\n" + |
| | | "\n" + |
| | | "dn: ou=People,dc=example,dc=com\n" + |
| | | "dn: ou=People,dc=importtest,dc=com\n" + |
| | | "objectclass: top\n" + |
| | | "objectclass: organizationalUnit\n" + |
| | | "ou: People\n" + |
| | | "\n" + |
| | | "dn: dc=example1,dc=com\n" + |
| | | "dn: dc=importtest1,dc=com\n" + |
| | | "objectclass: top\n" + |
| | | "objectclass: domain\n" + |
| | | "dc: example1\n"; |
| | | private String entries1 = |
| | | "dn: uid=user.0,ou=People,dc=example,dc=com\n" + |
| | | "dn: uid=user.0,ou=People,dc=importtest,dc=com\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person\n" + |
| | | "objectClass: organizationalPerson\n" + |
| | |
| | | "postalAddress: Aaccf Amar$99262 Eleventh Street$Salem, NM 36530\n" + |
| | | "description: This is the description for Aaccf Amar.\n" + |
| | | "\n" + |
| | | "dn: uid=user.539,ou=People,dc=example,dc=com\n" + |
| | | "dn: uid=user.539,ou=People,dc=importtest,dc=com\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person\n" + |
| | | "objectClass: organizationalPerson\n" + |
| | |
| | | "postalAddress: Ardyth Bainton$81170 Taylor Street$Syracuse, WV 93507\n" + |
| | | "description: This is the description for Ardyth Bainton.\n" + |
| | | "\n" + |
| | | "dn: uid=user.446,dc=example1,dc=com\n" + |
| | | "dn: uid=user.446,dc=importtest1,dc=com\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person\n" + |
| | | "objectClass: organizationalPerson\n" + |
| | |
| | | "postalAddress: Annalee Avard$46168 Mill Street$Charleston, CO 60948\n" + |
| | | "description: This is the description for Annalee Avard.\n" + |
| | | "\n" + |
| | | "dn: uid=user.362,dc=example1,dc=com\n" + |
| | | "dn: uid=user.362,dc=importtest1,dc=com\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person\n" + |
| | | "objectClass: organizationalPerson\n" + |
| | |
| | | "description: This is the description for Andaree Asawa.\n"; |
| | | |
| | | private String replacement1 = |
| | | "dn: uid=user.446,dc=example1,dc=com\n" + |
| | | "dn: uid=user.446,dc=importtest1,dc=com\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: person\n" + |
| | | "objectClass: organizationalPerson\n" + |
| | |
| | | |
| | | tempDir = TestCaseUtils.createTemporaryDirectory("jebimporttest"); |
| | | homeDirName = tempDir.getAbsolutePath(); |
| | | importDirName = homeDirName + File.separator + "importTmp"; |
| | | |
| | | EnvManager.createHomeDir(homeDirName); |
| | | |
| | |
| | | writer.close(); |
| | | ldifFile.close(); |
| | | |
| | | Entry configEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-backend-id=userRoot,cn=Backends,cn=config", |
| | | "objectClass: top", |
| | | "objectClass: ds-cfg-backend", |
| | | "objectClass: ds-cfg-je-backend", |
| | | "ds-cfg-backend-base-dn: dc=example,dc=com", |
| | | "ds-cfg-backend-base-dn: dc=example1,dc=com", |
| | | "ds-cfg-backend-writability-mode: enabled", |
| | | "ds-cfg-backend-enabled: true", |
| | | "ds-cfg-backend-class: org.opends.server.backends.jeb.BackendImpl", |
| | | "ds-cfg-backend-id: userRoot", |
| | | "ds-cfg-backend-directory:: " + Base64.encode(homeDirName.getBytes()), |
| | | "ds-cfg-backend-import-temp-directory:: " + Base64.encode(importDirName.getBytes())); |
| | | |
| | | ConfigEntry backendConfigEntry = new ConfigEntry(configEntry, null); |
| | | JEBackendCfg cfg = AdminTestCaseUtils.getConfiguration( |
| | | JEBackendCfgDefn.getInstance(), configEntry); |
| | | |
| | | Entry indexEntry = TestCaseUtils.makeEntry( |
| | | "dn: cn=Index,ds-cfg-backend-id=userRoot,cn=Backends,cn=config\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: ds-cfg-branch\n" + |
| | | "cn: Index"); |
| | | |
| | | ConfigEntry indexConfigEntry = new ConfigEntry(indexEntry, backendConfigEntry); |
| | | |
| | | Entry cnIndexEntry = TestCaseUtils.makeEntry( |
| | | "dn: ds-cfg-index-attribute=cn,cn=Index,ds-cfg-backend-id=userRoot,cn=Backends,cn=config\n" + |
| | | "objectClass: top\n" + |
| | | "objectClass: ds-cfg-je-index\n" + |
| | | "ds-cfg-index-attribute: cn\n" + |
| | | "ds-cfg-index-type: presence\n" + |
| | | "ds-cfg-index-type: equality\n" + |
| | | "ds-cfg-index-type: substring\n" + |
| | | "ds-cfg-index-type: ordering"); |
| | | |
| | | ConfigEntry cnIndexConfigEntry = new ConfigEntry(cnIndexEntry, indexConfigEntry); |
| | | |
| | | indexConfigEntry.addChild(cnIndexConfigEntry); |
| | | backendConfigEntry.addChild(indexConfigEntry); |
| | | |
| | | |
| | | baseDNs = new DN[] |
| | | { |
| | | DN.decode("dc=example,dc=com"), |
| | | DN.decode("dc=example1,dc=com") |
| | | DN.decode("dc=importtest,dc=com"), |
| | | DN.decode("dc=importtest1,dc=com") |
| | | }; |
| | | |
| | | backendConfig = new Config(); |
| | | backendConfig.initializeConfig(cfg); |
| | | } |
| | | |
| | | @AfterClass |
| | |
| | | @Test |
| | | public void testImportAll() throws Exception |
| | | { |
| | | TestCaseUtils.clearJEBackend(false, beID, null); |
| | | ArrayList<String> fileList = new ArrayList<String>(); |
| | | fileList.add(homeDirName + File.separator + "top.ldif"); |
| | | fileList.add(homeDirName + File.separator + "entries1.ldif"); |
| | |
| | | importConfig.setValidateSchema(true); |
| | | importConfig.writeRejectedEntries(rejectedEntries); |
| | | |
| | | ImportJob importJob = new ImportJob(null, backendConfig, importConfig); |
| | | importJob.importLDIF(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | TaskUtils.disableBackend(beID); |
| | | try |
| | | { |
| | | be.importLDIF(importConfig); |
| | | } |
| | | finally |
| | | { |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | |
| | | RootContainer rootContainer = new RootContainer(backendConfig, null); |
| | | rootContainer.open(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | RootContainer rootContainer = be.getRootContainer(); |
| | | EntryContainer entryContainer; |
| | | |
| | | assertTrue(rejectedEntries.size() <= 0); |
| | | for(DN baseDN : baseDNs) |
| | | { |
| | | entryContainer = rootContainer.openEntryContainer(baseDN); |
| | | |
| | | |
| | | assertNotNull(entryContainer); |
| | | |
| | | if(baseDN.toString().equals("dc=example,dc=com")) |
| | | entryContainer = rootContainer.getEntryContainer(baseDN); |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 4); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("ou=People,dc=example,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.0,ou=People,dc=example,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.539,ou=People,dc=example,dc=com"))); |
| | | assertNotNull(entryContainer); |
| | | |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(baseDN); |
| | | if(baseDN.toString().equals("dc=importtest,dc=com")) |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 4); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("ou=People,dc=importtest,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.0,ou=People,dc=importtest,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.539,ou=People,dc=importtest,dc=com"))); |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(backendConfig, verifyConfig); |
| | | Entry statEntry=bldStatEntry(""); |
| | | verifyJob.verifyBackend(rootContainer, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(baseDN); |
| | | |
| | | Entry statEntry=bldStatEntry(""); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | be.verifyBackend(verifyConfig, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | } |
| | | else if(baseDN.toString().equals("dc=importtest1,dc=com")) |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 3); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.446,dc=importtest1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.362,dc=importtest1,dc=com"))); |
| | | |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(baseDN); |
| | | |
| | | Entry statEntry=bldStatEntry(""); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | be.verifyBackend(verifyConfig, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | } |
| | | } |
| | | else if(baseDN.toString().equals("dc=example1,dc=com")) |
| | | finally |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 3); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.446,dc=example1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.362,dc=example1,dc=com"))); |
| | | entryContainer.sharedLock.unlock(); |
| | | |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(baseDN); |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(backendConfig, verifyConfig); |
| | | Entry statEntry=bldStatEntry(""); |
| | | verifyJob.verifyBackend(rootContainer, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | } |
| | | } |
| | | |
| | | rootContainer.close(); |
| | | } |
| | | |
| | | @Test(dependsOnMethods = "testImportAll") |
| | |
| | | importConfig.setValidateSchema(true); |
| | | importConfig.writeRejectedEntries(rejectedEntries); |
| | | |
| | | ImportJob importJob = new ImportJob(null, backendConfig, importConfig); |
| | | importJob.importLDIF(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | TaskUtils.disableBackend(beID); |
| | | try |
| | | { |
| | | be.importLDIF(importConfig); |
| | | } |
| | | finally |
| | | { |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | |
| | | RootContainer rootContainer = new RootContainer(backendConfig, null); |
| | | rootContainer.open(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | RootContainer rootContainer = be.getRootContainer(); |
| | | EntryContainer entryContainer; |
| | | |
| | | entryContainer = rootContainer.openEntryContainer(DN.decode("dc=example1,dc=com")); |
| | | entryContainer = rootContainer.getEntryContainer(DN.decode("dc=importtest1,dc=com")); |
| | | assertNotNull(entryContainer); |
| | | |
| | | assertTrue(rejectedEntries.size() <= 0); |
| | | Entry entry = entryContainer.getEntry(DN.decode("uid=user.446,dc=example1,dc=com")); |
| | | assertNotNull(entry); |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | assertTrue(rejectedEntries.size() <= 0); |
| | | Entry entry = entryContainer.getEntry(DN.decode("uid=user.446,dc=importtest1,dc=com")); |
| | | assertNotNull(entry); |
| | | |
| | | AttributeType attribute = entry.getAttribute("cn").get(0).getAttributeType(); |
| | | AttributeType attribute = entry.getAttribute("cn").get(0).getAttributeType(); |
| | | |
| | | assertTrue(entry.hasValue(attribute, null, new AttributeValue(attribute,"Annalee Bogard"))); |
| | | assertTrue(entry.hasValue(attribute, null, new AttributeValue(attribute,"Annalee Bogard"))); |
| | | |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(DN.decode("dc=example1,dc=com")); |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(DN.decode("dc=importtest1,dc=com")); |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(backendConfig, verifyConfig); |
| | | Entry statEntry=bldStatEntry(""); |
| | | verifyJob.verifyBackend(rootContainer, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | |
| | | rootContainer.close(); |
| | | Entry statEntry=bldStatEntry(""); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | be.verifyBackend(verifyConfig, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | @Test(dependsOnMethods = "testImportReplaceExisting") |
| | |
| | | importConfig.setValidateSchema(true); |
| | | importConfig.writeRejectedEntries(rejectedEntries); |
| | | |
| | | ImportJob importJob = new ImportJob(null, backendConfig, importConfig); |
| | | importJob.importLDIF(); |
| | | |
| | | assertTrue(rejectedEntries.toString().contains("uid=user.446,dc=example1,dc=com")); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | TaskUtils.disableBackend(beID); |
| | | try |
| | | { |
| | | be.importLDIF(importConfig); |
| | | } |
| | | finally |
| | | { |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | assertTrue(rejectedEntries.toString().contains("uid=user.446,dc=importtest1,dc=com")); |
| | | } |
| | | |
| | | @Test(dependsOnMethods = "testImportReplaceExisting") |
| | |
| | | importConfig.setReplaceExistingEntries(false); |
| | | importConfig.setValidateSchema(true); |
| | | |
| | | ImportJob importJob = new ImportJob(null, backendConfig, importConfig); |
| | | importJob.importLDIF(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | TaskUtils.disableBackend(beID); |
| | | try |
| | | { |
| | | be.importLDIF(importConfig); |
| | | } |
| | | finally |
| | | { |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | |
| | | importConfig = new LDIFImportConfig(homeDirName + File.separator + "entries1.ldif"); |
| | | importConfig.setAppendToExistingData(true); |
| | | importConfig.setReplaceExistingEntries(false); |
| | | importConfig.setValidateSchema(true); |
| | | |
| | | importJob = new ImportJob(null, backendConfig, importConfig); |
| | | importJob.importLDIF(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | TaskUtils.disableBackend(beID); |
| | | try |
| | | { |
| | | be.importLDIF(importConfig); |
| | | } |
| | | finally |
| | | { |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | |
| | | RootContainer rootContainer = new RootContainer(backendConfig, null); |
| | | rootContainer.open(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | RootContainer rootContainer = be.getRootContainer(); |
| | | EntryContainer entryContainer; |
| | | |
| | | for(DN baseDN : baseDNs) |
| | | { |
| | | entryContainer = rootContainer.openEntryContainer(baseDN); |
| | | entryContainer = rootContainer.getEntryContainer(baseDN); |
| | | assertNotNull(entryContainer); |
| | | |
| | | if(baseDN.toString().equals("dc=example,dc=com")) |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 4); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("ou=People,dc=example,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.0,ou=People,dc=example,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.539,ou=People,dc=example,dc=com"))); |
| | | if(baseDN.toString().equals("dc=importtest,dc=com")) |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 4); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("ou=People,dc=importtest,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.0,ou=People,dc=importtest,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.539,ou=People,dc=importtest,dc=com"))); |
| | | } |
| | | else if(baseDN.toString().equals("dc=importtest1,dc=com")) |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 3); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.446,dc=importtest1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.362,dc=importtest1,dc=com"))); |
| | | } |
| | | } |
| | | else if(baseDN.toString().equals("dc=example1,dc=com")) |
| | | finally |
| | | { |
| | | assertEquals(entryContainer.getEntryCount(), 3); |
| | | assertTrue(entryContainer.entryExists(baseDN)); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.446,dc=example1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.362,dc=example1,dc=com"))); |
| | | entryContainer.sharedLock.unlock(); |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | } |
| | | |
| | | rootContainer.close(); |
| | | } |
| | | |
| | | @Test(dependsOnMethods = "testImportAll") |
| | |
| | | importConfig.setValidateSchema(true); |
| | | importConfig.writeRejectedEntries(rejectedEntries); |
| | | |
| | | ImportJob importJob = new ImportJob(null, backendConfig, importConfig); |
| | | importJob.importLDIF(); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | TaskUtils.disableBackend(beID); |
| | | try |
| | | { |
| | | be.importLDIF(importConfig); |
| | | } |
| | | finally |
| | | { |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | |
| | | assertTrue(rejectedEntries.toString().contains("uid=user.446,dc=example1,dc=com")); |
| | | assertTrue(rejectedEntries.toString().contains("uid=user.446,dc=importtest1,dc=com")); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Test() |
| | | public void testCleanDN2ID() throws Exception { |
| | | preTest(3); |
| | | //Add a junk DN and non-existent entry id to DN2ID index |
| | | DN testDN=DN.decode(junkDN); |
| | | EntryID id=new EntryID(45); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | //Make two DN keys point at same entry. |
| | | testDN=DN.decode(junkDN1); |
| | | id=new EntryID(3); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | //Add badDN key with bad entry id |
| | | DatabaseEntry key= |
| | | new DatabaseEntry(StaticUtils.getBytes(badDN)); |
| | | DatabaseEntry data = |
| | | new EntryID(37).getDatabaseEntry(); |
| | | assertTrue(dn2id.putRaw(txn, key, data)); |
| | | //Add DN key with malformed entryID |
| | | key=new DatabaseEntry(StaticUtils.getBytes(junkDN2)); |
| | | data= new DatabaseEntry(new byte[3]); |
| | | assertTrue(dn2id.putRaw(txn, key, data)); |
| | | //Try to break JebFormat version |
| | | addID2EntryReturnKey(junkDN3, 20, true); |
| | | id=new EntryID(20); |
| | | assertTrue(dn2id.insert(txn, DN.decode(junkDN3), id)); |
| | | performBECleanVerify("dn2id", 5); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add a junk DN and non-existent entry id to DN2ID index |
| | | DN testDN=DN.decode(junkDN); |
| | | EntryID id=new EntryID(45); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | //Make two DN keys point at same entry. |
| | | testDN=DN.decode(junkDN1); |
| | | id=new EntryID(3); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | //Add badDN key with bad entry id |
| | | DatabaseEntry key= |
| | | new DatabaseEntry(StaticUtils.getBytes(badDN)); |
| | | DatabaseEntry data = |
| | | new EntryID(37).getDatabaseEntry(); |
| | | assertTrue(dn2id.putRaw(txn, key, data)); |
| | | //Add DN key with malformed entryID |
| | | key=new DatabaseEntry(StaticUtils.getBytes(junkDN2)); |
| | | data= new DatabaseEntry(new byte[3]); |
| | | assertTrue(dn2id.putRaw(txn, key, data)); |
| | | //Try to break JebFormat version |
| | | addID2EntryReturnKey(junkDN3, 20, true); |
| | | id=new EntryID(20); |
| | | assertTrue(dn2id.insert(txn, DN.decode(junkDN3), id)); |
| | | performBECleanVerify("dn2id", 5); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Test() public void testCleanID2Children() throws Exception { |
| | | preTest(3); |
| | | //Add malformed key |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | | EntryIDSet idSet=new EntryIDSet(); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //Try to break JebFormat version of key entry |
| | | key=addID2EntryReturnKey(junkDN, 4, true); |
| | | idSet=new EntryIDSet(new byte[16], new byte[16]); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //put invalid key -- no EntryID matches |
| | | key= new EntryID(45).getDatabaseEntry(); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //invalid ids in id list |
| | | key=addID2EntryReturnKey(junkDN1, 5, false); |
| | | byte[] idBytes=new byte[24]; |
| | | //doesn't exist |
| | | idBytes[3] = (byte)0xff; |
| | | //not a child |
| | | idBytes[15] = (byte)1; |
| | | //bad jeb format |
| | | idBytes[23] = (byte) 0x04; |
| | | idSet=new EntryIDSet(null, idBytes); |
| | | id2child.writeKey(txn, key, idSet); |
| | | performBECleanVerify("id2children", 6); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add malformed key |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | | EntryIDSet idSet=new EntryIDSet(); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //Try to break JebFormat version of key entry |
| | | key=addID2EntryReturnKey(junkDN, 4, true); |
| | | idSet=new EntryIDSet(new byte[16], new byte[16]); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //put invalid key -- no EntryID matches |
| | | key= new EntryID(45).getDatabaseEntry(); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //invalid ids in id list |
| | | key=addID2EntryReturnKey(junkDN1, 5, false); |
| | | byte[] idBytes=new byte[24]; |
| | | //doesn't exist |
| | | idBytes[3] = (byte)0xff; |
| | | //not a child |
| | | idBytes[15] = (byte)1; |
| | | //bad jeb format |
| | | idBytes[23] = (byte) 0x04; |
| | | idSet=new EntryIDSet(null, idBytes); |
| | | id2child.writeKey(txn, key, idSet); |
| | | performBECleanVerify("id2children", 6); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Test() public void testCleanID2Subtree() throws Exception { |
| | | preTest(4); |
| | | //break key |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | | EntryIDSet idSet=new EntryIDSet(); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | //put invalid ids into entry 3 idlist |
| | | key= new EntryID(3).getDatabaseEntry(); |
| | | byte[] idBytes=new byte[16]; |
| | | //invalid id |
| | | idBytes[3] = (byte)0xff; |
| | | //non-subordinate |
| | | idBytes[15] = (byte)1; |
| | | idSet=new EntryIDSet(null, idBytes); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | //Try to break JebFormat version of key entry |
| | | key=addID2EntryReturnKey(junkDN, 4, true); |
| | | idBytes[3]=(byte) 0x04; |
| | | idBytes[15]=(byte)0x00; |
| | | EntryIDSet idSet1=new EntryIDSet(null, idBytes); |
| | | id2subtree.writeKey(txn, key, idSet1); |
| | | //put invalid key -- no EntryID matches |
| | | key= new EntryID(45).getDatabaseEntry(); |
| | | idSet=new EntryIDSet(null, idBytes); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | performBECleanVerify("id2subtree", 7); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //break key |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | | EntryIDSet idSet=new EntryIDSet(); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | //put invalid ids into entry 3 idlist |
| | | key= new EntryID(3).getDatabaseEntry(); |
| | | byte[] idBytes=new byte[16]; |
| | | //invalid id |
| | | idBytes[3] = (byte)0xff; |
| | | //non-subordinate |
| | | idBytes[15] = (byte)1; |
| | | idSet=new EntryIDSet(null, idBytes); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | //Try to break JebFormat version of key entry |
| | | key=addID2EntryReturnKey(junkDN, 4, true); |
| | | idBytes[3]=(byte) 0x04; |
| | | idBytes[15]=(byte)0x00; |
| | | EntryIDSet idSet1=new EntryIDSet(null, idBytes); |
| | | id2subtree.writeKey(txn, key, idSet1); |
| | | //put invalid key -- no EntryID matches |
| | | key= new EntryID(45).getDatabaseEntry(); |
| | | idSet=new EntryIDSet(null, idBytes); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | performBECleanVerify("id2subtree", 7); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | * @throws Exception if the error count is not equal to 4. |
| | | */ |
| | | @Test() public void testCleanAttrIndex() throws Exception { |
| | | String phoneType="telephoneNumber"; |
| | | String phoneType="telephonenumber"; |
| | | preTest(3); |
| | | //Need to open a second database against this index |
| | | //so we can manipulate it. We can't get the index DB handle |
| | | //any other way. |
| | | DatabaseConfig config = new DatabaseConfig(); |
| | | config.setAllowCreate(true); |
| | | config.setTransactional(true); |
| | | Database db= |
| | | eContainer.openDatabase(config, phoneType + ".equality"); |
| | | //Add entry with bad JEB format Version |
| | | addID2EntryReturnKey(junkDN, 4, true); |
| | | //Add phone number with various bad id list entryIDs |
| | | byte[] subBytes = StaticUtils.getBytes("0009999999"); |
| | | DatabaseEntry key= new DatabaseEntry(subBytes); |
| | | byte[] dataBytes=new byte[32]; |
| | | //put duplicate ids in list |
| | | dataBytes[7] = (byte)1; |
| | | dataBytes[15] = (byte)1; |
| | | //put id that doesn't exist |
| | | dataBytes[23] = (byte)0xff; |
| | | //point to bad entry added above |
| | | dataBytes[31] = (byte) 0x04; |
| | | DatabaseEntry data= new DatabaseEntry(dataBytes); |
| | | OperationStatus status = EntryContainer.put(db, txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //really 5 errors, but duplicate reference doesn't increment error |
| | | //count for some reason |
| | | performBECleanVerify(phoneType, 4); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | AttributeType attributeType = |
| | | DirectoryServer.getAttributeType(phoneType); |
| | | Index index = |
| | | eContainer.getAttributeIndex(attributeType).equalityIndex; |
| | | //Add entry with bad JEB format Version |
| | | addID2EntryReturnKey(junkDN, 4, true); |
| | | //Add phone number with various bad id list entryIDs |
| | | byte[] subBytes = StaticUtils.getBytes("0009999999"); |
| | | DatabaseEntry key= new DatabaseEntry(subBytes); |
| | | byte[] dataBytes=new byte[32]; |
| | | //put duplicate ids in list |
| | | dataBytes[7] = (byte)1; |
| | | dataBytes[15] = (byte)1; |
| | | //put id that doesn't exist |
| | | dataBytes[23] = (byte)0xff; |
| | | //point to bad entry added above |
| | | dataBytes[31] = (byte) 0x04; |
| | | DatabaseEntry data= new DatabaseEntry(dataBytes); |
| | | OperationStatus status = index.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //really 5 errors, but duplicate reference doesn't increment error |
| | | //count for some reason |
| | | performBECleanVerify(phoneType, 4); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /* |
| | |
| | | */ |
| | | @Test() public void testVerifyID2Entry() throws Exception { |
| | | preTest(3); |
| | | //Add entry with short id |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | | Entry testEntry=bldStatEntry(junkDN); |
| | | byte []entryBytes = |
| | | JebFormat.entryToDatabase(testEntry, new DataConfig()); |
| | | DatabaseEntry data= new DatabaseEntry(entryBytes); |
| | | assertTrue(id2entry.putRaw(txn, key, data)); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add entry with short id |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | | Entry testEntry=bldStatEntry(junkDN); |
| | | byte []entryBytes = |
| | | JebFormat.entryToDatabase(testEntry, new DataConfig()); |
| | | DatabaseEntry data= new DatabaseEntry(entryBytes); |
| | | assertTrue(id2entry.putRaw(txn, key, data)); |
| | | |
| | | //add entry with ramdom bytes |
| | | DatabaseEntry key1= new EntryID(4).getDatabaseEntry(); |
| | | byte []eBytes = new byte[459]; |
| | | for(int i=0;i<459;i++) { |
| | | eBytes[i]=(byte) (i*2); |
| | | //add entry with ramdom bytes |
| | | DatabaseEntry key1= new EntryID(4).getDatabaseEntry(); |
| | | byte []eBytes = new byte[459]; |
| | | for(int i=0;i<459;i++) { |
| | | eBytes[i]=(byte) (i*2); |
| | | } |
| | | //set version correctly |
| | | eBytes[0]=0x01; |
| | | DatabaseEntry data1= new DatabaseEntry(eBytes); |
| | | assertTrue(id2entry.putRaw(txn, key1, data1)); |
| | | performBECompleteVerify("telephoneNumber", 3); |
| | | } |
| | | //set version correctly |
| | | eBytes[0]=0x01; |
| | | DatabaseEntry data1= new DatabaseEntry(eBytes); |
| | | assertTrue(id2entry.putRaw(txn, key1, data1)); |
| | | performBECompleteVerify("telephoneNumber", 3); |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Test() public void testVerifyDN2ID() throws Exception { |
| | | preTest(9); |
| | | //add entry but no corresponding dn2id key |
| | | addID2EntryReturnKey(junkDN, 10, false); |
| | | //entry has dn2id key but its entryID -- don't need key |
| | | addID2EntryReturnKey(junkDN1, 11, false); |
| | | //insert key with bad entry id (45 instead of 10) |
| | | DN testDN=DN.decode(junkDN1); |
| | | EntryID id=new EntryID(45); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | //entry has no parent in dn2id |
| | | addID2EntryReturnKey(noParentDN, 12, false); |
| | | //add the key/id |
| | | testDN=DN.decode(noParentDN); |
| | | id=new EntryID(12); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | performBECompleteVerify("dn2id", 3); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //add entry but no corresponding dn2id key |
| | | addID2EntryReturnKey(junkDN, 10, false); |
| | | //entry has dn2id key but its entryID -- don't need key |
| | | addID2EntryReturnKey(junkDN1, 11, false); |
| | | //insert key with bad entry id (45 instead of 10) |
| | | DN testDN=DN.decode(junkDN1); |
| | | EntryID id=new EntryID(45); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | //entry has no parent in dn2id |
| | | addID2EntryReturnKey(noParentDN, 12, false); |
| | | //add the key/id |
| | | testDN=DN.decode(noParentDN); |
| | | id=new EntryID(12); |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | performBECompleteVerify("dn2id", 3); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Test() public void testVerifyID2Children() throws Exception { |
| | | preTest(9); |
| | | //Add dn with no parent |
| | | DatabaseEntry key=addID2EntryReturnKey(noParentDN, 10, false); |
| | | byte[] idBytes=new byte[16]; |
| | | idBytes[7]=(byte) 0x0A; |
| | | EntryIDSet idSet=new EntryIDSet(null, idBytes); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(cDN, 11, false); |
| | | //Add its parent entry -- need the key |
| | | DatabaseEntry keyp=addID2EntryReturnKey(pDN, 12, false); |
| | | //add parent key/IDSet with bad IDset id |
| | | byte[] idBytesp=new byte[16]; |
| | | idBytesp[7]=(byte) 0xFF; |
| | | EntryIDSet idSetp=new EntryIDSet(null, idBytesp); |
| | | id2child.writeKey(txn, keyp, idSetp); |
| | | performBECompleteVerify("id2children", 3); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add dn with no parent |
| | | DatabaseEntry key=addID2EntryReturnKey(noParentDN, 10, false); |
| | | byte[] idBytes=new byte[16]; |
| | | idBytes[7]=(byte) 0x0A; |
| | | EntryIDSet idSet=new EntryIDSet(null, idBytes); |
| | | id2child.writeKey(txn, key, idSet); |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(cDN, 11, false); |
| | | //Add its parent entry -- need the key |
| | | DatabaseEntry keyp=addID2EntryReturnKey(pDN, 12, false); |
| | | //add parent key/IDSet with bad IDset id |
| | | byte[] idBytesp=new byte[16]; |
| | | idBytesp[7]=(byte) 0xFF; |
| | | EntryIDSet idSetp=new EntryIDSet(null, idBytesp); |
| | | id2child.writeKey(txn, keyp, idSetp); |
| | | performBECompleteVerify("id2children", 3); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Test() public void testVerifyID2Children1() throws Exception { |
| | | preTest(2); |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(pDN, 10, false); |
| | | //add parent key/IDSet with null keyset |
| | | EntryIDSet idSetp=new EntryIDSet(); |
| | | DatabaseEntry key= new EntryID(2).getDatabaseEntry(); |
| | | id2child.writeKey(txn, key, idSetp); |
| | | performBECompleteVerify("id2children", 0); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(pDN, 10, false); |
| | | //add parent key/IDSet with null keyset |
| | | EntryIDSet idSetp=new EntryIDSet(); |
| | | DatabaseEntry key= new EntryID(2).getDatabaseEntry(); |
| | | id2child.writeKey(txn, key, idSetp); |
| | | performBECompleteVerify("id2children", 0); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | @Test |
| | | public void testVerifyID2Subtree() throws Exception { |
| | | preTest(2); |
| | | //Add entry with no parent |
| | | addID2EntryReturnKey(noParentDN, 3, false); |
| | | performBECompleteVerify("id2subtree", 3); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add entry with no parent |
| | | addID2EntryReturnKey(noParentDN, 3, false); |
| | | performBECompleteVerify("id2subtree", 3); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | @Test |
| | | public void testVerifyID2Subtree1() throws Exception { |
| | | preTest(2); |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(pDN, 3, false); |
| | | //add parent key/IDSet with null keyset |
| | | EntryIDSet idSet=new EntryIDSet(); |
| | | DatabaseEntry key= new EntryID(2).getDatabaseEntry(); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | performBECompleteVerify("id2subtree", 1); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(pDN, 3, false); |
| | | //add parent key/IDSet with null keyset |
| | | EntryIDSet idSet=new EntryIDSet(); |
| | | DatabaseEntry key= new EntryID(2).getDatabaseEntry(); |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | performBECompleteVerify("id2subtree", 1); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | @Test() public void testVerifyAttribute() throws Exception { |
| | | String mailType="mail"; |
| | | preTest(4); |
| | | //Need to open a second databases against this index |
| | | //so we can manipulate it. |
| | | DatabaseConfig config = new DatabaseConfig(); |
| | | config.setAllowCreate(true); |
| | | config.setTransactional(true); |
| | | //Get db handles to each index. |
| | | Database dbEq= |
| | | eContainer.openDatabase(config, mailType + ".equality"); |
| | | Database dbPres= |
| | | eContainer.openDatabase(config, mailType + ".presence"); |
| | | Database dbSub= |
| | | eContainer.openDatabase(config, mailType + ".substring"); |
| | | Database dbOr= |
| | | eContainer.openDatabase(config, mailType + ".ordering"); |
| | | //Add invalid idlist ids to both equality and ordering indexes. |
| | | DatabaseEntry key= |
| | | new DatabaseEntry(StaticUtils.getBytes("user.0@example.com")); |
| | | byte[] dataBytes=new byte[16]; |
| | | //put duplicate ids in list |
| | | dataBytes[7] = (byte)0xff; |
| | | dataBytes[15] = (byte)0xfe; |
| | | DatabaseEntry data= new DatabaseEntry(dataBytes); |
| | | OperationStatus status = EntryContainer.put(dbEq, txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | status = EntryContainer.put(dbOr, txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //Add null idlist to both equality and ordering indexes. |
| | | key = |
| | | new DatabaseEntry(StaticUtils.getBytes("user.1@example.com")); |
| | | data= new DatabaseEntry(new EntryIDSet().toDatabase()); |
| | | status = EntryContainer.put(dbEq, txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | status = EntryContainer.put(dbOr, txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //Add invalid idlist ids to presence index. |
| | | key = |
| | | new DatabaseEntry(StaticUtils.getBytes("+")); |
| | | data = new DatabaseEntry(dataBytes); |
| | | status = EntryContainer.put(dbPres, txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //Add invalid idlist ids to substring index. |
| | | key = |
| | | new DatabaseEntry(StaticUtils.getBytes("@examp")); |
| | | data = new DatabaseEntry(dataBytes); |
| | | status = EntryContainer.put(dbSub, txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | performBECompleteVerify(mailType, 6); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | AttributeType attributeType = |
| | | DirectoryServer.getAttributeType(mailType); |
| | | //Get db handles to each index. |
| | | Index eqIndex = |
| | | eContainer.getAttributeIndex(attributeType).equalityIndex; |
| | | Index presIndex = |
| | | eContainer.getAttributeIndex(attributeType).presenceIndex; |
| | | Index subIndex = |
| | | eContainer.getAttributeIndex(attributeType).substringIndex; |
| | | Index ordIndex = |
| | | eContainer.getAttributeIndex(attributeType).orderingIndex; |
| | | //Add invalid idlist ids to both equality and ordering indexes. |
| | | DatabaseEntry key= |
| | | new DatabaseEntry(StaticUtils.getBytes("user.0@example.com")); |
| | | byte[] dataBytes=new byte[16]; |
| | | //put duplicate ids in list |
| | | dataBytes[7] = (byte)0xff; |
| | | dataBytes[15] = (byte)0xfe; |
| | | DatabaseEntry data= new DatabaseEntry(dataBytes); |
| | | OperationStatus status = eqIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | status = ordIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //Add null idlist to both equality and ordering indexes. |
| | | key = |
| | | new DatabaseEntry(StaticUtils.getBytes("user.1@example.com")); |
| | | data= new DatabaseEntry(new EntryIDSet().toDatabase()); |
| | | status = eqIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | status = ordIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //Add invalid idlist ids to presence index. |
| | | key = |
| | | new DatabaseEntry(StaticUtils.getBytes("+")); |
| | | data = new DatabaseEntry(dataBytes); |
| | | status = presIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | //Add invalid idlist ids to substring index. |
| | | key = |
| | | new DatabaseEntry(StaticUtils.getBytes("@examp")); |
| | | data = new DatabaseEntry(dataBytes); |
| | | status = subIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | performBECompleteVerify(mailType, 6); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /* Various tests not either clean or complete */ |