This refactoring includes the following changes to the JE backend:
- Extracted common interface DatabaseContainer from DN2ID, ID2Entry, etc... classes.
- Moved database read and write methods from EntryContainer to DatabaseContainer.
- Added index configuration to the XML based admin framework.
- Removed redundant configuration objects (Config, IndexConfig).
- Added exclusive/shared lock to EntryContainer. All access to an EntryContainer must acquire a lock before using the internal
DatabaseContainers or making configuration changes.
- Added the ability to add/remove/modify indexes with the backend online. Server will issue rebuild required warning when adding new indexes
or sub-indexes (equality, substring, presence...).
- Added the ability to change the index entry limit for both the backend and each index with the backend online. Server will issue rebuild
required warning if the previous limit has been exceeded.
- Added the ability to change entry compression and index substring length setting while the backend is online.
- Added a persistent state database to each EntryContainer to persist backend configuration between server restarts. Server will issue
rebuild required warning if a new index is added when the backend is offline.
- Added a trusted flag to indexes so that non existent keys will not be interpreted as an empty entry ID set when an index is untrusted. An
index is untrusted when it is added to an non-empty EntryContainer or an inconsistency is detected. Server will issue warning on startup to
rebuild the index.
- Fixed a issue where the LDIF import process stops responding if the temporary import dir is full or unwritable.
Fix for issue 1480 1455 1575
2 files deleted
3 files added
48 files modified
| | |
| | | </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", |
| | | approximateIndexer, |
| | | indexConfig.getEqualityEntryLimit(), |
| | | indexConfig.getCursorEntryLimit()); |
| | | 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, |
| | | 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; |
| | |
| | | /** |
| | | * The configuration of this JE backend. |
| | | */ |
| | | private Config config; |
| | | private JEBackendCfg cfg; |
| | | |
| | | /** |
| | |
| | | */ |
| | | private long checksumDbEnv() { |
| | | |
| | | List<File> jdbFiles = |
| | | Arrays.asList(config.getBackendDirectory().listFiles(new FilenameFilter() { |
| | | 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()); |
| | |
| | | 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; |
| | | } |
| | | |
| | | |
| | |
| | | DirectoryServer.registerOfflineBackendStateID(this.getBackendID(), |
| | | 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(); |
| | | try |
| | | { |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | ec.sharedLock.lock(); |
| | | Entry entry; |
| | | try |
| | | { |
| | |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | |
| | | return entry; |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | readerEnd(); |
| | | } |
| | | |
| | | return entry; |
| | | } |
| | | |
| | | |
| | |
| | | throws DirectoryException |
| | | { |
| | | writerBegin(); |
| | | try |
| | | { |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | ec.addEntry(entry, addOperation); |
| | |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | throws DirectoryException |
| | | { |
| | | writerBegin(); |
| | | try |
| | | { |
| | | |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | ec.deleteEntry(entryDN, deleteOperation); |
| | |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | throws DirectoryException |
| | | { |
| | | writerBegin(); |
| | | try |
| | | { |
| | | |
| | | DN entryDN = entry.getDN(); |
| | | EntryContainer ec = rootContainer.getEntryContainer(entryDN); |
| | | ec.sharedLock.lock(); |
| | | |
| | | try |
| | | { |
| | |
| | | e.getMessage(), |
| | | e.getMessageID()); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | throws DirectoryException, CancelledOperationException |
| | | { |
| | | writerBegin(); |
| | | try |
| | | { |
| | | EntryContainer currentContainer = rootContainer.getEntryContainer( |
| | | currentDN); |
| | | EntryContainer container = rootContainer.getEntryContainer(entry.getDN()); |
| | |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | msg, msgID); |
| | | } |
| | | try |
| | | { |
| | | currentContainer.sharedLock.lock(); |
| | | |
| | | currentContainer.renameEntry(currentDN, entry, modifyDNOperation); |
| | | } |
| | |
| | | } |
| | | finally |
| | | { |
| | | currentContainer.sharedLock.unlock(); |
| | | writerEnd(); |
| | | } |
| | | } |
| | |
| | | throws DirectoryException |
| | | { |
| | | readerBegin(); |
| | | try |
| | | { |
| | | EntryContainer ec = rootContainer.getEntryContainer( |
| | | searchOperation.getBaseDN()); |
| | | ec.sharedLock.lock(); |
| | | |
| | | try |
| | | { |
| | | ec.search(searchOperation); |
| | | } |
| | | catch (DatabaseException e) |
| | |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | readerEnd(); |
| | | } |
| | | } |
| | |
| | | { |
| | | 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 |
| | |
| | | public void importLDIF(LDIFImportConfig importConfig) |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | { |
| | | 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) |
| | |
| | | { |
| | | 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); |
| | |
| | | { |
| | | // 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); |
| | | if(rootContainer != null) |
| | | { |
| | | DN[] baseDNs = new DN[newCfg.getBackendBaseDN().size()]; |
| | | baseDNs = newCfg.getBackendBaseDN().toArray(baseDNs); |
| | | |
| | | // Check for changes to the base DNs. |
| | | for (DN baseDN : config.getBaseDNs()) |
| | | for (DN baseDN : cfg.getBackendBaseDN()) |
| | | { |
| | | boolean found = false; |
| | | for (DN dn : baseDNs) |
| | |
| | | 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); |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | // 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; |
| | |
| | | public void clearBackend() |
| | | 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); |
| | | |
| | | dn2idComparator = new EntryContainer.KeyReverseComparator(); |
| | | DatabaseConfig dn2idConfig = new DatabaseConfig(); |
| | | |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | dn2idConfig.setReadOnly(true); |
| | | dn2idConfig.setAllowCreate(false); |
| | | dn2idConfig.setTransactional(false); |
| | | } |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | dn2idConfig.setAllowCreate(true); |
| | | dn2idConfig.setTransactional(false); |
| | | dn2idConfig.setDeferredWrite(true); |
| | | } |
| | | else |
| | | { |
| | | dn2idConfig.setAllowCreate(true); |
| | | dn2idConfig.setTransactional(true); |
| | | } |
| | | |
| | | /** |
| | | * 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) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("JE DN2ID database %s opened with %d records.", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | } |
| | | return database; |
| | | 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) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | dn2uriComparator = new EntryContainer.KeyReverseComparator(); |
| | | DatabaseConfig dn2uriConfig = new DatabaseConfig(); |
| | | |
| | | if(debugEnabled()) |
| | | if(env.getConfig().getReadOnly()) |
| | | { |
| | | 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); |
| | | } |
| | | else if(!env.getConfig().getTransactional()) |
| | | { |
| | | dn2uriConfig.setSortedDuplicates(true); |
| | | dn2uriConfig.setAllowCreate(true); |
| | | dn2uriConfig.setTransactional(false); |
| | | dn2uriConfig.setDeferredWrite(true); |
| | | } |
| | | return database; |
| | | 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.getDatabaseName(); |
| | | String name = database.getName(); |
| | | if (name.endsWith(EntryContainer.ID2ENTRY_DATABASE_NAME)) |
| | | { |
| | | return 1; |
| | |
| | | return 3; |
| | | } |
| | | } |
| | | catch (DatabaseException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | return 3; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Compares its two arguments for order. Returns a negative integer, |
| | |
| | | * 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; |
| | | } |
| | | } |
| | | |
| | |
| | | public void close() |
| | | throws DatabaseException |
| | | { |
| | | // Close each database handle that has been opened. |
| | | for (Database database : databases) |
| | | try |
| | | { |
| | | if (database.getConfig().getDeferredWrite()) |
| | | { |
| | | database.sync(); |
| | | dn2id.close(); |
| | | } |
| | | |
| | | database.close(); |
| | | catch (DatabaseNotFoundException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | 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()) |
| | | { |
| | | 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. |
| | |
| | | { |
| | | // 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. |
| | |
| | | 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. |
| | |
| | | |
| | | // 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. |
| | |
| | | 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); |
| | | } |
| | | |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | { |
| | | 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. |
| | |
| | | } |
| | | |
| | | /** |
| | | * 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.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; |
| | | |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | | * Open the entry database. |
| | | * |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public void open() throws DatabaseException |
| | | { |
| | | 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. |
| | | */ |
| | | private Database getDatabase() throws DatabaseException |
| | | { |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("JE ID2Entry database %s opened with %d records.", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | } |
| | | return database; |
| | | 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; |
| | |
| | | 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. |
| | |
| | | */ |
| | | 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); |
| | |
| | | TRACER.debugInfo( |
| | | rootContainer.getEnvironmentConfig().toString()); |
| | | |
| | | |
| | | rootContainer.openEntryContainers(config.getBaseDNs()); |
| | | |
| | | // 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(); |
| | | |
| | | try |
| | | { |
| | | // Create a temporary work directory. |
| | | File tempDir = new File(config.getImportTempDirectory()); |
| | | tempDir.mkdir(); |
| | | 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()) |
| | |
| | | } |
| | | finally |
| | | { |
| | | rootContainer.close(); |
| | | |
| | | // Sync the environment to disk. |
| | | msgID = MSGID_JEB_IMPORT_CLOSING_DATABASE; |
| | | message = getMessage(msgID); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | reader.close(); |
| | | } |
| | | |
| | |
| | | */ |
| | | 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, |
| | | 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, |
| | | new IndexMergeThread(config, |
| | | ldifImportConfig, |
| | | id2Subtree, |
| | | config.getBackendIndexEntryLimit()); |
| | | mergers.add(indexMergeThread); |
| | |
| | | { |
| | | 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); |
| | | |
| | | if(threads.size() > 0) |
| | | { |
| | | // Wait for the queues to be drained. |
| | | for (ImportContext ic : importMap.values()) |
| | | { |
| | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | finally |
| | |
| | | // 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); |
| | | } |
| | | |
| | | /** |
| | |
| | | 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; |
| | | |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | | * 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(); |
| | | this.dbConfig = dbNodupsConfig; |
| | | this.dbConfig.setOverrideBtreeComparator(true); |
| | | this.dbConfig.setBtreeComparator(comparator.getClass()); |
| | | getDatabase(); |
| | | |
| | | this.state = state; |
| | | |
| | | this.trusted = state.getIndexTrustState(null, this); |
| | | if(!trusted && entryContainer.getEntryCount() <= 0) |
| | | { |
| | | // 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); |
| | | } |
| | | |
| | | /** |
| | | * 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 |
| | | // Issue warning if this index is not trusted |
| | | if(!trusted) |
| | | { |
| | | Database database = threadLocalDatabase.get(); |
| | | if (database == null) |
| | | { |
| | | database = entryContainer.openDatabase(dbConfig, name); |
| | | threadLocalDatabase.set(database); |
| | | int msgID = MSGID_JEB_INDEX_ADD_REQUIRES_REBUILD; |
| | | ErrorLogger.logError(ErrorLogCategory.BACKEND, |
| | | ErrorLogSeverity.NOTICE, msgID, name); |
| | | } |
| | | |
| | | if(debugEnabled()) |
| | | { |
| | | TRACER.debugInfo("JE Index database %s opened with %d records.", |
| | | database.getDatabaseName(), database.count()); |
| | | } |
| | | } |
| | | return database; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @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 |
| | | { |
| | | 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) |
| | | { |
| | | if(trusted) |
| | | { |
| | | return new EntryIDSet(key.getData(), null); |
| | | } |
| | | else |
| | | { |
| | | return new EntryIDSet(); |
| | | } |
| | | } |
| | | return new EntryIDSet(key.getData(), data.getData()); |
| | | } |
| | | catch (DatabaseException e) |
| | |
| | | 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()) |
| | | { |
| | |
| | | // 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. |
| | | // 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(); |
| | | |
| | |
| | | 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(); |
| | | } |
| | | 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(); |
| | | EntryContainer ec = getEntryContainer(baseDN); |
| | | ec.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | ec.close(); |
| | | entryContainers.remove(baseDN); |
| | | } |
| | | finally |
| | | { |
| | | ec.exclusiveLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Close and remove a entry container for a base DN from disk. |
| | |
| | | */ |
| | | public void removeEntryContainer(DN baseDN) throws DatabaseException |
| | | { |
| | | getEntryContainer(baseDN).close(); |
| | | getEntryContainer(baseDN).removeContainer(); |
| | | EntryContainer ec = getEntryContainer(baseDN); |
| | | ec.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | ec.close(); |
| | | ec.removeContainer(); |
| | | entryContainers.remove(baseDN); |
| | | } |
| | | finally |
| | | { |
| | | ec.exclusiveLock.unlock(); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Get the DatabaseEnvironmentMonitor object for JE environment used by this |
| | |
| | | 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.sharedLock.lock(); |
| | | try |
| | | { |
| | | ec.listDatabases(dbList); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | // Sort the list in order of priority. |
| | | Collections.sort(dbList, new DbPreloadComparator()); |
| | |
| | | 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()) |
| | | { |
| | | ec.sharedLock.lock(); |
| | | try |
| | | { |
| | | entryCount += ec.getEntryCount(); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | return entryCount; |
| | | } |
| | |
| | | { |
| | | 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 |
| | | { |
| | | if(env != null) |
| | | { |
| | | // Check if any JE non-mutable properties were changed. |
| | | EnvironmentConfig oldEnvConfig = env.getConfig(); |
| | | EnvironmentConfig newEnvConfig = |
| | |
| | | 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()); |
| | | |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | ArrayList<String> completeList = verifyConfig.getCompleteList(); |
| | | ArrayList<String> cleanList = verifyConfig.getCleanList(); |
| | | |
| | |
| | | verifyDN2ID = true; |
| | | verifyID2Children = true; |
| | | verifyID2Subtree = true; |
| | | Map<AttributeType,IndexConfig> indexMap = config.getIndexConfigMap(); |
| | | for (IndexConfig ic : indexMap.values()) |
| | | { |
| | | AttributeIndex attrIndex = |
| | | entryContainer.getAttributeIndex(ic.getAttributeType()); |
| | | attrIndexList.add(attrIndex); |
| | | } |
| | | attrIndexList.addAll(entryContainer.getAttributeIndexes()); |
| | | } |
| | | else |
| | | { |
| | |
| | | } |
| | | else |
| | | { |
| | | AttributeType attrType = DirectoryServer.getAttributeType(lowerName); |
| | | 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); |
| | | AttributeIndex attrIndex = |
| | | entryContainer.getAttributeIndex(attrType); |
| | | if (attrIndex == null) |
| | | { |
| | | int msgID = MSGID_JEB_ATTRIBUTE_INDEX_NOT_CONFIGURED; |
| | |
| | | |
| | | msgID = MSGID_JEB_VERIFY_ENTRY_LIMIT_STATS_ROW; |
| | | message = getMessage(msgID, index.toString(), values.length, |
| | | values[0], values[values.length-1], medianValue); |
| | | values[0], values[values.length-1], |
| | | medianValue); |
| | | logError(ErrorLogCategory.BACKEND, ErrorLogSeverity.NOTICE, |
| | | message, msgID); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Iterate through the entries in id2entry to perform a check for |
| | |
| | | * @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")); |
| | | 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", |
| | | "testSearchScope"}) |
| | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("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")); |
| | |
| | | |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | presenceIndexer = new PresenceIndexer(index.indexConfig); |
| | | presenceIndexer = new PresenceIndexer(index.getAttributeType()); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer = new EqualityIndexer(index.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer = new SubstringIndexer(index.getAttributeType(), |
| | | index.getConfiguration().getIndexSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer = new OrderingIndexer(index.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | @Test(dependsOnMethods = {"testSearchNotIndexed", "testAdd", "testSearchIndex", "testSearchScope"}) |
| | | public void testReplaceEntry() throws Exception { |
| | |
| | | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("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, |
| | |
| | | index = ec.getAttributeIndex(attribute); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer = new OrderingIndexer(index.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer = new SubstringIndexer(index.getAttributeType(), |
| | | index.getConfiguration().getIndexSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.FALSE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer = new EqualityIndexer(index.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer.indexEntry(null, oldEntry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | |
| | | 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"))); |
| | |
| | | Attribute("employeenumber", "1"))); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | presenceIndexer = new PresenceIndexer(index.indexConfig); |
| | | presenceIndexer = new PresenceIndexer(index.getAttributeType()); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.indexConfig); |
| | | orderingIndexer = new OrderingIndexer(index.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.indexConfig); |
| | | equalityIndexer = new EqualityIndexer(index.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | ConditionResult.TRUE); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.indexConfig); |
| | | substringIndexer = new SubstringIndexer(index.getAttributeType(), |
| | | index.getConfiguration().getIndexSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | finally |
| | | { |
| | | ec.sharedLock.unlock(); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | public void testModifyDN() throws Exception { |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("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")); |
| | |
| | | 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", |
| | | "testSearchScope", "testModifyEntry", "testModifyDN", "testReplaceEntry", |
| | |
| | | |
| | | EntryContainer ec = |
| | | backend.getRootContainer().getEntryContainer(DN.decode("dc=test,dc=com")); |
| | | 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")); |
| | |
| | | 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")); |
| | | |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | EntryID actualHighestID = entryContainer.getHighestEntryID(); |
| | | assertTrue(actualHighestID.equals(new EntryID(0))); |
| | | |
| | |
| | | } |
| | | 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); |
| | | |
| | | |
| | | entryContainer = rootContainer.getEntryContainer(baseDN); |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | assertNotNull(entryContainer); |
| | | |
| | | if(baseDN.toString().equals("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=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"))); |
| | | 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"))); |
| | | |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(baseDN); |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(backendConfig, verifyConfig); |
| | | Entry statEntry=bldStatEntry(""); |
| | | verifyJob.verifyBackend(rootContainer, statEntry); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | be.verifyBackend(verifyConfig, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | } |
| | | else if(baseDN.toString().equals("dc=example1,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=example1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.362,dc=example1,dc=com"))); |
| | | 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); |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(backendConfig, verifyConfig); |
| | | Entry statEntry=bldStatEntry(""); |
| | | verifyJob.verifyBackend(rootContainer, statEntry); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | be.verifyBackend(verifyConfig, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.sharedLock.unlock(); |
| | | |
| | | 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); |
| | | |
| | | entryContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | assertTrue(rejectedEntries.size() <= 0); |
| | | Entry entry = entryContainer.getEntry(DN.decode("uid=user.446,dc=example1,dc=com")); |
| | | Entry entry = entryContainer.getEntry(DN.decode("uid=user.446,dc=importtest1,dc=com")); |
| | | assertNotNull(entry); |
| | | |
| | | AttributeType attribute = entry.getAttribute("cn").get(0).getAttributeType(); |
| | |
| | | assertTrue(entry.hasValue(attribute, null, new AttributeValue(attribute,"Annalee Bogard"))); |
| | | |
| | | VerifyConfig verifyConfig = new VerifyConfig(); |
| | | verifyConfig.setBaseDN(DN.decode("dc=example1,dc=com")); |
| | | verifyConfig.setBaseDN(DN.decode("dc=importtest1,dc=com")); |
| | | |
| | | VerifyJob verifyJob = new VerifyJob(backendConfig, verifyConfig); |
| | | Entry statEntry=bldStatEntry(""); |
| | | verifyJob.verifyBackend(rootContainer, statEntry); |
| | | be=(BackendImpl) DirectoryServer.getBackend(beID); |
| | | be.verifyBackend(verifyConfig, statEntry); |
| | | assertEquals(getStatEntryCount(statEntry, errorCount), 0); |
| | | |
| | | rootContainer.close(); |
| | | } |
| | | 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 |
| | | { |
| | | if(baseDN.toString().equals("dc=importtest,dc=com")) |
| | | { |
| | | 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"))); |
| | | 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=example1,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=example1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.362,dc=example1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.446,dc=importtest1,dc=com"))); |
| | | assertTrue(entryContainer.entryExists(DN.decode("uid=user.362,dc=importtest1,dc=com"))); |
| | | } |
| | | } |
| | | |
| | | rootContainer.close(); |
| | | finally |
| | | { |
| | | entryContainer.sharedLock.unlock(); |
| | | TaskUtils.enableBackend(beID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @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); |
| | | 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, DN.decode(junkDN3), id)); |
| | | performBECleanVerify("dn2id", 5); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Runs clean verify against the id2children index after adding |
| | |
| | | */ |
| | | @Test() public void testCleanID2Children() throws Exception { |
| | | preTest(3); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add malformed key |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | |
| | | id2child.writeKey(txn, key, idSet); |
| | | performBECleanVerify("id2children", 6); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Runs clean verify against the id2subtree index after adding |
| | |
| | | */ |
| | | @Test() public void testCleanID2Subtree() throws Exception { |
| | | preTest(4); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //break key |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | performBECleanVerify("id2subtree", 7); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Runs clean verify against the telephoneNumber.equality index |
| | |
| | | * @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"); |
| | | 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 |
| | |
| | | //point to bad entry added above |
| | | dataBytes[31] = (byte) 0x04; |
| | | DatabaseEntry data= new DatabaseEntry(dataBytes); |
| | | OperationStatus status = EntryContainer.put(db, txn, key, data); |
| | | 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(); |
| | | } |
| | | } |
| | | |
| | | /* |
| | | * Begin complete verify index tests. As described above, these are |
| | |
| | | */ |
| | | @Test() public void testVerifyID2Entry() throws Exception { |
| | | preTest(3); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add entry with short id |
| | | byte[] shortBytes = new byte[3]; |
| | | DatabaseEntry key= new DatabaseEntry(shortBytes); |
| | |
| | | assertTrue(id2entry.putRaw(txn, key1, data1)); |
| | | performBECompleteVerify("telephoneNumber", 3); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * |
| | |
| | | */ |
| | | @Test() public void testVerifyDN2ID() throws Exception { |
| | | preTest(9); |
| | | 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 |
| | |
| | | assertTrue(dn2id.insert(txn, testDN, id)); |
| | | performBECompleteVerify("dn2id", 3); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * |
| | |
| | | */ |
| | | @Test() public void testVerifyID2Children() throws Exception { |
| | | preTest(9); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add dn with no parent |
| | | DatabaseEntry key=addID2EntryReturnKey(noParentDN, 10, false); |
| | | byte[] idBytes=new byte[16]; |
| | |
| | | id2child.writeKey(txn, keyp, idSetp); |
| | | performBECompleteVerify("id2children", 3); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * |
| | |
| | | */ |
| | | @Test() public void testVerifyID2Children1() throws Exception { |
| | | preTest(2); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(pDN, 10, false); |
| | | //add parent key/IDSet with null keyset |
| | |
| | | id2child.writeKey(txn, key, idSetp); |
| | | performBECompleteVerify("id2children", 0); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * |
| | |
| | | @Test |
| | | public void testVerifyID2Subtree() throws Exception { |
| | | preTest(2); |
| | | 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); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | //Add child entry - don't worry about key |
| | | addID2EntryReturnKey(pDN, 3, false); |
| | | //add parent key/IDSet with null keyset |
| | |
| | | id2subtree.writeKey(txn, key, idSet); |
| | | performBECompleteVerify("id2subtree", 1); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Runs complete verify against the mail indexes |
| | |
| | | @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); |
| | | eContainer.sharedLock.lock(); |
| | | try |
| | | { |
| | | AttributeType attributeType = |
| | | DirectoryServer.getAttributeType(mailType); |
| | | //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"); |
| | | 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")); |
| | |
| | | dataBytes[7] = (byte)0xff; |
| | | dataBytes[15] = (byte)0xfe; |
| | | DatabaseEntry data= new DatabaseEntry(dataBytes); |
| | | OperationStatus status = EntryContainer.put(dbEq, txn, key, data); |
| | | OperationStatus status = eqIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | status = EntryContainer.put(dbOr, txn, key, data); |
| | | 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 = EntryContainer.put(dbEq, txn, key, data); |
| | | status = eqIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | status = EntryContainer.put(dbOr, txn, key, data); |
| | | 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 = EntryContainer.put(dbPres, txn, key, data); |
| | | 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 = EntryContainer.put(dbSub, txn, key, data); |
| | | status = subIndex.put(txn, key, data); |
| | | assertTrue(status == OperationStatus.SUCCESS); |
| | | performBECompleteVerify(mailType, 6); |
| | | } |
| | | finally |
| | | { |
| | | eContainer.sharedLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | /* Various tests not either clean or complete */ |
| | | |