| | |
| | | * @throws StorageRuntimeException if a JE database error occurs while |
| | | * opening the index. |
| | | */ |
| | | public void open(WriteableStorage txn) throws StorageRuntimeException |
| | | void open(WriteableStorage txn) throws StorageRuntimeException |
| | | { |
| | | for (Index index : nameToIndexes.values()) |
| | | { |
| | |
| | | |
| | | this.cfg = cfg; |
| | | baseDNs = this.cfg.getBaseDN().toArray(new DN[0]); |
| | | storage = configureStorage(cfg); |
| | | storage = new TracedStorage(configureStorage(cfg), cfg.getBackendId()); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void preloadEntryCache() throws |
| | | UnsupportedOperationException { |
| | | public void preloadEntryCache() throws UnsupportedOperationException |
| | | { |
| | | EntryCachePreloader preloader = new EntryCachePreloader(this); |
| | | preloader.preload(); |
| | | } |
| | |
| | | { |
| | | ByteString key = dnToDNKey(dn, prefixRDNComponents); |
| | | ByteString value = id.toByteString(); |
| | | |
| | | return insert(txn, key, value); |
| | | return txn.putIfAbsent(getName(), key, value); |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | ByteString key = dnToDNKey(dn, prefixRDNComponents); |
| | | |
| | | return delete(txn, key); |
| | | return txn.delete(getName(), key); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param txn A JE database transaction to be used for the database read, or |
| | | * null if none is required. |
| | | * @param dn The DN for which the entry ID is desired. |
| | | * @param isRMW whether the read operation is part of a larger read-modify-write operation |
| | | * @return The entry ID, or null if the given DN is not in the DN database. |
| | | * @throws StorageRuntimeException If an error occurs in the JE database. |
| | | */ |
| | | EntryID get(ReadableStorage txn, DN dn, boolean isRMW) throws StorageRuntimeException |
| | | EntryID get(ReadableStorage txn, DN dn) throws StorageRuntimeException |
| | | { |
| | | ByteString key = dnToDNKey(dn, prefixRDNComponents); |
| | | ByteString value = read(txn, key, isRMW); |
| | | ByteString value = txn.read(getName(), key); |
| | | if (value != null) |
| | | { |
| | | return new EntryID(value); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | EntryID getRMW(ReadableStorage txn, DN dn) throws StorageRuntimeException |
| | | { |
| | | ByteString key = dnToDNKey(dn, prefixRDNComponents); |
| | | ByteString value = txn.getRMW(getName(), key); |
| | | if (value != null) |
| | | { |
| | | return new EntryID(value); |
| | |
| | | { |
| | | ByteString key = toKey(dn); |
| | | |
| | | ByteString oldValue = read(txn, key, true); |
| | | ByteString oldValue = txn.getRMW(getName(), key); |
| | | if (oldValue != null) |
| | | { |
| | | final Pair<DN, List<String>> dnAndUris = decode(oldValue); |
| | | final Collection<String> newUris = dnAndUris.getSecond(); |
| | | if (newUris.addAll(labeledURIs)) |
| | | { |
| | | put(txn, key, encode(dn, newUris)); |
| | | txn.create(getName(), key, encode(dn, newUris)); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | txn.putIfAbsent(treeName, key, encode(dn, labeledURIs)); |
| | | txn.putIfAbsent(getName(), key, encode(dn, labeledURIs)); |
| | | } |
| | | containsReferrals = ConditionResult.TRUE; |
| | | } |
| | |
| | | { |
| | | ByteString key = toKey(dn); |
| | | |
| | | if (delete(txn, key)) |
| | | if (txn.delete(getName(), key)) |
| | | { |
| | | containsReferrals = containsReferrals(txn); |
| | | return true; |
| | |
| | | { |
| | | ByteString key = toKey(dn); |
| | | |
| | | ByteString oldValue = read(txn, key, true); |
| | | ByteString oldValue = txn.getRMW(getName(), key); |
| | | if (oldValue != null) |
| | | { |
| | | final Pair<DN, List<String>> dnAndUris = decode(oldValue); |
| | | final Collection<String> oldUris = dnAndUris.getSecond(); |
| | | if (oldUris.removeAll(labeledURIs)) |
| | | { |
| | | put(txn, key, encode(dn, oldUris)); |
| | | txn.create(getName(), key, encode(dn, oldUris)); |
| | | containsReferrals = containsReferrals(txn); |
| | | return true; |
| | | } |
| | |
| | | */ |
| | | private ConditionResult containsReferrals(ReadableStorage txn) |
| | | { |
| | | Cursor cursor = txn.openCursor(treeName); |
| | | Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | return ConditionResult.valueOf(cursor.next()); |
| | |
| | | |
| | | try |
| | | { |
| | | final Cursor cursor = txn.openCursor(treeName); |
| | | final Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | // Go up through the DIT hierarchy until we find a referral. |
| | |
| | | |
| | | try |
| | | { |
| | | final Cursor cursor = txn.openCursor(treeName); |
| | | final Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | // Initialize the cursor very close to the starting value then |
| | |
| | | |
| | | import java.io.Closeable; |
| | | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.opends.server.backends.pluggable.spi.Cursor; |
| | | import org.opends.server.backends.pluggable.spi.ReadableStorage; |
| | | import org.opends.server.backends.pluggable.spi.Storage; |
| | | import org.opends.server.backends.pluggable.spi.StorageRuntimeException; |
| | | import org.opends.server.backends.pluggable.spi.TreeName; |
| | | import org.opends.server.backends.pluggable.spi.WriteableStorage; |
| | | import org.opends.server.util.ServerConstants; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | /** |
| | | * This class is a wrapper around the JE database object and provides basic |
| | |
| | | */ |
| | | abstract class DatabaseContainer implements Closeable |
| | | { |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | /** The database entryContainer. */ |
| | | final EntryContainer entryContainer; |
| | | |
| | | /** The name of the database within the entryContainer. */ |
| | | TreeName treeName; |
| | | private TreeName name; |
| | | |
| | | /** The reference to the JE Storage. */ |
| | | final Storage storage; |
| | |
| | | { |
| | | this.storage = storage; |
| | | this.entryContainer = entryContainer; |
| | | this.treeName = treeName; |
| | | this.name = treeName; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | void open(WriteableStorage txn) throws StorageRuntimeException |
| | | { |
| | | txn.openTree(treeName); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("JE database %s opened. txn=%s", treeName, txn); |
| | | } |
| | | // FIXME: remove? |
| | | txn.openTree(name); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public synchronized void close() throws StorageRuntimeException |
| | | { |
| | | storage.closeTree(treeName); |
| | | |
| | | if(logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Closed tree %s", treeName); |
| | | } |
| | | } |
| | | |
| | | void put(WriteableStorage txn, ByteSequence key, ByteSequence value) throws StorageRuntimeException |
| | | { |
| | | txn.create(treeName, key, value); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace(messageToLog(true, treeName, txn, key, value)); |
| | | } |
| | | } |
| | | |
| | | ByteString read(ReadableStorage txn, ByteSequence key, boolean isRMW) throws StorageRuntimeException |
| | | { |
| | | ByteString value = isRMW ? txn.getRMW(treeName, key) : txn.read(treeName, key); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace(messageToLog(value != null, treeName, txn, key, value)); |
| | | } |
| | | return value; |
| | | } |
| | | |
| | | /** |
| | | * 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 value The record value. |
| | | * @return {@code true} if the key-value mapping could be inserted, |
| | | * {@code false} if the key was already mapped to another value |
| | | * @throws StorageRuntimeException If an error occurs in the JE operation. |
| | | */ |
| | | boolean insert(WriteableStorage txn, ByteString key, ByteString value) throws StorageRuntimeException |
| | | { |
| | | boolean result = txn.putIfAbsent(treeName, key, value); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace(messageToLog(result, treeName, txn, key, value)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 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 {@code true} if the key mapping was removed, {@code false} otherwise |
| | | * @throws StorageRuntimeException If an error occurs in the JE operation. |
| | | */ |
| | | boolean delete(WriteableStorage txn, ByteSequence key) throws StorageRuntimeException |
| | | { |
| | | final boolean result = txn.delete(treeName, key); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace(messageToLog(result, treeName, txn, key, null)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Open a JE cursor on the JE database. This is a simple wrapper around |
| | | * the JE Database.openCursor method. |
| | | * @param txn A JE database transaction to be used by the cursor, |
| | | * or null if none. |
| | | * @return A JE cursor. |
| | | * @throws StorageRuntimeException If an error occurs while attempting to open |
| | | * the cursor. |
| | | */ |
| | | final Cursor openCursor(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | return txn.openCursor(treeName); |
| | | // FIXME: is this method needed? |
| | | storage.closeTree(name); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | long getRecordCount(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | long count = count(txn); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace(messageToLog(true, treeName, txn, null, null)); |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | private long count(ReadableStorage txn) |
| | | { |
| | | final Cursor cursor = txn.openCursor(treeName); |
| | | /* |
| | | * FIXME: push down to storage. Some DBs have native support for this, e.g. using counted |
| | | * B-Trees. |
| | | */ |
| | | final Cursor cursor = txn.openCursor(name); |
| | | try |
| | | { |
| | | long count = 0; |
| | |
| | | @Override |
| | | public String toString() |
| | | { |
| | | return treeName.toString(); |
| | | return name.toString(); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | final TreeName getName() |
| | | { |
| | | return treeName; |
| | | return name; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | final void setName(TreeName name) |
| | | { |
| | | this.treeName = name; |
| | | this.name = name; |
| | | } |
| | | |
| | | /** Returns the message to log given the provided information. */ |
| | | private String messageToLog(boolean success, TreeName treeName, ReadableStorage txn, ByteSequence key, |
| | | ByteSequence value) |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | builder.append(" ("); |
| | | builder.append(success ? "SUCCESS" : "ERROR"); |
| | | builder.append(")"); |
| | | builder.append(" db="); |
| | | builder.append(treeName); |
| | | builder.append(" txn="); |
| | | builder.append(txn); |
| | | builder.append(ServerConstants.EOL); |
| | | if (key != null) |
| | | { |
| | | builder.append("key:"); |
| | | builder.append(ServerConstants.EOL); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.toByteArray(), 4); |
| | | } |
| | | |
| | | // If the operation was successful we log the same common information |
| | | // plus the data |
| | | if (value != null) |
| | | { |
| | | builder.append("value(len="); |
| | | builder.append(value.length()); |
| | | builder.append("):"); |
| | | builder.append(ServerConstants.EOL); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, value.toByteArray(), 4); |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | } |
| | |
| | | private static final int bytesPerMegabyte = 1024*1024; |
| | | |
| | | /** |
| | | * Constructs the Entry Cache Pre-loader for |
| | | * a given JEB implementation instance. |
| | | * Constructs the Entry Cache Pre-loader for a given JEB implementation instance. |
| | | * |
| | | * @param jeb The JEB instance to pre-load. |
| | | * @param jeb |
| | | * The JEB instance to pre-load. |
| | | */ |
| | | public EntryCachePreloader(BackendImpl<?> jeb) |
| | | { |
| | | // These should not be exposed as configuration |
| | | // parameters and are only useful for testing. |
| | | syncSleepTime = Long.getLong( |
| | | "org.opends.server.entrycache.preload.sleep", |
| | | PRELOAD_DEFAULT_SLEEP_TIME); |
| | | queueCapacity = Integer.getInteger( |
| | | "org.opends.server.entrycache.preload.queue", |
| | | PRELOAD_DEFAULT_QUEUE_CAPACITY); |
| | | entryQueue = |
| | | new LinkedBlockingQueue<PreloadEntry>( |
| | | queueCapacity); |
| | | syncSleepTime = Long.getLong("org.opends.server.entrycache.preload.sleep", PRELOAD_DEFAULT_SLEEP_TIME); |
| | | queueCapacity = Integer.getInteger("org.opends.server.entrycache.preload.queue", PRELOAD_DEFAULT_QUEUE_CAPACITY); |
| | | entryQueue = new LinkedBlockingQueue<PreloadEntry>(queueCapacity); |
| | | this.backend = jeb; |
| | | } |
| | | |
| | |
| | | public void run() { |
| | | Cursor cursor = null; |
| | | ID2Entry id2entry = null; |
| | | Collection<EntryContainer> entryContainers = |
| | | backend.getRootContainer().getEntryContainers(); |
| | | Iterator<EntryContainer> ecIterator = |
| | | entryContainers.iterator(); |
| | | boolean success = true; |
| | | RootContainer rootContainer = backend.getRootContainer(); |
| | | Collection<EntryContainer> entryContainers = rootContainer.getEntryContainers(); |
| | | Iterator<EntryContainer> ecIterator = entryContainers.iterator(); |
| | | |
| | | // FIXME: this loop needs fixing. |
| | | boolean success = false; |
| | | |
| | | try { |
| | | while (success) { |
| | |
| | | break; |
| | | } |
| | | if (id2entry != null) { |
| | | cursor = id2entry.openCursor(null); |
| | | // FIXME: "null" should be a transaction. |
| | | // cursor = null.openCursor(id2entry.getName()); |
| | | } else { |
| | | continue; |
| | | } |
| | |
| | | @Override |
| | | public Long run(ReadableStorage txn) throws Exception |
| | | { |
| | | EntryID entryID = dn2id.get(txn, entryDN, false); |
| | | EntryID entryID = dn2id.get(txn, entryDN); |
| | | if (entryID != null) |
| | | { |
| | | ByteString key = entryID.toByteString(); |
| | |
| | | if (entryIDList.size() > IndexFilter.FILTER_CANDIDATE_THRESHOLD) |
| | | { |
| | | // Read the ID from dn2id. |
| | | EntryID baseID = dn2id.get(txn, aBaseDN, false); |
| | | EntryID baseID = dn2id.get(txn, aBaseDN); |
| | | if (baseID == null) |
| | | { |
| | | LocalizableMessage message = ERR_JEB_SEARCH_NO_SUCH_OBJECT.get(aBaseDN); |
| | |
| | | return cacheEntry; |
| | | } |
| | | |
| | | final Entry entry = id2entry.get(txn, entryID, false); |
| | | final Entry entry = id2entry.get(txn, entryID); |
| | | if (entry != null) |
| | | { |
| | | // Put the entry in the cache making sure not to overwrite a newer copy |
| | |
| | | try |
| | | { |
| | | // Check whether the entry already exists. |
| | | if (dn2id.get(txn, entry.getName(), false) != null) |
| | | if (dn2id.get(txn, entry.getName()) != null) |
| | | { |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, ERR_JEB_ADD_ENTRY_ALREADY_EXISTS.get(entry |
| | | .getName())); |
| | |
| | | dn2uri.targetEntryReferrals(txn, entry.getName(), null); |
| | | |
| | | // Read the parent ID from dn2id. |
| | | parentID = dn2id.get(txn, parentDN, false); |
| | | parentID = dn2id.get(txn, parentDN); |
| | | if (parentID == null) |
| | | { |
| | | LocalizableMessage message = ERR_JEB_ADD_NO_SUCH_OBJECT.get(entry.getName()); |
| | |
| | | for (DN dn = getParentWithinBase(parentDN); dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | // Read the ID from dn2id. |
| | | EntryID nodeID = dn2id.get(txn, dn, false); |
| | | EntryID nodeID = dn2id.get(txn, dn); |
| | | if (nodeID == null) |
| | | { |
| | | throw new StorageRuntimeException(ERR_JEB_MISSING_DN2ID_RECORD.get(dn).toString()); |
| | |
| | | |
| | | int subordinateEntriesDeleted = 0; |
| | | |
| | | Cursor cursor = dn2id.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(dn2id.getName()); |
| | | try |
| | | { |
| | | // Step forward until we pass the ending value. |
| | |
| | | // Invoke any subordinate delete plugins on the entry. |
| | | if (deleteOperation != null && !deleteOperation.isSynchronizationOperation()) |
| | | { |
| | | Entry subordinateEntry = id2entry.get(txn, entryID, false); |
| | | Entry subordinateEntry = id2entry.get(txn, entryID); |
| | | SubordinateDelete pluginResult = |
| | | getPluginConfigManager().invokeSubordinateDeletePlugins(deleteOperation, subordinateEntry); |
| | | |
| | |
| | | { |
| | | leafDNKey = dnToDNKey(targetDN, baseDN.size()); |
| | | } |
| | | ByteString value = dn2id.read(txn, leafDNKey, true); |
| | | ByteString value = txn.getRMW(dn2id.getName(), leafDNKey); |
| | | if (value == null) |
| | | { |
| | | LocalizableMessage message = ERR_JEB_DELETE_NO_SUCH_OBJECT.get(targetDN); |
| | |
| | | } |
| | | |
| | | // Remove from dn2id. |
| | | if (!dn2id.delete(txn, leafDNKey)) |
| | | if (!txn.delete(dn2id.getName(), leafDNKey)) |
| | | { |
| | | // Do not expect to ever come through here. |
| | | LocalizableMessage message = ERR_JEB_DELETE_NO_SUCH_OBJECT.get(leafDNKey); |
| | |
| | | } |
| | | |
| | | // Check that the entry exists in id2entry and read its contents. |
| | | Entry entry = id2entry.get(txn, leafID, true); |
| | | Entry entry = id2entry.getRMW(txn, leafID); |
| | | if (entry == null) |
| | | { |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | |
| | | parentDN = getParentWithinBase(parentDN)) |
| | | { |
| | | // Read the ID from dn2id. |
| | | EntryID parentID = dn2id.get(txn, parentDN, false); |
| | | EntryID parentID = dn2id.get(txn, parentDN); |
| | | if (parentID == null) |
| | | { |
| | | throw new StorageRuntimeException(ERR_JEB_MISSING_DN2ID_RECORD.get(parentDN).toString()); |
| | |
| | | @Override |
| | | public Boolean run(ReadableStorage txn) throws Exception |
| | | { |
| | | EntryID id = dn2id.get(null, entryDN, false); |
| | | EntryID id = dn2id.get(null, entryDN); |
| | | return id != null; |
| | | } |
| | | }); |
| | |
| | | try |
| | | { |
| | | // Read dn2id. |
| | | EntryID entryID = dn2id.get(txn, entryDN, false); |
| | | EntryID entryID = dn2id.get(txn, entryDN); |
| | | if (entryID == null) |
| | | { |
| | | // The entryDN does not exist. |
| | |
| | | } |
| | | |
| | | // Read id2entry. |
| | | final Entry entry = id2entry.get(txn, entryID, false); |
| | | final Entry entry = id2entry.get(txn, entryID); |
| | | if (entry == null) |
| | | { |
| | | // The entryID does not exist. |
| | |
| | | try |
| | | { |
| | | // Read dn2id. |
| | | EntryID entryID = dn2id.get(txn, newEntry.getName(), true); |
| | | EntryID entryID = dn2id.getRMW(txn, newEntry.getName()); |
| | | if (entryID == null) |
| | | { |
| | | // The entry does not exist. |
| | |
| | | try |
| | | { |
| | | // Check whether the renamed entry already exists. |
| | | if (!currentDN.equals(entry.getName()) && dn2id.get(txn, entry.getName(), false) != null) |
| | | if (!currentDN.equals(entry.getName()) && dn2id.get(txn, entry.getName()) != null) |
| | | { |
| | | LocalizableMessage message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(entry.getName()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message); |
| | | } |
| | | |
| | | EntryID oldApexID = dn2id.get(txn, currentDN, false); |
| | | EntryID oldApexID = dn2id.get(txn, currentDN); |
| | | if (oldApexID == null) |
| | | { |
| | | // Check for referral entries above the target entry. |
| | |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, matchedDN, null); |
| | | } |
| | | |
| | | Entry oldApexEntry = id2entry.get(txn, oldApexID, false); |
| | | Entry oldApexEntry = id2entry.get(txn, oldApexID); |
| | | if (oldApexEntry == null) |
| | | { |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), ERR_JEB_MISSING_ID2ENTRY_RECORD |
| | |
| | | * greater than its parent, since search results are returned in |
| | | * ID order. |
| | | */ |
| | | EntryID newSuperiorID = dn2id.get(txn, newSuperiorDN, false); |
| | | EntryID newSuperiorID = dn2id.get(txn, newSuperiorDN); |
| | | if (newSuperiorID == null) |
| | | { |
| | | LocalizableMessage msg = ERR_JEB_NEW_SUPERIOR_NO_SUCH_OBJECT.get(newSuperiorDN); |
| | |
| | | { |
| | | // We have found a subordinate entry. |
| | | EntryID oldID = new EntryID(cursor.getValue()); |
| | | Entry oldEntry = id2entry.get(txn, oldID, false); |
| | | Entry oldEntry = id2entry.get(txn, oldID); |
| | | |
| | | // Construct the new DN of the entry. |
| | | DN newDN = modDN(oldEntry.getName(), currentDN.size(), entry.getName()); |
| | |
| | | for (DN dn = getParentWithinBase(newEntry.getName()); dn != null; |
| | | dn = getParentWithinBase(dn)) |
| | | { |
| | | EntryID parentID = dn2id.get(txn, dn, false); |
| | | EntryID parentID = dn2id.get(txn, dn); |
| | | ByteString parentIDKeyBytes = parentID.toByteString(); |
| | | if(isParent) |
| | | { |
| | |
| | | boolean isParent = true; |
| | | for (DN dn = oldSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | EntryID parentID = dn2id.get(txn, dn, false); |
| | | EntryID parentID = dn2id.get(txn, dn); |
| | | ByteString parentIDKeyBytes = parentID.toByteString(); |
| | | if(isParent) |
| | | { |
| | |
| | | // Remove the old ID from id2subtree of old apex superior entries. |
| | | for (DN dn = oldSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | EntryID parentID = dn2id.get(txn, dn, false); |
| | | EntryID parentID = dn2id.get(txn, dn); |
| | | ByteString parentIDKeyBytes = parentID.toByteString(); |
| | | id2subtree.removeID(buffer, parentIDKeyBytes, oldID); |
| | | } |
| | |
| | | */ |
| | | long getEntryCount(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | final EntryID entryID = dn2id.get(txn, baseDN, false); |
| | | final EntryID entryID = dn2id.get(txn, baseDN); |
| | | if (entryID != null) |
| | | { |
| | | final EntryIDSet entryIDSet = id2subtree.readKey(entryID.toByteString(), txn); |
| | |
| | | |
| | | try |
| | | { |
| | | // Rename in transaction. |
| | | storage.write(new WriteOperation() |
| | | { |
| | | @Override |
| | |
| | | } |
| | | } |
| | | }); |
| | | storage.write(new WriteOperation() |
| | | // Only rename the containers if the txn succeeded. |
| | | for (DatabaseContainer db : databases) |
| | | { |
| | | @Override |
| | | public void run(WriteableStorage txn) throws Exception |
| | | { |
| | | for (DatabaseContainer db : databases) |
| | | { |
| | | TreeName oldName = db.getName(); |
| | | TreeName newName = oldName.replaceBaseDN(newBaseDN); |
| | | db.setName(newName); |
| | | } |
| | | } |
| | | }); |
| | | TreeName oldName = db.getName(); |
| | | TreeName newName = oldName.replaceBaseDN(newBaseDN); |
| | | db.setName(newName); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | try |
| | | { |
| | | ByteString value = codec.encodeInternal(entry, dataConfig); |
| | | return insert(txn, key, value); |
| | | return txn.putIfAbsent(getName(), key, value); |
| | | } |
| | | finally |
| | | { |
| | |
| | | try |
| | | { |
| | | ByteString value = codec.encodeInternal(entry, dataConfig); |
| | | put(txn, key, value); |
| | | txn.create(getName(), key, value); |
| | | } |
| | | finally |
| | | { |
| | |
| | | */ |
| | | boolean remove(WriteableStorage txn, EntryID id) throws StorageRuntimeException |
| | | { |
| | | return delete(txn, id.toByteString()); |
| | | return txn.delete(getName(), id.toByteString()); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @param txn The database transaction or null if none. |
| | | * @param id The desired entry ID which forms the key. |
| | | * @param isRMW whether the read operation is part of a larger read-modify-write operation |
| | | * @return The requested entry, or null if there is no such record. |
| | | * @throws DirectoryException If a problem occurs while getting the entry. |
| | | * @throws StorageRuntimeException If an error occurs in the JE database. |
| | | */ |
| | | public Entry get(ReadableStorage txn, EntryID id, boolean isRMW) |
| | | public Entry get(ReadableStorage txn, EntryID id) |
| | | throws DirectoryException, StorageRuntimeException |
| | | { |
| | | ByteString value = read(txn, id.toByteString(), isRMW); |
| | | return get0(id, txn.read(getName(), id.toByteString())); |
| | | } |
| | | |
| | | public Entry getRMW(ReadableStorage txn, EntryID id) throws DirectoryException, StorageRuntimeException |
| | | { |
| | | return get0(id, txn.getRMW(getName(), id.toByteString())); |
| | | } |
| | | |
| | | private Entry get0(EntryID id, ByteString value) throws DirectoryException |
| | | { |
| | | if (value == null) |
| | | { |
| | | return null; |
| | |
| | | if (entryContainer != null && !suffix.getExcludeBranches().isEmpty()) |
| | | { |
| | | logger.info(NOTE_JEB_IMPORT_MIGRATION_START, "excluded", suffix.getBaseDN()); |
| | | Cursor cursor = entryContainer.getDN2ID().openCursor(txn); |
| | | Cursor cursor = txn.openCursor(entryContainer.getDN2ID().getName()); |
| | | try |
| | | { |
| | | for (DN excludedDN : suffix.getExcludeBranches()) |
| | |
| | | && !importConfiguration.isCancelled() && !isCanceled) |
| | | { |
| | | EntryID id = new EntryID(cursor.getValue()); |
| | | Entry entry = entryContainer.getID2Entry().get(txn, id, false); |
| | | Entry entry = entryContainer.getID2Entry().get(txn, id); |
| | | processEntry(entry, rootContainer.getNextEntryID(), suffix); |
| | | migratedCount++; |
| | | success = cursor.next(); |
| | |
| | | if (entryContainer != null && !suffix.getIncludeBranches().isEmpty()) |
| | | { |
| | | logger.info(NOTE_JEB_IMPORT_MIGRATION_START, "existing", suffix.getBaseDN()); |
| | | Cursor cursor = entryContainer.getDN2ID().openCursor(txn); |
| | | Cursor cursor = txn.openCursor(entryContainer.getDN2ID().getName()); |
| | | try |
| | | { |
| | | final List<ByteString> includeBranches = includeBranchesAsBytes(suffix); |
| | |
| | | if (!includeBranches.contains(key)) |
| | | { |
| | | EntryID id = new EntryID(key); |
| | | Entry entry = entryContainer.getID2Entry().get(txn, id, false); |
| | | Entry entry = entryContainer.getID2Entry().get(txn, id); |
| | | processEntry(entry, rootContainer.getNextEntryID(), suffix); |
| | | migratedCount++; |
| | | success = cursor.next(); |
| | |
| | | { |
| | | DN entryDN = entry.getName(); |
| | | DN2ID dn2id = suffix.getDN2ID(); |
| | | EntryID oldID = dn2id.get(txn, entryDN, false); |
| | | EntryID oldID = dn2id.get(txn, entryDN); |
| | | if (oldID != null) |
| | | { |
| | | oldEntry = suffix.getID2Entry().get(txn, oldID, false); |
| | | oldEntry = suffix.getID2Entry().get(txn, oldID); |
| | | } |
| | | if (oldEntry == null) |
| | | { |
| | |
| | | //the suffixes dn2id DB, then the dn cache is used. |
| | | if (!clearedBackend) |
| | | { |
| | | EntryID id = suffix.getDN2ID().get(txn, entryDN, false); |
| | | EntryID id = suffix.getDN2ID().get(txn, entryDN); |
| | | if (id != null || !tmpEnv.insert(entryDN)) |
| | | { |
| | | reader.rejectEntry(entry, WARN_JEB_IMPORT_ENTRY_EXISTS.get()); |
| | |
| | | if (parentDN != null) |
| | | { |
| | | ByteString key = toByteString(parentDN); |
| | | ByteString value = entryContainer.getDN2ID().read(txn, key, false); |
| | | ByteString value = txn.read(entryContainer.getDN2ID().getName(), key); |
| | | if (value != null) |
| | | { |
| | | parentID = new EntryID(value); |
| | |
| | | return parentIDMap.get(dn); |
| | | } |
| | | ByteString key = toByteString(dn); |
| | | ByteString value = entryContainer.getDN2ID().read(txn, key, false); |
| | | ByteString value = txn.read(entryContainer.getDN2ID().getName(), key); |
| | | return value != null ? new EntryID(value) : null; |
| | | } |
| | | |
| | |
| | | |
| | | public void writeToDB() throws DirectoryException |
| | | { |
| | | entryContainer.getDN2ID().put(txn, dnKey, dnValue); |
| | | txn.create(entryContainer.getDN2ID().getName(), dnKey, dnValue); |
| | | indexMgr.addTotDNCount(1); |
| | | if (parentDN != null) |
| | | { |
| | |
| | | public Void call() throws Exception |
| | | { |
| | | ID2Entry id2entry = entryContainer.getID2Entry(); |
| | | Cursor cursor = id2entry.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(id2entry.getName()); |
| | | try |
| | | { |
| | | while (cursor.next()) |
| | |
| | | final void delete(WriteableStorage txn, ByteSequence key, ImportIDSet importIdSet) |
| | | throws StorageRuntimeException |
| | | { |
| | | ByteString value = read(txn, key, false); |
| | | ByteString value = txn.read(getName(), key); |
| | | if (value != null) { |
| | | newImportIDSet.clear(); |
| | | newImportIDSet.remove(value, importIdSet); |
| | | if (newImportIDSet.isDefined() && newImportIDSet.size() == 0) |
| | | { |
| | | delete(txn, key); |
| | | txn.delete(getName(), key); |
| | | } |
| | | else |
| | | { |
| | | value = newImportIDSet.valueToByteString(); |
| | | put(txn, key, value); |
| | | txn.create(getName(), key, value); |
| | | } |
| | | } else { |
| | | // Should never happen -- the keys should always be there. |
| | |
| | | final void insert(WriteableStorage txn, ByteSequence key, ImportIDSet importIdSet) |
| | | throws StorageRuntimeException |
| | | { |
| | | ByteString value = read(txn, key, false); |
| | | ByteString value = txn.read(getName(), key); |
| | | if(value != null) { |
| | | newImportIDSet.clear(); |
| | | if (newImportIDSet.merge(value, importIdSet)) { |
| | |
| | | } |
| | | value = importIdSet.valueToByteString(); |
| | | } |
| | | put(txn, key, value); |
| | | txn.create(getName(), key, value); |
| | | } |
| | | |
| | | void updateKey(WriteableStorage txn, ByteString key, EntryIDSet deletedIDs, EntryIDSet addedIDs) |
| | |
| | | */ |
| | | if (deletedIDs == null && addedIDs == null) |
| | | { |
| | | boolean success = delete(txn, key); |
| | | boolean success = txn.delete(getName(), key); |
| | | if (success && logger.isTraceEnabled()) |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.toByteArray(), 4); |
| | | logger.trace("The expected key does not exist in the index %s.\nKey:%s ", treeName, builder); |
| | | logger.trace("The expected key does not exist in the index %s.\nKey:%s ", getName(), builder); |
| | | } |
| | | return; |
| | | } |
| | |
| | | * Avoid taking a write lock on a record which has hit all IDs because it is likely to be a |
| | | * point of contention. |
| | | */ |
| | | ByteString value = read(txn, key, false); |
| | | ByteString value = txn.read(getName(), key); |
| | | if (value != null) |
| | | { |
| | | EntryIDSet entryIDList = new EntryIDSet(key, value); |
| | |
| | | |
| | | if ((rebuildRunning || trusted) |
| | | && isNotNullOrEmpty(addedIDs) |
| | | && !insert(txn, key, addedIDs.toByteString())) |
| | | && !txn.putIfAbsent(getName(), key, addedIDs.toByteString())) |
| | | { |
| | | updateKeyWithRMW(txn, key, deletedIDs, addedIDs); |
| | | } |
| | |
| | | private void updateKeyWithRMW(WriteableStorage txn, ByteString key, EntryIDSet deletedIDs, EntryIDSet addedIDs) |
| | | throws StorageRuntimeException |
| | | { |
| | | final ByteString value = read(txn, key, true); |
| | | final ByteString value = txn.getRMW(getName(), key); |
| | | if (value != null) |
| | | { |
| | | EntryIDSet entryIDList = computeEntryIDList(key, value, deletedIDs, addedIDs); |
| | | ByteString after = entryIDList.toByteString(); |
| | | if (!after.isEmpty()) |
| | | { |
| | | put(txn, key, after); |
| | | txn.create(getName(), key, after); |
| | | } |
| | | else |
| | | { |
| | | // 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); |
| | | txn.delete(getName(), key); |
| | | } |
| | | } |
| | | else |
| | |
| | | |
| | | if ((rebuildRunning || trusted) && isNotNullOrEmpty(addedIDs)) |
| | | { |
| | | insert(txn, key, addedIDs.toByteString()); |
| | | txn.putIfAbsent(getName(), key, addedIDs.toByteString()); |
| | | } |
| | | } |
| | | } |
| | |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.toByteArray(), 4); |
| | | logger.trace("Index entry exceeded in index %s. " + |
| | | "Limit: %d. ID list size: %d.\nKey:%s", |
| | | treeName, indexEntryLimit, idCountDelta + addedIDs.size(), builder); |
| | | getName(), indexEntryLimit, idCountDelta + addedIDs.size(), builder); |
| | | |
| | | } |
| | | } |
| | |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | StaticUtils.byteArrayToHexPlusAscii(builder, key.toByteArray(), 4); |
| | | logger.trace("The expected key does not exist in the index %s.\nKey:%s", treeName, builder); |
| | | logger.trace("The expected key does not exist in the index %s.\nKey:%s", getName(), builder); |
| | | } |
| | | |
| | | setTrusted(txn, false); |
| | | logger.error(ERR_JEB_INDEX_CORRUPT_REQUIRES_REBUILD, treeName); |
| | | logger.error(ERR_JEB_INDEX_CORRUPT_REQUIRES_REBUILD, getName()); |
| | | } |
| | | |
| | | void delete(IndexBuffer buffer, ByteString keyBytes) |
| | |
| | | return ConditionResult.UNDEFINED; |
| | | } |
| | | |
| | | ByteString value = read(txn, key, false); |
| | | ByteString value = txn.read(getName(), key); |
| | | if (value != null) |
| | | { |
| | | EntryIDSet entryIDList = new EntryIDSet(key, value); |
| | |
| | | |
| | | try |
| | | { |
| | | ByteString value = read(txn, key, false); |
| | | ByteString value = txn.read(getName(), key); |
| | | if (value == null) |
| | | { |
| | | if(trusted) |
| | |
| | | |
| | | ArrayList<EntryIDSet> lists = new ArrayList<EntryIDSet>(); |
| | | |
| | | Cursor cursor = txn.openCursor(treeName); |
| | | Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | boolean success; |
| | |
| | | } |
| | | |
| | | @Override |
| | | void put(WriteableStorage txn, ByteSequence key, ByteSequence value) throws StorageRuntimeException |
| | | { |
| | | } |
| | | |
| | | @Override |
| | | ByteString read(ReadableStorage txn, ByteSequence key, boolean isRMW) throws StorageRuntimeException |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | boolean insert(WriteableStorage txn, ByteString key, ByteString value) throws StorageRuntimeException |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | boolean delete(WriteableStorage txn, ByteSequence key) throws StorageRuntimeException |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | long getRecordCount(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | return 0; |
| | |
| | | boolean removeIndexTrustState(WriteableStorage txn, DatabaseContainer index) throws StorageRuntimeException |
| | | { |
| | | ByteString key = keyForIndex(index); |
| | | return delete(txn, key); |
| | | return txn.delete(getName(), key); |
| | | } |
| | | |
| | | /** |
| | |
| | | throws StorageRuntimeException |
| | | { |
| | | ByteString key = keyForIndex(index); |
| | | ByteString value = read(txn, key, false); |
| | | ByteString value = txn.read(getName(), key); |
| | | |
| | | return value != null && value.equals(trueBytes); |
| | | } |
| | |
| | | { |
| | | ByteString key = keyForIndex(index); |
| | | |
| | | txn.create(treeName, key, trusted ? trueBytes : falseBytes); |
| | | txn.create(getName(), key, trusted ? trueBytes : falseBytes); |
| | | } |
| | | |
| | | } |
| | |
| | | // or else check the dn2id database for the DN (only if backend wasn't cleared) |
| | | final boolean parentThere = dnCache.contains(dn) |
| | | || (!clearedBackend |
| | | && getDN2ID().get(txn, dn, false) != null); |
| | | && getDN2ID().get(txn, dn) != null); |
| | | //Add the DN to the parent set if needed. |
| | | if (parentThere) { |
| | | synchronized(synchObject) { |
| 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 legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * 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 legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2015 ForgeRock AS |
| | | */ |
| | | package org.opends.server.backends.pluggable; |
| | | |
| | | import java.io.File; |
| | | import java.io.FilenameFilter; |
| | | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.opends.server.backends.pluggable.spi.Cursor; |
| | | import org.opends.server.backends.pluggable.spi.Importer; |
| | | import org.opends.server.backends.pluggable.spi.ReadOperation; |
| | | import org.opends.server.backends.pluggable.spi.ReadableStorage; |
| | | import org.opends.server.backends.pluggable.spi.Storage; |
| | | import org.opends.server.backends.pluggable.spi.StorageRuntimeException; |
| | | import org.opends.server.backends.pluggable.spi.StorageStatus; |
| | | import org.opends.server.backends.pluggable.spi.TreeName; |
| | | import org.opends.server.backends.pluggable.spi.UpdateFunction; |
| | | import org.opends.server.backends.pluggable.spi.WriteOperation; |
| | | import org.opends.server.backends.pluggable.spi.WriteableStorage; |
| | | |
| | | /** |
| | | * Decorates a {@link Storage} with additional trace logging. |
| | | */ |
| | | final class TracedStorage implements Storage |
| | | { |
| | | /** |
| | | * Decorates an {@link Importer} with additional trace logging. |
| | | */ |
| | | private final class TracedImporter implements Importer |
| | | { |
| | | private final Importer importer; |
| | | |
| | | private TracedImporter(final Importer importer) |
| | | { |
| | | this.importer = importer; |
| | | } |
| | | |
| | | @Override |
| | | public void close() |
| | | { |
| | | importer.close(); |
| | | logger.trace("Storage.Importer.close(%s)", backendId); |
| | | } |
| | | |
| | | @Override |
| | | public void createTree(final TreeName name) |
| | | { |
| | | importer.createTree(name); |
| | | logger.trace("Storage.Importer.createTree(%s, %s)", backendId, name); |
| | | } |
| | | |
| | | @Override |
| | | public void put(final TreeName name, final ByteSequence key, final ByteSequence value) |
| | | { |
| | | importer.put(name, key, value); |
| | | logger.trace("Storage.Importer.put(%s, %s, %s, %s)", backendId, name, hex(key), hex(value)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Decorates an {@link ReadableStorage} with additional trace logging. |
| | | */ |
| | | private final class TracedReadableStorage implements ReadableStorage |
| | | { |
| | | private final ReadableStorage txn; |
| | | |
| | | private TracedReadableStorage(final ReadableStorage txn) |
| | | { |
| | | this.txn = txn; |
| | | } |
| | | |
| | | @Override |
| | | public ByteString getRMW(final TreeName name, final ByteSequence key) |
| | | { |
| | | final ByteString value = txn.getRMW(name, key); |
| | | logger.trace("Storage.ReadableStorage.getRMW(%s, %s, %s) = %s", backendId, name, hex(key), hex(value)); |
| | | return value; |
| | | } |
| | | |
| | | @Override |
| | | public Cursor openCursor(final TreeName name) |
| | | { |
| | | final Cursor cursor = txn.openCursor(name); |
| | | logger.trace("Storage.ReadableStorage.openCursor(%s, %s)", backendId, name); |
| | | return cursor; |
| | | } |
| | | |
| | | @Override |
| | | public ByteString read(final TreeName name, final ByteSequence key) |
| | | { |
| | | final ByteString value = txn.read(name, key); |
| | | logger.trace("Storage.ReadableStorage.read(%s, %s, %s) = %s", backendId, name, hex(key), hex(value)); |
| | | return value; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Decorates an {@link WriteableStorage} with additional trace logging. |
| | | */ |
| | | private final class TracedWriteableStorage implements WriteableStorage |
| | | { |
| | | private final WriteableStorage txn; |
| | | |
| | | private TracedWriteableStorage(final WriteableStorage txn) |
| | | { |
| | | this.txn = txn; |
| | | } |
| | | |
| | | @Override |
| | | public void create(final TreeName name, final ByteSequence key, final ByteSequence value) |
| | | { |
| | | txn.create(name, key, value); |
| | | logger.trace("Storage.WriteableStorage.create(%s, %s, %s, %s)", backendId, name, hex(key), hex(value)); |
| | | } |
| | | |
| | | @Override |
| | | public boolean delete(final TreeName name, final ByteSequence key) |
| | | { |
| | | final boolean isDeleted = txn.delete(name, key); |
| | | logger.trace("Storage.WriteableStorage.delete(%s, %s, %s) = %s", backendId, name, hex(key), isDeleted); |
| | | return isDeleted; |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void deleteTree(final TreeName name) |
| | | { |
| | | txn.deleteTree(name); |
| | | logger.trace("Storage.WriteableStorage.deleteTree(%s, %s)", backendId, name); |
| | | } |
| | | |
| | | @Override |
| | | public ByteString getRMW(final TreeName name, final ByteSequence key) |
| | | { |
| | | final ByteString value = txn.getRMW(name, key); |
| | | logger.trace("Storage.WriteableStorage.getRMW(%s, %s, %s) = %s", backendId, name, hex(key), hex(value)); |
| | | return value; |
| | | } |
| | | |
| | | @Override |
| | | public Cursor openCursor(final TreeName name) |
| | | { |
| | | final Cursor cursor = txn.openCursor(name); |
| | | logger.trace("Storage.WriteableStorage.openCursor(%s, %s)", backendId, name); |
| | | return cursor; |
| | | } |
| | | |
| | | @Override |
| | | public void openTree(final TreeName name) |
| | | { |
| | | txn.openTree(name); |
| | | logger.trace("Storage.WriteableStorage.openTree(%s, %s)", backendId, name); |
| | | } |
| | | |
| | | @Override |
| | | public boolean putIfAbsent(final TreeName name, final ByteSequence key, final ByteSequence value) |
| | | { |
| | | final boolean isCreated = txn.putIfAbsent(name, key, value); |
| | | logger.trace("Storage.WriteableStorage.putIfAbsent(%s, %s, %s, %s) = %s", backendId, name, hex(key), hex(value), |
| | | isCreated); |
| | | return isCreated; |
| | | } |
| | | |
| | | @Override |
| | | public ByteString read(final TreeName name, final ByteSequence key) |
| | | { |
| | | final ByteString value = txn.read(name, key); |
| | | logger.trace("Storage.WriteableStorage.read(%s, %s, %s) = %s", backendId, name, hex(key), hex(value)); |
| | | return value; |
| | | } |
| | | |
| | | @Override |
| | | public void renameTree(final TreeName oldName, final TreeName newName) |
| | | { |
| | | txn.renameTree(oldName, newName); |
| | | logger.trace("Storage.WriteableStorage.renameTree(%s, %s, %s)", backendId, oldName, newName); |
| | | } |
| | | |
| | | @Override |
| | | public void truncateTree(final TreeName name) |
| | | { |
| | | txn.truncateTree(name); |
| | | logger.trace("Storage.WriteableStorage.truncateTree(%s, %s)", backendId, name); |
| | | } |
| | | |
| | | @Override |
| | | public boolean update(final TreeName name, final ByteSequence key, final UpdateFunction f) |
| | | { |
| | | final boolean isUpdated = txn.update(name, key, f); |
| | | logger.trace("Storage.WriteableStorage.update(%s, %s, %s, %s) = %s", backendId, name, hex(key), f, isUpdated); |
| | | return isUpdated; |
| | | } |
| | | } |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | private final String backendId; |
| | | private final Storage storage; |
| | | |
| | | TracedStorage(final Storage storage, final String backendId) |
| | | { |
| | | this.storage = storage; |
| | | this.backendId = backendId; |
| | | } |
| | | |
| | | @Override |
| | | public void close() |
| | | { |
| | | storage.close(); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Storage.close(%s)", backendId); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void closeTree(final TreeName name) |
| | | { |
| | | storage.closeTree(name); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Storage.closeTree(%s, %s)", backendId, name); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public File getDirectory() |
| | | { |
| | | return storage.getDirectory(); |
| | | } |
| | | |
| | | @Override |
| | | public FilenameFilter getFilesToBackupFilter() |
| | | { |
| | | return storage.getFilesToBackupFilter(); |
| | | } |
| | | |
| | | @Override |
| | | public StorageStatus getStorageStatus() |
| | | { |
| | | return storage.getStorageStatus(); |
| | | } |
| | | |
| | | @Override |
| | | public void open() throws Exception |
| | | { |
| | | storage.open(); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Opened storage for backend %s", backendId); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public <T> T read(final ReadOperation<T> readOperation) throws Exception |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | return storage.read(new ReadOperation<T>() |
| | | { |
| | | @Override |
| | | public T run(final ReadableStorage txn) throws Exception |
| | | { |
| | | return readOperation.run(new TracedReadableStorage(txn)); |
| | | } |
| | | }); |
| | | } |
| | | return storage.read(readOperation); |
| | | } |
| | | |
| | | @Override |
| | | public void removeStorageFiles() throws StorageRuntimeException |
| | | { |
| | | storage.removeStorageFiles(); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Storage.removeStorageFiles(%s)", backendId); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Importer startImport() throws Exception |
| | | { |
| | | final Importer importer = storage.startImport(); |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | logger.trace("Storage.startImport(%s)", backendId); |
| | | return new TracedImporter(importer); |
| | | } |
| | | return importer; |
| | | } |
| | | |
| | | @Override |
| | | public boolean supportsBackupAndRestore() |
| | | { |
| | | return storage.supportsBackupAndRestore(); |
| | | } |
| | | |
| | | @Override |
| | | public void write(final WriteOperation writeOperation) throws Exception |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | | storage.write(new WriteOperation() |
| | | { |
| | | @Override |
| | | public void run(final WriteableStorage txn) throws Exception |
| | | { |
| | | writeOperation.run(new TracedWriteableStorage(txn)); |
| | | } |
| | | }); |
| | | } |
| | | storage.write(writeOperation); |
| | | } |
| | | |
| | | private String hex(final ByteSequence bytes) |
| | | { |
| | | return bytes.toByteString().toHexString(); |
| | | } |
| | | } |
| | |
| | | catch(Exception e) |
| | | { |
| | | LocalizableMessage msg = ERR_JEB_CONFIG_VLV_INDEX_BAD_FILTER.get( |
| | | config.getFilter(), treeName, stackTraceToSingleLineString(e)); |
| | | config.getFilter(), getName(), stackTraceToSingleLineString(e)); |
| | | throw new ConfigException(msg); |
| | | } |
| | | |
| | |
| | | catch (Exception e) |
| | | { |
| | | throw new ConfigException(ERR_JEB_CONFIG_VLV_INDEX_UNDEFINED_ATTR.get( |
| | | sortKeys[i], treeName)); |
| | | sortKeys[i], getName())); |
| | | } |
| | | |
| | | AttributeType attrType = DirectoryServer.getAttributeType(sortAttrs[i] |
| | |
| | | if (attrType == null) |
| | | { |
| | | LocalizableMessage msg = ERR_JEB_CONFIG_VLV_INDEX_UNDEFINED_ATTR.get( |
| | | sortAttrs[i], treeName); |
| | | sortAttrs[i], getName()); |
| | | throw new ConfigException(msg); |
| | | } |
| | | sortKeys[i] = new SortKey(attrType, ascending); |
| | |
| | | { |
| | | super.open(txn); |
| | | |
| | | final Cursor cursor = txn.openCursor(treeName); |
| | | final Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | while (cursor.next()) |
| | |
| | | |
| | | private SortValuesSet getSortValuesSet(ReadableStorage txn, ByteString key, boolean isRMW) |
| | | { |
| | | ByteString value = isRMW ? txn.getRMW(treeName, key) : txn.read(treeName, key); |
| | | ByteString value = isRMW ? txn.getRMW(getName(), key) : txn.read(getName(), key); |
| | | if (value == null) |
| | | { |
| | | // There are no records in the database |
| | |
| | | if(newSize >= sortedSetCapacity) |
| | | { |
| | | SortValuesSet splitSortValuesSet = sortValuesSet.split(newSize / 2); |
| | | put(txn, splitSortValuesSet.getKeyBytes(), splitSortValuesSet.toByteString()); // splitAfter |
| | | put(txn, sortValuesSet.getKeyBytes(), sortValuesSet.toByteString()); // after |
| | | txn.create(getName(), splitSortValuesSet.getKeyBytes(), splitSortValuesSet.toByteString()); // splitAfter |
| | | txn.create(getName(), sortValuesSet.getKeyBytes(), sortValuesSet.toByteString()); // after |
| | | |
| | | if(logger.isTraceEnabled()) |
| | | { |
| | |
| | | } |
| | | else if(newSize == 0) |
| | | { |
| | | delete(txn, key); |
| | | txn.delete(getName(), key); |
| | | } |
| | | else |
| | | { |
| | | ByteString after = sortValuesSet.toByteString(); |
| | | put(txn, key, after); |
| | | txn.create(getName(), key, after); |
| | | } |
| | | |
| | | count.getAndAdd(newSize - oldSize); |
| | |
| | | { |
| | | debugBuilder.append("vlv="); |
| | | debugBuilder.append("[INDEX:"); |
| | | debugBuilder.append(treeName.getIndexId()); |
| | | debugBuilder.append(getName().getIndexId()); |
| | | debugBuilder.append("]"); |
| | | } |
| | | |
| | |
| | | int count = 1 + beforeCount + afterCount; |
| | | selectedIDs = new long[count]; |
| | | |
| | | Cursor cursor = txn.openCursor(treeName); |
| | | Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | //Locate the set that contains the target entry. |
| | |
| | | int includedAfterCount = 0; |
| | | LinkedList<EntryID> idList = new LinkedList<EntryID>(); |
| | | |
| | | Cursor cursor = openCursor(txn); |
| | | Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | ByteSequence vBytes = vlvRequest.getGreaterThanOrEqualAssertion(); |
| | |
| | | LinkedList<long[]> idSets = new LinkedList<long[]>(); |
| | | int currentCount = 0; |
| | | |
| | | Cursor cursor = openCursor(txn); |
| | | Cursor cursor = txn.openCursor(getName()); |
| | | try |
| | | { |
| | | while (cursor.next()) |
| | |
| | | catch(Exception e) |
| | | { |
| | | LocalizableMessage msg = ERR_JEB_CONFIG_VLV_INDEX_BAD_FILTER.get( |
| | | cfg.getFilter(), treeName, |
| | | cfg.getFilter(), getName(), |
| | | stackTraceToSingleLineString(e)); |
| | | unacceptableReasons.add(msg); |
| | | return false; |
| | |
| | | catch(Exception e) |
| | | { |
| | | ccr.addMessage(ERR_JEB_CONFIG_VLV_INDEX_BAD_FILTER.get( |
| | | config.getFilter(), treeName, stackTraceToSingleLineString(e))); |
| | | config.getFilter(), getName(), stackTraceToSingleLineString(e))); |
| | | ccr.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | } |
| | | } |
| | |
| | | ccr.addMessage(e.getMessageObject()); |
| | | ccr.setResultCode(ResultCode.INVALID_ATTRIBUTE_SYNTAX); |
| | | } |
| | | |
| | | // We have to close the database and open it using the new comparator. |
| | | entryContainer.exclusiveLock.lock(); |
| | | try |
| | | { |
| | | close(); |
| | | open(txn); |
| | | } |
| | | catch (StorageRuntimeException de) |
| | | { |
| | | ccr.addMessage(LocalizableMessage.raw(StaticUtils.stackTraceToSingleLineString(de))); |
| | | ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); |
| | | } |
| | | finally |
| | | { |
| | | entryContainer.exclusiveLock.unlock(); |
| | | } |
| | | |
| | | ccr.setAdminActionRequired(true); |
| | | } |
| | | |
| | |
| | | if (ccr.adminActionRequired()) |
| | | { |
| | | trusted = false; |
| | | ccr.addMessage(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(treeName)); |
| | | ccr.addMessage(NOTE_JEB_INDEX_ADD_REQUIRES_REBUILD.get(getName())); |
| | | try |
| | | { |
| | | state.putIndexTrustState(txn, this, false); |
| | |
| | | */ |
| | | private void iterateID2Entry(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | Cursor cursor = id2entry.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(id2entry.getName()); |
| | | try |
| | | { |
| | | long storedEntryCount = id2entry.getRecordCount(txn); |
| | |
| | | */ |
| | | private void iterateDN2ID(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | Cursor cursor = dn2id.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(dn2id.getName()); |
| | | try |
| | | { |
| | | while (cursor.next()) |
| | |
| | | Entry entry; |
| | | try |
| | | { |
| | | entry = id2entry.get(txn, entryID, false); |
| | | entry = id2entry.get(txn, entryID); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | */ |
| | | private void iterateID2Children(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | Cursor cursor = id2c.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(id2c.getName()); |
| | | try |
| | | { |
| | | while (cursor.next()) |
| | |
| | | Entry entry; |
| | | try |
| | | { |
| | | entry = id2entry.get(txn, entryID, false); |
| | | entry = id2entry.get(txn, entryID); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | Entry childEntry; |
| | | try |
| | | { |
| | | childEntry = id2entry.get(txn, id, false); |
| | | childEntry = id2entry.get(txn, id); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | */ |
| | | private void iterateID2Subtree(ReadableStorage txn) throws StorageRuntimeException |
| | | { |
| | | Cursor cursor = id2s.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(id2s.getName()); |
| | | try |
| | | { |
| | | while (cursor.next()) |
| | |
| | | Entry entry; |
| | | try |
| | | { |
| | | entry = id2entry.get(txn, entryID, false); |
| | | entry = id2entry.get(txn, entryID); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | Entry subordEntry; |
| | | try |
| | | { |
| | | subordEntry = id2entry.get(txn, id, false); |
| | | subordEntry = id2entry.get(txn, id); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | return; |
| | | } |
| | | |
| | | Cursor cursor = vlvIndex.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(vlvIndex.getName()); |
| | | try |
| | | { |
| | | SortValues lastValues = null; |
| | |
| | | EntryID id = new EntryID(values.getEntryID()); |
| | | try |
| | | { |
| | | entry = id2entry.get(txn, id, false); |
| | | entry = id2entry.get(txn, id); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | return; |
| | | } |
| | | |
| | | Cursor cursor = index.openCursor(txn); |
| | | Cursor cursor = txn.openCursor(index.getName()); |
| | | try |
| | | { |
| | | while (cursor.next()) |
| | |
| | | Entry entry; |
| | | try |
| | | { |
| | | entry = id2entry.get(txn, id, false); |
| | | entry = id2entry.get(txn, id); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | // Check the ID is in dn2id with the correct DN. |
| | | try |
| | | { |
| | | EntryID id = dn2id.get(txn, dn, false); |
| | | EntryID id = dn2id.get(txn, dn); |
| | | if (id == null) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | |
| | | { |
| | | try |
| | | { |
| | | EntryID id = dn2id.get(txn, parentDN, false); |
| | | EntryID id = dn2id.get(txn, parentDN); |
| | | if (id == null) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | |
| | | EntryID parentID = null; |
| | | try |
| | | { |
| | | parentID = dn2id.get(txn, parentDN, false); |
| | | parentID = dn2id.get(txn, parentDN); |
| | | if (parentID == null) |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | |
| | | EntryID id = null; |
| | | try |
| | | { |
| | | id = dn2id.get(txn, dn, false); |
| | | id = dn2id.get(txn, dn); |
| | | if (id == null) |
| | | { |
| | | if (logger.isTraceEnabled()) |