| | |
| | | */ |
| | | private HashMap<String, VLVIndex> vlvIndexMap; |
| | | |
| | | /** |
| | | * Cached value from config so they don't have to be retrieved per operation. |
| | | */ |
| | | |
| | | private int subtreeDeleteSizeLimit; |
| | | |
| | | private int subtreeDeleteBatchSize; |
| | | |
| | | private String databasePrefix; |
| | | /** |
| | | * This class is responsible for managing the configuraiton for attribute |
| | |
| | | public boolean isConfigurationAddAcceptable( |
| | | LocalDBVLVIndexCfg cfg, List<Message> unacceptableReasons) |
| | | { |
| | | SearchFilter filter; |
| | | try |
| | | { |
| | | filter = |
| | | SearchFilter.createFilterFromString(cfg.getFilter()); |
| | | SearchFilter.createFilterFromString(cfg.getFilter()); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | |
| | | } |
| | | this.databasePrefix = builder.toString(); |
| | | |
| | | this.subtreeDeleteSizeLimit = config.getSubtreeDeleteSizeLimit(); |
| | | this.subtreeDeleteBatchSize = config.getSubtreeDeleteBatchSize(); |
| | | |
| | | // Instantiate the attribute indexes. |
| | | attrIndexMap = new HashMap<AttributeType, AttributeIndex>(); |
| | | |
| | |
| | | * If a problem occurs while processing the |
| | | * search. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws JebException If an error occurs in the JE database. |
| | | * @throws CanceledOperationException if this operation should be cancelled. |
| | | */ |
| | | public void search(SearchOperation searchOperation) |
| | | throws DirectoryException, DatabaseException, JebException |
| | | throws DirectoryException, DatabaseException, CanceledOperationException |
| | | { |
| | | DN baseDN = searchOperation.getBaseDN(); |
| | | SearchScope searchScope = searchOperation.getScope(); |
| | |
| | | */ |
| | | private void searchNotIndexed(SearchOperation searchOperation, |
| | | PagedResultsControl pageRequest) |
| | | throws DirectoryException |
| | | throws DirectoryException, CanceledOperationException |
| | | { |
| | | EntryCache<?> entryCache = DirectoryServer.getEntryCache(); |
| | | DN baseDN = searchOperation.getBaseDN(); |
| | |
| | | |
| | | if (isInScope) |
| | | { |
| | | Entry entry = null; |
| | | Entry cacheEntry = null; |
| | | Entry entry; |
| | | Entry cacheEntry; |
| | | |
| | | // Try the entry cache first. Note no need to take a lock. |
| | | lockList.clear(); |
| | |
| | | |
| | | if (cacheEntry == null) |
| | | { |
| | | GetEntryByIDOperation operation = |
| | | new GetEntryByIDOperation(entryID); |
| | | |
| | | // Fetch the candidate entry from the database. |
| | | this.invokeTransactedOperation(operation); |
| | | entry = operation.getEntry(); |
| | | entry = id2entry.get(null, entryID, LockMode.DEFAULT); |
| | | } |
| | | else |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | searchOperation.checkIfCanceled(false); |
| | | |
| | | // Move to the next record. |
| | | status = cursor.getNext(key, data, LockMode.DEFAULT); |
| | | } |
| | |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | catch (JebException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | if (pageRequest != null) |
| | | { |
| | |
| | | boolean candidatesAreInScope, |
| | | SearchOperation searchOperation, |
| | | PagedResultsControl pageRequest) |
| | | throws DirectoryException |
| | | throws DirectoryException, CanceledOperationException |
| | | { |
| | | EntryCache<?> entryCache = DirectoryServer.getEntryCache(); |
| | | SearchScope searchScope = searchOperation.getScope(); |
| | |
| | | while (iterator.hasNext()) |
| | | { |
| | | EntryID id = iterator.next(); |
| | | Entry entry = null; |
| | | Entry cacheEntry = null; |
| | | Entry entry; |
| | | Entry cacheEntry; |
| | | |
| | | // Try the entry cache first. Note no need to take a lock. |
| | | lockList.clear(); |
| | |
| | | { |
| | | if (cacheEntry == null) |
| | | { |
| | | GetEntryByIDOperation operation = new GetEntryByIDOperation(id); |
| | | |
| | | // Fetch the candidate entry from the database. |
| | | try |
| | | { |
| | | this.invokeTransactedOperation(operation); |
| | | entry = operation.getEntry(); |
| | | entry = id2entry.get(null, id, LockMode.DEFAULT); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | searchOperation.checkIfCanceled(false); |
| | | } |
| | | } |
| | | |
| | |
| | | * @throws DirectoryException If a problem occurs while trying to add the |
| | | * entry. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | * @throws CanceledOperationException if this operation should be cancelled. |
| | | */ |
| | | public void addEntry(Entry entry, AddOperation addOperation) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, CanceledOperationException |
| | | { |
| | | TransactedOperation operation = |
| | | new AddEntryTransaction(entry); |
| | | Transaction txn = beginTransaction(); |
| | | DN parentDN = getParentWithinBase(entry.getDN()); |
| | | |
| | | invokeTransactedOperation(operation); |
| | | } |
| | | |
| | | /** |
| | | * This method is common to all operations invoked under a database |
| | | * transaction. It retries the operation if the transaction is |
| | | * aborted due to a deadlock condition, up to a configured maximum |
| | | * number of retries. |
| | | * |
| | | * @param operation An object implementing the TransactedOperation interface. |
| | | * @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. |
| | | */ |
| | | private void invokeTransactedOperation(TransactedOperation operation) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | // Attempt the operation under a transaction until it fails or completes. |
| | | boolean completed = false; |
| | | while (!completed) |
| | | { |
| | | // Start a transaction. |
| | | Transaction txn = operation.beginOperationTransaction(); |
| | | |
| | | try |
| | | { |
| | | // Invoke the operation. |
| | | operation.invokeOperation(txn); |
| | | |
| | | // Commit the transaction. |
| | | EntryContainer.transactionCommit(txn); |
| | | completed = true; |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw databaseException; |
| | | } |
| | | catch (DirectoryException directoryException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw directoryException; |
| | | } |
| | | catch (JebException jebException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw jebException; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | |
| | | String msg = e.getMessage(); |
| | | if (msg == null) |
| | | { |
| | | msg = stackTraceToSingleLineString(e); |
| | | } |
| | | Message message = ERR_JEB_UNCHECKED_EXCEPTION.get(msg); |
| | | throw new JebException(message, e); |
| | | } |
| | | } |
| | | |
| | | // Do any actions necessary after successful commit, |
| | | // usually to update the entry cache. |
| | | operation.postCommitAction(); |
| | | } |
| | | |
| | | /** |
| | | * This interface represents any kind of operation on the database |
| | | * that must be performed under a transaction. A class which implements |
| | | * this interface does not need to be concerned with creating the |
| | | * transaction nor retrying the transaction after deadlock. |
| | | */ |
| | | private interface TransactedOperation |
| | | { |
| | | /** |
| | | * Begin a transaction for this operation. |
| | | * |
| | | * @return The transaction for the operation, or null if the operation |
| | | * will not use a transaction. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public abstract Transaction beginOperationTransaction() |
| | | throws DatabaseException; |
| | | |
| | | /** |
| | | * Invoke the operation under the given transaction. |
| | | * |
| | | * @param txn The transaction to be used to perform the operation. |
| | | * @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. |
| | | */ |
| | | public abstract void invokeOperation(Transaction txn) |
| | | throws DatabaseException, DirectoryException, JebException; |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | | * committed. |
| | | */ |
| | | public abstract void postCommitAction(); |
| | | } |
| | | |
| | | /** |
| | | * This inner class implements the Add Entry operation through |
| | | * the TransactedOperation interface. |
| | | */ |
| | | private class AddEntryTransaction implements TransactedOperation |
| | | { |
| | | /** |
| | | * The entry to be added. |
| | | */ |
| | | private Entry entry; |
| | | |
| | | /** |
| | | * The DN of the superior entry of the entry to be added. This can be |
| | | * null if the entry to be added is a base entry. |
| | | */ |
| | | DN parentDN; |
| | | |
| | | /** |
| | | * The ID of the entry once it has been assigned. |
| | | */ |
| | | EntryID entryID = null; |
| | | |
| | | /** |
| | | * Begin a transaction for this operation. |
| | | * |
| | | * @return The transaction for the operation, or null if the operation |
| | | * will not use a transaction. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Transaction beginOperationTransaction() throws DatabaseException |
| | | { |
| | | return beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | | * Create a new Add Entry Transaction. |
| | | * @param entry The entry to be added. |
| | | */ |
| | | public AddEntryTransaction(Entry entry) |
| | | { |
| | | this.entry = entry; |
| | | this.parentDN = getParentWithinBase(entry.getDN()); |
| | | } |
| | | |
| | | /** |
| | | * Invoke the operation under the given transaction. |
| | | * |
| | | * @param txn The transaction to be used to perform the operation. |
| | | * @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. |
| | | */ |
| | | public void invokeOperation(Transaction txn) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | try |
| | | { |
| | | // Check whether the entry already exists. |
| | | if (dn2id.get(txn, entry.getDN(), LockMode.DEFAULT) != null) |
| | |
| | | Message message = |
| | | ERR_JEB_ADD_ENTRY_ALREADY_EXISTS.get(entry.getDN().toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | message); |
| | | } |
| | | |
| | | // Check that the parent entry exists. |
| | |
| | | if (parentID == null) |
| | | { |
| | | Message message = ERR_JEB_ADD_NO_SUCH_OBJECT.get( |
| | | entry.getDN().toString()); |
| | | entry.getDN().toString()); |
| | | DN matchedDN = getMatchedDN(baseDN); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, |
| | | message, matchedDN, null); |
| | | } |
| | | } |
| | | |
| | | // First time through, assign the next entryID. |
| | | if (entryID == null) |
| | | { |
| | | entryID = rootContainer.getNextEntryID(); |
| | | } |
| | | EntryID entryID = rootContainer.getNextEntryID(); |
| | | |
| | | // Insert into dn2id. |
| | | if (!dn2id.insert(txn, entry.getDN(), entryID)) |
| | |
| | | Message message = |
| | | ERR_JEB_ADD_ENTRY_ALREADY_EXISTS.get(entry.getDN().toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | message); |
| | | } |
| | | |
| | | // Update the referral database for referral entries. |
| | |
| | | Message message = |
| | | ERR_JEB_ADD_ENTRY_ALREADY_EXISTS.get(entry.getDN().toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | message); |
| | | } |
| | | |
| | | // Insert into id2entry. |
| | |
| | | Message message = |
| | | ERR_JEB_ADD_ENTRY_ALREADY_EXISTS.get(entry.getDN().toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | message); |
| | | } |
| | | |
| | | // Insert into the indexes, in index configuration order. |
| | |
| | | } |
| | | } |
| | | |
| | | } |
| | | if(addOperation != null) |
| | | { |
| | | // One last check before committing |
| | | addOperation.checkIfCanceled(true); |
| | | } |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | | * committed. |
| | | */ |
| | | public void postCommitAction() |
| | | { |
| | | // Commit the transaction. |
| | | EntryContainer.transactionCommit(txn); |
| | | |
| | | // Update the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | |
| | | entryCache.putEntry(entry, backend, entryID.longValue()); |
| | | } |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw databaseException; |
| | | } |
| | | catch (DirectoryException directoryException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw directoryException; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | |
| | | String msg = e.getMessage(); |
| | | if (msg == null) |
| | | { |
| | | msg = stackTraceToSingleLineString(e); |
| | | } |
| | | Message message = ERR_JEB_UNCHECKED_EXCEPTION.get(msg); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | * @throws DirectoryException If a problem occurs while trying to remove the |
| | | * entry. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | * @throws CanceledOperationException if this operation should be cancelled. |
| | | */ |
| | | public void deleteEntry(DN entryDN, DeleteOperation deleteOperation) |
| | | throws DirectoryException, DatabaseException, JebException |
| | | throws DirectoryException, DatabaseException, CanceledOperationException |
| | | { |
| | | DeleteEntryTransaction operation = |
| | | new DeleteEntryTransaction(entryDN, deleteOperation); |
| | | boolean isComplete = false; |
| | | while(!isComplete) |
| | | { |
| | | invokeTransactedOperation(operation); |
| | | Transaction txn = beginTransaction(); |
| | | IndexBuffer indexBuffer = null; |
| | | |
| | | if (operation.adminSizeLimitExceeded()) |
| | | { |
| | | Message message = NOTE_JEB_SUBTREE_DELETE_SIZE_LIMIT_EXCEEDED.get( |
| | | operation.getDeletedEntryCount()); |
| | | throw new DirectoryException( |
| | | ResultCode.ADMIN_LIMIT_EXCEEDED, |
| | | message); |
| | | } |
| | | if(operation.batchSizeExceeded()) |
| | | { |
| | | operation.resetBatchSize(); |
| | | continue; |
| | | } |
| | | isComplete = true; |
| | | Message message = |
| | | NOTE_JEB_DELETED_ENTRY_COUNT.get(operation.getDeletedEntryCount()); |
| | | MessageBuilder errorMessage = new MessageBuilder(); |
| | | errorMessage.append(message); |
| | | deleteOperation.setErrorMessage(errorMessage); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This inner class implements the Delete Entry operation through |
| | | * the TransactedOperation interface. |
| | | */ |
| | | private class DeleteEntryTransaction implements TransactedOperation |
| | | { |
| | | /** |
| | | * The DN of the entry or subtree to be deleted. |
| | | */ |
| | | private DN entryDN; |
| | | |
| | | /** |
| | | * The Delete operation. |
| | | */ |
| | | private DeleteOperation deleteOperation; |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the subtree delete size limit has been exceeded. |
| | | */ |
| | | private boolean adminSizeLimitExceeded = false; |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the subtree delete batch size has been exceeded. |
| | | */ |
| | | private boolean batchSizeExceeded = false; |
| | | |
| | | |
| | | /** |
| | | * Indicates the total count of deleted DNs in the Delete Operation. |
| | | */ |
| | | private int totalDeletedDN; |
| | | |
| | | /** |
| | | * Indicates the batch count of deleted DNs in the Delete Operation. |
| | | */ |
| | | private int batchDeletedDN; |
| | | |
| | | /** |
| | | * The index buffer used to buffer up the index changes. |
| | | */ |
| | | private IndexBuffer indexBuffer = null; |
| | | |
| | | /** |
| | | * Create a new Delete Entry Transaction. |
| | | * @param entryDN The entry or subtree to be deleted. |
| | | * @param deleteOperation The Delete operation. |
| | | */ |
| | | public DeleteEntryTransaction(DN entryDN, DeleteOperation deleteOperation) |
| | | { |
| | | this.entryDN = entryDN; |
| | | this.deleteOperation = deleteOperation; |
| | | } |
| | | |
| | | /** |
| | | * Determine whether the subtree delete size limit has been exceeded. |
| | | * @return true if the size limit has been exceeded. |
| | | */ |
| | | public boolean adminSizeLimitExceeded() |
| | | { |
| | | return adminSizeLimitExceeded; |
| | | } |
| | | |
| | | /** |
| | | * Determine whether the subtree delete batch size has been exceeded. |
| | | * @return true if the batch size has been exceeded. |
| | | */ |
| | | public boolean batchSizeExceeded() |
| | | { |
| | | return batchSizeExceeded; |
| | | } |
| | | |
| | | /** |
| | | * Resets the batchSizeExceeded parameter to reuse the object |
| | | * for multiple batches. |
| | | */ |
| | | public void resetBatchSize() |
| | | { |
| | | batchSizeExceeded=false; |
| | | batchDeletedDN = 0; |
| | | } |
| | | |
| | | /** |
| | | * Get the number of entries deleted during the operation. |
| | | * @return The number of entries deleted. |
| | | */ |
| | | public int getDeletedEntryCount() |
| | | { |
| | | return totalDeletedDN; |
| | | } |
| | | |
| | | /** |
| | | * Begin a transaction for this operation. |
| | | * |
| | | * @return The transaction for the operation, or null if the operation |
| | | * will not use a transaction. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Transaction beginOperationTransaction() throws DatabaseException |
| | | { |
| | | Transaction txn = beginTransaction(); |
| | | return txn; |
| | | } |
| | | |
| | | /** |
| | | * Invoke the operation under the given transaction. |
| | | * |
| | | * @param txn The transaction to be used to perform the operation. |
| | | * @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. |
| | | */ |
| | | public void invokeOperation(Transaction txn) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | try |
| | | { |
| | | // Check for referral entries above the target entry. |
| | | dn2uri.targetEntryReferrals(entryDN, null); |
| | | |
| | | // Determine whether this is a subtree delete. |
| | | boolean isSubtreeDelete = false; |
| | | List<Control> controls = deleteOperation.getRequestControls(); |
| | | if (controls != null) |
| | | |
| | | if(deleteOperation != null) |
| | | { |
| | | for (Control control : controls) |
| | | List<Control> controls = deleteOperation.getRequestControls(); |
| | | if (controls != null) |
| | | { |
| | | if (control.getOID().equals(OID_SUBTREE_DELETE_CONTROL)) |
| | | for (Control control : controls) |
| | | { |
| | | isSubtreeDelete = true; |
| | | if (control.getOID().equals(OID_SUBTREE_DELETE_CONTROL)) |
| | | { |
| | | isSubtreeDelete = true; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* |
| | | * We will iterate backwards through a range of the dn2id keys to |
| | | * find subordinates of the target entry from the bottom of the tree |
| | | * upwards. For example, any subordinates of "dc=example,dc=com" appear |
| | | * in dn2id with a key ending in ",dc=example,dc=com". The entry |
| | | * "cn=joe,ou=people,dc=example,dc=com" will appear after the entry |
| | | * "ou=people,dc=example,dc=com". |
| | | */ |
| | | * We will iterate backwards through a range of the dn2id keys to |
| | | * find subordinates of the target entry from the bottom of the tree |
| | | * upwards. For example, any subordinates of "dc=example,dc=com" appear |
| | | * in dn2id with a key ending in ",dc=example,dc=com". The entry |
| | | * "cn=joe,ou=people,dc=example,dc=com" will appear after the entry |
| | | * "ou=people,dc=example,dc=com". |
| | | */ |
| | | byte[] suffix = StaticUtils.getBytes("," + entryDN.toNormalizedString()); |
| | | |
| | | /* |
| | | * Set the starting value to a value of equal length but slightly |
| | | * greater than the target DN. Since keys are compared in |
| | | * reverse order we must set the first byte (the comma). |
| | | * No possibility of overflow here. |
| | | */ |
| | | * Set the starting value to a value of equal length but slightly |
| | | * greater than the target DN. Since keys are compared in |
| | | * reverse order we must set the first byte (the comma). |
| | | * No possibility of overflow here. |
| | | */ |
| | | byte[] begin = suffix.clone(); |
| | | begin[0] = (byte) (begin[0] + 1); |
| | | |
| | | // Set the ending value to the suffix. |
| | | byte[] end = suffix; |
| | | int subordinateEntriesDeleted = 0; |
| | | |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | DatabaseEntry key = new DatabaseEntry(begin); |
| | |
| | | // Step back until we pass the ending value. |
| | | while (status == OperationStatus.SUCCESS) |
| | | { |
| | | int cmp = dn2id.getComparator().compare(key.getData(), end); |
| | | int cmp = dn2id.getComparator().compare(key.getData(), suffix); |
| | | if (cmp < 0) |
| | | { |
| | | // We have gone past the ending value. |
| | |
| | | { |
| | | // The subtree delete control was not specified and |
| | | // the target entry is not a leaf. |
| | | |
| | | Message message = |
| | | ERR_JEB_DELETE_NOT_ALLOWED_ON_NONLEAF.get(entryDN.toString()); |
| | | throw new DirectoryException(ResultCode.NOT_ALLOWED_ON_NONLEAF, |
| | | message); |
| | | message); |
| | | } |
| | | |
| | | // Enforce any subtree delete size limit. |
| | | if (subtreeDeleteSizeLimit > 0 && |
| | | totalDeletedDN >= subtreeDeleteSizeLimit) |
| | | { |
| | | adminSizeLimitExceeded = true; |
| | | break; |
| | | } |
| | | |
| | | // Enforce any subtree delete batch size. |
| | | if (subtreeDeleteBatchSize > 0 && |
| | | batchDeletedDN >= subtreeDeleteBatchSize) |
| | | { |
| | | batchSizeExceeded = true; |
| | | break; |
| | | } |
| | | |
| | | // This is a subtree delete so crate a index buffer |
| | | // This is a subtree delete so create a index buffer |
| | | // if it there isn't one. |
| | | if(indexBuffer == null) |
| | | { |
| | |
| | | } |
| | | |
| | | /* |
| | | * Delete this entry which by now must be a leaf because |
| | | * we have been deleting from the bottom of the tree upwards. |
| | | */ |
| | | * Delete this entry which by now must be a leaf because |
| | | * we have been deleting from the bottom of the tree upwards. |
| | | */ |
| | | EntryID entryID = new EntryID(data); |
| | | DN subordinateDN = DN.decode(new ASN1OctetString(key.getData())); |
| | | deleteEntry(txn, true, entryDN, subordinateDN, entryID); |
| | | deleteEntry(txn, indexBuffer, true, entryDN, subordinateDN, entryID); |
| | | subordinateEntriesDeleted++; |
| | | |
| | | batchDeletedDN++; |
| | | totalDeletedDN++; |
| | | if(deleteOperation != null) |
| | | { |
| | | deleteOperation.checkIfCanceled(false); |
| | | } |
| | | |
| | | status = cursor.getPrev(key, data, LockMode.DEFAULT); |
| | | } |
| | | } |
| | |
| | | cursor.close(); |
| | | } |
| | | |
| | | // Finally delete the target entry as it was not included |
| | | // in the dn2id iteration. |
| | | if (!adminSizeLimitExceeded && !batchSizeExceeded) |
| | | { |
| | | // Enforce any subtree delete size limit. |
| | | if (subtreeDeleteSizeLimit > 0 && |
| | | totalDeletedDN >= subtreeDeleteSizeLimit) |
| | | { |
| | | adminSizeLimitExceeded = true; |
| | | } |
| | | else if (subtreeDeleteBatchSize > 0 && |
| | | batchDeletedDN >= subtreeDeleteBatchSize) |
| | | { |
| | | batchSizeExceeded = true; |
| | | } |
| | | else |
| | | { |
| | | // draft-armijo-ldap-treedelete, 4.1 Tree Delete Semantics: |
| | | // The server MUST NOT chase referrals stored in the tree. If |
| | | // information about referrals is stored in this section of the |
| | | // tree, this pointer will be deleted. |
| | | deleteEntry(txn, |
| | | isSubtreeDelete || isManageDsaITOperation(deleteOperation), |
| | | entryDN, null, null); |
| | | // draft-armijo-ldap-treedelete, 4.1 Tree Delete Semantics: |
| | | // The server MUST NOT chase referrals stored in the tree. If |
| | | // information about referrals is stored in this section of the |
| | | // tree, this pointer will be deleted. |
| | | deleteEntry(txn, indexBuffer, |
| | | isSubtreeDelete || isManageDsaITOperation(deleteOperation), |
| | | entryDN, null, null); |
| | | |
| | | batchDeletedDN++; |
| | | totalDeletedDN++; |
| | | } |
| | | } |
| | | |
| | | if(indexBuffer != null) |
| | | { |
| | | indexBuffer.flush(txn); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Delete an entry with appropriate handling of referral entries. |
| | | * The caller must be sure that the entry is indeed a leaf. We cannot |
| | | * rely on id2children to check for children since this entry may at |
| | | * one time have had enough children to exceed the index entry limit, |
| | | * after which the number of children IDs is unknown. |
| | | * |
| | | * @param txn The database transaction. |
| | | * @param manageDsaIT Whether it is an manage DSA IT operation. |
| | | * @param targetDN The DN of the target entry. |
| | | * @param leafDN The DN of the leaf entry to be deleted. |
| | | * @param leafID The ID of the leaf entry. |
| | | * @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. |
| | | */ |
| | | private void deleteEntry(Transaction txn, |
| | | boolean manageDsaIT, |
| | | DN targetDN, |
| | | DN leafDN, |
| | | EntryID leafID) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | if(leafID == null || leafDN == null) |
| | | |
| | | if(deleteOperation != null) |
| | | { |
| | | // Read the entry ID from dn2id. |
| | | leafDN = targetDN; |
| | | leafID = dn2id.get(txn, leafDN, LockMode.RMW); |
| | | if (leafID == null) |
| | | { |
| | | Message message = |
| | | ERR_JEB_DELETE_NO_SUCH_OBJECT.get(leafDN.toString()); |
| | | DN matchedDN = getMatchedDN(baseDN); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, |
| | | message, matchedDN, null); |
| | | } |
| | | // One last check before committing |
| | | deleteOperation.checkIfCanceled(true); |
| | | } |
| | | |
| | | // Remove from dn2id. |
| | | if (!dn2id.remove(txn, leafDN)) |
| | | // Commit the transaction. |
| | | EntryContainer.transactionCommit(txn); |
| | | |
| | | if(isSubtreeDelete) |
| | | { |
| | | // Do not expect to ever come through here. |
| | | Message message = ERR_JEB_DELETE_NO_SUCH_OBJECT.get(leafDN.toString()); |
| | | deleteOperation.appendAdditionalLogMessage( |
| | | NOTE_JEB_DELETED_ENTRY_COUNT.get(subordinateEntriesDeleted + 1)); |
| | | } |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw databaseException; |
| | | } |
| | | catch (DirectoryException directoryException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw directoryException; |
| | | } |
| | | catch (CanceledOperationException coe) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw coe; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | |
| | | String msg = e.getMessage(); |
| | | if (msg == null) |
| | | { |
| | | msg = stackTraceToSingleLineString(e); |
| | | } |
| | | Message message = ERR_JEB_UNCHECKED_EXCEPTION.get(msg); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | } |
| | | |
| | | private void deleteEntry(Transaction txn, |
| | | IndexBuffer indexBuffer, |
| | | boolean manageDsaIT, |
| | | DN targetDN, |
| | | DN leafDN, |
| | | EntryID leafID) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | if(leafID == null || leafDN == null) |
| | | { |
| | | // Read the entry ID from dn2id. |
| | | leafDN = targetDN; |
| | | leafID = dn2id.get(txn, leafDN, LockMode.RMW); |
| | | if (leafID == null) |
| | | { |
| | | Message message = |
| | | ERR_JEB_DELETE_NO_SUCH_OBJECT.get(leafDN.toString()); |
| | | DN matchedDN = getMatchedDN(baseDN); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, |
| | | message, matchedDN, null); |
| | | } |
| | | } |
| | | |
| | | // Check that the entry exists in id2entry and read its contents. |
| | | Entry entry = id2entry.get(txn, leafID, LockMode.RMW); |
| | | if (entry == null) |
| | | // Remove from dn2id. |
| | | if (!dn2id.remove(txn, leafDN)) |
| | | { |
| | | // Do not expect to ever come through here. |
| | | Message message = ERR_JEB_DELETE_NO_SUCH_OBJECT.get(leafDN.toString()); |
| | | DN matchedDN = getMatchedDN(baseDN); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, |
| | | message, matchedDN, null); |
| | | } |
| | | |
| | | // Check that the entry exists in id2entry and read its contents. |
| | | Entry entry = id2entry.get(txn, leafID, LockMode.RMW); |
| | | if (entry == null) |
| | | { |
| | | Message msg = ERR_JEB_MISSING_ID2ENTRY_RECORD.get(leafID.toString()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | msg); |
| | | } |
| | | |
| | | if (!manageDsaIT) |
| | | { |
| | | dn2uri.checkTargetForReferral(entry, null); |
| | | } |
| | | |
| | | // Update the referral database. |
| | | dn2uri.deleteEntry(txn, entry); |
| | | |
| | | // Remove from id2entry. |
| | | if (!id2entry.remove(txn, leafID)) |
| | | { |
| | | Message msg = ERR_JEB_MISSING_ID2ENTRY_RECORD.get(leafID.toString()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | msg); |
| | | } |
| | | |
| | | // Remove from the indexes, in index config order. |
| | | if(indexBuffer != null) |
| | | { |
| | | indexRemoveEntry(indexBuffer, entry, leafID); |
| | | } |
| | | else |
| | | { |
| | | indexRemoveEntry(txn, entry, leafID); |
| | | } |
| | | |
| | | // Remove the id2c and id2s records for this entry. |
| | | if(indexBuffer != null) |
| | | { |
| | | byte[] leafIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(leafID.longValue()); |
| | | id2children.delete(indexBuffer, leafIDKeyBytes); |
| | | id2subtree.delete(indexBuffer, leafIDKeyBytes); |
| | | } |
| | | else |
| | | { |
| | | DatabaseEntry leafIDKey = leafID.getDatabaseEntry(); |
| | | id2children.delete(txn, leafIDKey); |
| | | id2subtree.delete(txn, leafIDKey); |
| | | } |
| | | |
| | | // Iterate up through the superior entries from the target entry. |
| | | boolean isParent = true; |
| | | for (DN parentDN = getParentWithinBase(targetDN); parentDN != null; |
| | | parentDN = getParentWithinBase(parentDN)) |
| | | { |
| | | // Read the ID from dn2id. |
| | | EntryID parentID = dn2id.get(txn, parentDN, LockMode.DEFAULT); |
| | | if (parentID == null) |
| | | { |
| | | Message msg = ERR_JEB_MISSING_ID2ENTRY_RECORD.get(leafID.toString()); |
| | | Message msg = |
| | | ERR_JEB_MISSING_DN2ID_RECORD.get(parentDN.toNormalizedString()); |
| | | throw new JebException(msg); |
| | | } |
| | | |
| | | if (!manageDsaIT) |
| | | { |
| | | dn2uri.checkTargetForReferral(entry, null); |
| | | } |
| | | |
| | | // Update the referral database. |
| | | dn2uri.deleteEntry(txn, entry); |
| | | |
| | | // Remove from id2entry. |
| | | if (!id2entry.remove(txn, leafID)) |
| | | { |
| | | Message msg = ERR_JEB_MISSING_ID2ENTRY_RECORD.get(leafID.toString()); |
| | | throw new JebException(msg); |
| | | } |
| | | |
| | | // Remove from the indexes, in index config order. |
| | | if(indexBuffer != null) |
| | | { |
| | | indexRemoveEntry(indexBuffer, entry, leafID); |
| | | byte[] parentIDBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | // Remove from id2children. |
| | | if (isParent) |
| | | { |
| | | id2children.removeID(indexBuffer, parentIDBytes, leafID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.removeID(indexBuffer, parentIDBytes, leafID); |
| | | } |
| | | else |
| | | { |
| | | indexRemoveEntry(txn, entry, leafID); |
| | | } |
| | | |
| | | // Remove the id2c and id2s records for this entry. |
| | | if(indexBuffer != null) |
| | | { |
| | | byte[] leafIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(leafID.longValue()); |
| | | id2children.delete(indexBuffer, leafIDKeyBytes); |
| | | id2subtree.delete(indexBuffer, leafIDKeyBytes); |
| | | } |
| | | else |
| | | { |
| | | DatabaseEntry leafIDKey = leafID.getDatabaseEntry(); |
| | | id2children.delete(txn, leafIDKey); |
| | | id2subtree.delete(txn, leafIDKey); |
| | | } |
| | | |
| | | // Iterate up through the superior entries from the target entry. |
| | | boolean isParent = true; |
| | | for (DN parentDN = getParentWithinBase(targetDN); parentDN != null; |
| | | parentDN = getParentWithinBase(parentDN)) |
| | | { |
| | | // Read the ID from dn2id. |
| | | EntryID parentID = dn2id.get(txn, parentDN, LockMode.DEFAULT); |
| | | if (parentID == null) |
| | | DatabaseEntry nodeIDData = parentID.getDatabaseEntry(); |
| | | // Remove from id2children. |
| | | if(isParent) |
| | | { |
| | | Message msg = |
| | | ERR_JEB_MISSING_DN2ID_RECORD.get(parentDN.toNormalizedString()); |
| | | throw new JebException(msg); |
| | | id2children.removeID(txn, nodeIDData, leafID); |
| | | isParent = false; |
| | | } |
| | | |
| | | if(indexBuffer != null) |
| | | { |
| | | byte[] parentIDBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | // Remove from id2children. |
| | | if (isParent) |
| | | { |
| | | id2children.removeID(indexBuffer, parentIDBytes, leafID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.removeID(indexBuffer, parentIDBytes, leafID); |
| | | } |
| | | else |
| | | { |
| | | DatabaseEntry nodeIDData = parentID.getDatabaseEntry(); |
| | | // Remove from id2children. |
| | | if(isParent) |
| | | { |
| | | id2children.removeID(txn, nodeIDData, leafID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.removeID(txn, nodeIDData, leafID); |
| | | } |
| | | } |
| | | |
| | | // Remove the entry from the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | | { |
| | | entryCache.removeEntry(leafDN); |
| | | id2subtree.removeID(txn, nodeIDData, leafID); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | | * committed. |
| | | */ |
| | | public void postCommitAction() |
| | | // Remove the entry from the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | | { |
| | | |
| | | entryCache.removeEntry(leafDN); |
| | | } |
| | | } |
| | | |
| | |
| | | * exist. |
| | | * @throws DirectoryException If a problem occurs while trying to retrieve |
| | | * the entry. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | * @throws DatabaseException An error occurred during a database operation. |
| | | */ |
| | | public Entry getEntry(DN entryDN) |
| | | throws JebException, DatabaseException, DirectoryException |
| | | throws DatabaseException, DirectoryException |
| | | { |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | Entry entry = null; |
| | |
| | | |
| | | if (entry == null) |
| | | { |
| | | GetEntryByDNOperation operation = new GetEntryByDNOperation(entryDN); |
| | | |
| | | // Fetch the entry from the database. |
| | | invokeTransactedOperation(operation); |
| | | |
| | | entry = operation.getEntry(); |
| | | |
| | | // Put the entry in the cache making sure not to overwrite |
| | | // a newer copy that may have been inserted since the time |
| | | // we read the cache. |
| | | if (entry != null && entryCache != null) |
| | | { |
| | | entryCache.putEntryIfAbsent(entry, backend, |
| | | operation.getEntryID().longValue()); |
| | | } |
| | | } |
| | | |
| | | return entry; |
| | | } |
| | | |
| | | /** |
| | | * This inner class gets an entry by DN through |
| | | * the TransactedOperation interface. |
| | | */ |
| | | private class GetEntryByDNOperation implements TransactedOperation |
| | | { |
| | | /** |
| | | * The retrieved entry. |
| | | */ |
| | | private Entry entry = null; |
| | | |
| | | /** |
| | | * The ID of the retrieved entry. |
| | | */ |
| | | private EntryID entryID = null; |
| | | |
| | | /** |
| | | * The DN of the entry to be retrieved. |
| | | */ |
| | | DN entryDN; |
| | | |
| | | /** |
| | | * Create a new transacted operation to retrieve an entry by DN. |
| | | * @param entryDN The DN of the entry to be retrieved. |
| | | */ |
| | | public GetEntryByDNOperation(DN entryDN) |
| | | { |
| | | this.entryDN = entryDN; |
| | | } |
| | | |
| | | /** |
| | | * Get the retrieved entry. |
| | | * @return The retrieved entry. |
| | | */ |
| | | public Entry getEntry() |
| | | { |
| | | return entry; |
| | | } |
| | | |
| | | /** |
| | | * Get the ID of the retrieved entry. |
| | | * @return The ID of the retrieved entry. |
| | | */ |
| | | public EntryID getEntryID() |
| | | { |
| | | return entryID; |
| | | } |
| | | |
| | | /** |
| | | * Begin a transaction for this operation. |
| | | * |
| | | * @return The transaction for the operation, or null if the operation |
| | | * will not use a transaction. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Transaction beginOperationTransaction() throws DatabaseException |
| | | { |
| | | // For best performance queries do not use a transaction. |
| | | // We permit temporary inconsistencies between the multiple |
| | | // records that make up a single entry. |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * Invoke the operation under the given transaction. |
| | | * |
| | | * @param txn The transaction to be used to perform the operation |
| | | * @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. |
| | | */ |
| | | public void invokeOperation(Transaction txn) throws DatabaseException, |
| | | DirectoryException, |
| | | JebException |
| | | { |
| | | // Read dn2id. |
| | | entryID = dn2id.get(txn, entryDN, LockMode.DEFAULT); |
| | | EntryID entryID = dn2id.get(null, entryDN, LockMode.DEFAULT); |
| | | if (entryID == null) |
| | | { |
| | | // The entryDN does not exist. |
| | |
| | | // Check for referral entries above the target entry. |
| | | dn2uri.targetEntryReferrals(entryDN, null); |
| | | |
| | | return; |
| | | return null; |
| | | } |
| | | |
| | | // Read id2entry. |
| | | entry = id2entry.get(txn, entryID, LockMode.DEFAULT); |
| | | entry = id2entry.get(null, entryID, LockMode.DEFAULT); |
| | | |
| | | if (entry == null) |
| | | { |
| | | // The entryID does not exist. |
| | | Message msg = ERR_JEB_MISSING_ID2ENTRY_RECORD.get(entryID.toString()); |
| | | throw new JebException(msg); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | msg); |
| | | } |
| | | |
| | | // Put the entry in the cache making sure not to overwrite |
| | | // a newer copy that may have been inserted since the time |
| | | // we read the cache. |
| | | if (entryCache != null) |
| | | { |
| | | entryCache.putEntryIfAbsent(entry, backend, entryID.longValue()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | | * committed. |
| | | */ |
| | | public void postCommitAction() |
| | | { |
| | | // No implementation required. |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This inner class gets an entry by ID through |
| | | * the TransactedOperation interface. |
| | | */ |
| | | private class GetEntryByIDOperation implements TransactedOperation |
| | | { |
| | | /** |
| | | * The retrieved entry. |
| | | */ |
| | | private Entry entry = null; |
| | | |
| | | /** |
| | | * The ID of the entry to be retrieved. |
| | | */ |
| | | private EntryID entryID; |
| | | |
| | | /** |
| | | * Create a new transacted operation to retrieve an entry by ID. |
| | | * @param entryID The ID of the entry to be retrieved. |
| | | */ |
| | | public GetEntryByIDOperation(EntryID entryID) |
| | | { |
| | | this.entryID = entryID; |
| | | } |
| | | |
| | | /** |
| | | * Get the retrieved entry. |
| | | * @return The retrieved entry. |
| | | */ |
| | | public Entry getEntry() |
| | | { |
| | | return entry; |
| | | } |
| | | |
| | | /** |
| | | * Get the ID of the retrieved entry. |
| | | * @return the ID of the retrieved entry. |
| | | */ |
| | | public EntryID getEntryID() |
| | | { |
| | | return entryID; |
| | | } |
| | | |
| | | /** |
| | | * Begin a transaction for this operation. |
| | | * |
| | | * @return The transaction for the operation, or null if the operation |
| | | * will not use a transaction. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Transaction beginOperationTransaction() throws DatabaseException |
| | | { |
| | | // For best performance queries do not use a transaction. |
| | | // We permit temporary inconsistencies between the multiple |
| | | // records that make up a single entry. |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * Invoke the operation under the given transaction. |
| | | * |
| | | * @param txn The transaction to be used to perform the operation. |
| | | * @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. |
| | | */ |
| | | public void invokeOperation(Transaction txn) throws DatabaseException, |
| | | DirectoryException, |
| | | JebException |
| | | { |
| | | // Read id2entry. |
| | | entry = id2entry.get(txn, entryID, LockMode.DEFAULT); |
| | | } |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | | * committed. |
| | | */ |
| | | public void postCommitAction() |
| | | { |
| | | // No implementation required. |
| | | } |
| | | return entry; |
| | | } |
| | | |
| | | /** |
| | |
| | | * modifications performed internally. |
| | | * @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. |
| | | * @throws CanceledOperationException if this operation should be cancelled. |
| | | */ |
| | | public void replaceEntry(Entry entry, ModifyOperation modifyOperation) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException, CanceledOperationException |
| | | { |
| | | TransactedOperation operation = |
| | | new ReplaceEntryTransaction(entry, modifyOperation); |
| | | Transaction txn = beginTransaction(); |
| | | |
| | | invokeTransactedOperation(operation); |
| | | } |
| | | |
| | | /** |
| | | * This inner class implements the Replace Entry operation through |
| | | * the TransactedOperation interface. |
| | | */ |
| | | private class ReplaceEntryTransaction implements TransactedOperation |
| | | { |
| | | /** |
| | | * The new contents of the entry. |
| | | */ |
| | | private Entry entry; |
| | | |
| | | /** |
| | | * The Modify operation, or null if the replace is not due to a Modify |
| | | * operation. |
| | | */ |
| | | private ModifyOperation modifyOperation; |
| | | |
| | | /** |
| | | * The ID of the entry that was replaced. |
| | | */ |
| | | private EntryID entryID = null; |
| | | |
| | | /** |
| | | * Create a new transacted operation to replace an entry. |
| | | * @param entry The new contents of the entry. |
| | | * @param modifyOperation The Modify operation, or null if the replace is |
| | | * not due to a Modify operation. |
| | | */ |
| | | public ReplaceEntryTransaction(Entry entry, |
| | | ModifyOperation modifyOperation) |
| | | { |
| | | this.entry = entry; |
| | | this.modifyOperation = modifyOperation; |
| | | } |
| | | |
| | | /** |
| | | * Begin a transaction for this operation. |
| | | * |
| | | * @return The transaction for the operation, or null if the operation |
| | | * will not use a transaction. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Transaction beginOperationTransaction() throws DatabaseException |
| | | { |
| | | return beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | | * Invoke the operation under the given transaction. |
| | | * |
| | | * @param txn The transaction to be used to perform the operation. |
| | | * @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. |
| | | */ |
| | | public void invokeOperation(Transaction txn) throws DatabaseException, |
| | | DirectoryException, |
| | | JebException |
| | | try |
| | | { |
| | | // Read dn2id. |
| | | entryID = dn2id.get(txn, entry.getDN(), LockMode.RMW); |
| | | EntryID entryID = dn2id.get(txn, entry.getDN(), LockMode.RMW); |
| | | if (entryID == null) |
| | | { |
| | | // The entry does not exist. |
| | | Message message = |
| | | ERR_JEB_MODIFY_NO_SUCH_OBJECT.get(entry.getDN().toString()); |
| | | ERR_JEB_MODIFY_NO_SUCH_OBJECT.get(entry.getDN().toString()); |
| | | DN matchedDN = getMatchedDN(baseDN); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, |
| | | message, matchedDN, null); |
| | |
| | | { |
| | | // The entry does not exist. |
| | | Message msg = ERR_JEB_MISSING_ID2ENTRY_RECORD.get(entryID.toString()); |
| | | throw new JebException(msg); |
| | | throw new DirectoryException( |
| | | DirectoryServer.getServerErrorResultCode(), msg); |
| | | } |
| | | |
| | | if (!isManageDsaITOperation(modifyOperation)) |
| | |
| | | indexRemoveEntry(txn, originalEntry, entryID); |
| | | indexInsertEntry(txn, entry, entryID); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | | * committed. |
| | | */ |
| | | public void postCommitAction() |
| | | { |
| | | if(modifyOperation != null) |
| | | { |
| | | // One last check before committing |
| | | modifyOperation.checkIfCanceled(true); |
| | | } |
| | | |
| | | // Commit the transaction. |
| | | EntryContainer.transactionCommit(txn); |
| | | |
| | | // Update the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | |
| | | entryCache.putEntry(entry, backend, entryID.longValue()); |
| | | } |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw databaseException; |
| | | } |
| | | catch (DirectoryException directoryException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw directoryException; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | |
| | | String msg = e.getMessage(); |
| | | if (msg == null) |
| | | { |
| | | msg = stackTraceToSingleLineString(e); |
| | | } |
| | | Message message = ERR_JEB_UNCHECKED_EXCEPTION.get(msg); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | * to a request to cancel or abandon the |
| | | * modify DN operation. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | */ |
| | | public void renameEntry(DN currentDN, Entry entry, |
| | | ModifyDNOperation modifyDNOperation) |
| | | throws DatabaseException, JebException, DirectoryException, |
| | | CanceledOperationException { |
| | | TransactedOperation operation = |
| | | new RenameEntryTransaction(currentDN, entry, modifyDNOperation); |
| | | |
| | | invokeTransactedOperation(operation); |
| | | } |
| | | |
| | | /** |
| | | * This inner class implements the Modify DN operation through |
| | | * the TransactedOperation interface. |
| | | */ |
| | | private class RenameEntryTransaction implements TransactedOperation |
| | | throws DatabaseException, DirectoryException, CanceledOperationException |
| | | { |
| | | /** |
| | | * The DN of the entry to be renamed. |
| | | */ |
| | | private DN oldApexDN; |
| | | Transaction txn = beginTransaction(); |
| | | DN oldSuperiorDN = getParentWithinBase(currentDN); |
| | | DN newSuperiorDN = getParentWithinBase(entry.getDN()); |
| | | boolean isApexEntryMoved; |
| | | |
| | | /** |
| | | * The DN of the superior entry of the entry to be renamed. |
| | | * This is null if the entry to be renamed is a base entry. |
| | | */ |
| | | private DN oldSuperiorDN; |
| | | |
| | | /** |
| | | * The DN of the new superior entry, which can be the same |
| | | * as the current superior entry. |
| | | */ |
| | | private DN newSuperiorDN; |
| | | |
| | | /** |
| | | * The new contents of the entry to be renamed. |
| | | */ |
| | | private Entry newApexEntry; |
| | | |
| | | /** |
| | | * The Modify DN operation. |
| | | */ |
| | | private ModifyDNOperation modifyDNOperation; |
| | | |
| | | /** |
| | | * Whether the apex entry moved under another parent. |
| | | */ |
| | | private boolean isApexEntryMoved; |
| | | |
| | | /** |
| | | * Create a new transacted operation for a Modify DN operation. |
| | | * @param currentDN The DN of the entry to be renamed. |
| | | * @param entry The new contents of the entry. |
| | | * @param modifyDNOperation The Modify DN operation to be performed. |
| | | */ |
| | | public RenameEntryTransaction(DN currentDN, Entry entry, |
| | | ModifyDNOperation modifyDNOperation) |
| | | if(oldSuperiorDN != null) |
| | | { |
| | | this.oldApexDN = currentDN; |
| | | this.oldSuperiorDN = getParentWithinBase(currentDN); |
| | | this.newSuperiorDN = getParentWithinBase(entry.getDN()); |
| | | this.newApexEntry = entry; |
| | | this.modifyDNOperation = modifyDNOperation; |
| | | |
| | | if(oldSuperiorDN != null) |
| | | { |
| | | this.isApexEntryMoved = ! oldSuperiorDN.equals(newSuperiorDN); |
| | | } |
| | | else if(newSuperiorDN != null) |
| | | { |
| | | this.isApexEntryMoved = ! newSuperiorDN.equals(oldSuperiorDN); |
| | | } |
| | | else |
| | | { |
| | | this.isApexEntryMoved = false; |
| | | } |
| | | isApexEntryMoved = ! oldSuperiorDN.equals(newSuperiorDN); |
| | | } |
| | | else if(newSuperiorDN != null) |
| | | { |
| | | isApexEntryMoved = ! newSuperiorDN.equals(oldSuperiorDN); |
| | | } |
| | | else |
| | | { |
| | | isApexEntryMoved = false; |
| | | } |
| | | |
| | | /** |
| | | * Invoke the operation under the given transaction. |
| | | * |
| | | * @param txn The transaction to be used to perform the operation. |
| | | * @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. |
| | | */ |
| | | public void invokeOperation(Transaction txn) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | { |
| | | IndexBuffer buffer = new IndexBuffer(EntryContainer.this); |
| | | IndexBuffer buffer = new IndexBuffer(EntryContainer.this); |
| | | |
| | | try |
| | | { |
| | | // Check whether the renamed entry already exists. |
| | | if (dn2id.get(txn, newApexEntry.getDN(), LockMode.DEFAULT) != null) |
| | | if (dn2id.get(txn, entry.getDN(), LockMode.DEFAULT) != null) |
| | | { |
| | | Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get( |
| | | newApexEntry.getDN().toString()); |
| | | entry.getDN().toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | } |
| | | |
| | | EntryID oldApexID = dn2id.get(txn, oldApexDN, LockMode.DEFAULT); |
| | | EntryID oldApexID = dn2id.get(txn, currentDN, LockMode.DEFAULT); |
| | | if (oldApexID == null) |
| | | { |
| | | // Check for referral entries above the target entry. |
| | | dn2uri.targetEntryReferrals(oldApexDN, null); |
| | | dn2uri.targetEntryReferrals(currentDN, null); |
| | | |
| | | Message message = |
| | | ERR_JEB_MODIFYDN_NO_SUCH_OBJECT.get(oldApexDN.toString()); |
| | | ERR_JEB_MODIFYDN_NO_SUCH_OBJECT.get(currentDN.toString()); |
| | | DN matchedDN = getMatchedDN(baseDN); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, |
| | | message, matchedDN, null); |
| | |
| | | if (oldApexEntry == null) |
| | | { |
| | | Message msg = ERR_JEB_MISSING_ID2ENTRY_RECORD.get(oldApexID.toString()); |
| | | throw new JebException(msg); |
| | | throw new DirectoryException( |
| | | DirectoryServer.getServerErrorResultCode(), msg); |
| | | } |
| | | |
| | | if (!isManageDsaITOperation(modifyDNOperation)) |
| | |
| | | "Old entry ID: %d " + |
| | | "New entry ID: %d " + |
| | | "New Superior ID: %d" + |
| | | oldApexEntry.getDN(), newApexEntry.getDN(), |
| | | oldApexEntry.getDN(), entry.getDN(), |
| | | oldApexID.longValue(), newApexID.longValue(), |
| | | newSuperiorID.longValue()); |
| | | } |
| | |
| | | } |
| | | |
| | | // Move or rename the apex entry. |
| | | renameApexEntry(txn, buffer, oldApexID, newApexID, oldApexEntry, |
| | | newApexEntry); |
| | | renameApexEntry(txn, buffer, oldSuperiorDN, newSuperiorDN, oldApexID, |
| | | newApexID, oldApexEntry, entry,isApexEntryMoved, modifyDNOperation); |
| | | |
| | | /* |
| | | * We will iterate forwards through a range of the dn2id keys to |
| | |
| | | * downwards. |
| | | */ |
| | | byte[] suffix = StaticUtils.getBytes("," + |
| | | oldApexDN.toNormalizedString()); |
| | | currentDN.toNormalizedString()); |
| | | |
| | | /* |
| | | * Set the ending value to a value of equal length but slightly |
| | |
| | | byte[] end = suffix.clone(); |
| | | end[0] = (byte) (end[0] + 1); |
| | | |
| | | // Set the starting value to the suffix. |
| | | byte[] begin = suffix; |
| | | |
| | | DatabaseEntry data = new DatabaseEntry(); |
| | | DatabaseEntry key = new DatabaseEntry(begin); |
| | | int subordinateEntriesMoved = 0; |
| | | DatabaseEntry key = new DatabaseEntry(suffix); |
| | | |
| | | CursorConfig cursorConfig = new CursorConfig(); |
| | | cursorConfig.setReadCommitted(true); |
| | |
| | | |
| | | // Step forward until the key is greater than the starting value. |
| | | while (status == OperationStatus.SUCCESS && |
| | | dn2id.getComparator().compare(key.getData(), begin) <= 0) |
| | | dn2id.getComparator().compare(key.getData(), suffix) <= 0) |
| | | { |
| | | status = cursor.getNext(key, data, LockMode.DEFAULT); |
| | | } |
| | |
| | | |
| | | // Construct the new DN of the entry. |
| | | DN newDN = modDN(oldEntry.getDN(), |
| | | oldApexDN.getNumComponents(), |
| | | newApexEntry.getDN()); |
| | | currentDN.getNumComponents(), |
| | | entry.getDN()); |
| | | |
| | | // Assign a new entry ID if we are renumbering. |
| | | EntryID newID = oldID; |
| | |
| | | } |
| | | |
| | | // Move this entry. |
| | | renameSubordinateEntry(txn, buffer, oldID, newID, oldEntry, newDN); |
| | | subordinateEntriesMoved++; |
| | | renameSubordinateEntry(txn, buffer, oldSuperiorDN, newSuperiorDN, |
| | | oldID, newID, oldEntry, newDN, isApexEntryMoved, |
| | | modifyDNOperation); |
| | | |
| | | if(subordinateEntriesMoved >= subtreeDeleteBatchSize) |
| | | if(modifyDNOperation != null) |
| | | { |
| | | buffer.flush(txn); |
| | | subordinateEntriesMoved = 0; |
| | | modifyDNOperation.checkIfCanceled(false); |
| | | } |
| | | |
| | | // Get the next DN. |
| | |
| | | } |
| | | |
| | | buffer.flush(txn); |
| | | } |
| | | |
| | | /** |
| | | * Begin a transaction for this operation. |
| | | * |
| | | * @return The transaction for the operation, or null if the operation |
| | | * will not use a transaction. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | public Transaction beginOperationTransaction() throws DatabaseException |
| | | { |
| | | return beginTransaction(); |
| | | } |
| | | |
| | | /** |
| | | * Update the database for the target entry of a Modify DN operation |
| | | * not specifying a new superior. |
| | | * |
| | | * @param txn The database transaction to be used for the updates. |
| | | * @param buffer The index buffer used to buffer up the index changes. |
| | | * @param oldID The old ID of the target entry. |
| | | * @param newID The new ID of the target entry. |
| | | * @param oldEntry The original contents of the target entry. |
| | | * @param newEntry The new contents of the target entry. |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | * @throws JebException if an error occurs in the JE database. |
| | | */ |
| | | private void renameApexEntry(Transaction txn, IndexBuffer buffer, |
| | | EntryID oldID, EntryID newID, |
| | | Entry oldEntry, Entry newEntry) |
| | | throws DirectoryException, DatabaseException, JebException |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | DN newDN = newEntry.getDN(); |
| | | |
| | | // Remove the old DN from dn2id. |
| | | dn2id.remove(txn, oldDN); |
| | | |
| | | // Put the new DN in dn2id. |
| | | if (!dn2id.insert(txn, newDN, newID)) |
| | | if(modifyDNOperation != null) |
| | | { |
| | | Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(newDN.toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | // One last check before committing |
| | | modifyDNOperation.checkIfCanceled(true); |
| | | } |
| | | |
| | | // Remove old ID from id2entry and put the new entry |
| | | // (old entry with new DN) in id2entry. |
| | | if (!newID.equals(oldID)) |
| | | // Commit the transaction. |
| | | EntryContainer.transactionCommit(txn); |
| | | } |
| | | catch (DatabaseException databaseException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw databaseException; |
| | | } |
| | | catch (DirectoryException directoryException) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw directoryException; |
| | | } |
| | | catch (CanceledOperationException coe) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | throw coe; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | EntryContainer.transactionAbort(txn); |
| | | |
| | | String msg = e.getMessage(); |
| | | if (msg == null) |
| | | { |
| | | id2entry.remove(txn, oldID); |
| | | msg = stackTraceToSingleLineString(e); |
| | | } |
| | | id2entry.put(txn, newID, newEntry); |
| | | Message message = ERR_JEB_UNCHECKED_EXCEPTION.get(msg); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, e); |
| | | } |
| | | } |
| | | |
| | | // Update any referral records. |
| | | dn2uri.replaceEntry(txn, oldEntry, newEntry); |
| | | private void renameApexEntry(Transaction txn, IndexBuffer buffer, |
| | | DN oldSuperiorDN, DN newSuperiorDN, |
| | | EntryID oldID, EntryID newID, |
| | | Entry oldEntry, Entry newEntry, |
| | | boolean isApexEntryMoved, |
| | | ModifyDNOperation modifyDNOperation) |
| | | throws DirectoryException, DatabaseException |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | DN newDN = newEntry.getDN(); |
| | | |
| | | // Remove the old ID from id2children and id2subtree of |
| | | // the old apex parent entry. |
| | | if(oldSuperiorDN != null && isApexEntryMoved) |
| | | // Remove the old DN from dn2id. |
| | | dn2id.remove(txn, oldDN); |
| | | |
| | | // Put the new DN in dn2id. |
| | | if (!dn2id.insert(txn, newDN, newID)) |
| | | { |
| | | Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(newDN.toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | } |
| | | |
| | | // Remove old ID from id2entry and put the new entry |
| | | // (old entry with new DN) in id2entry. |
| | | if (!newID.equals(oldID)) |
| | | { |
| | | id2entry.remove(txn, oldID); |
| | | } |
| | | id2entry.put(txn, newID, newEntry); |
| | | |
| | | // Update any referral records. |
| | | dn2uri.replaceEntry(txn, oldEntry, newEntry); |
| | | |
| | | // Remove the old ID from id2children and id2subtree of |
| | | // the old apex parent entry. |
| | | if(oldSuperiorDN != null && isApexEntryMoved) |
| | | { |
| | | EntryID parentID; |
| | | byte[] parentIDKeyBytes; |
| | | boolean isParent = true; |
| | | for (DN dn = oldSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | EntryID parentID; |
| | | byte[] parentIDKeyBytes; |
| | | boolean isParent = true; |
| | | for (DN dn = oldSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | parentID = dn2id.get(txn, dn, LockMode.DEFAULT); |
| | | parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | if(isParent) |
| | | { |
| | | parentID = dn2id.get(txn, dn, LockMode.DEFAULT); |
| | | parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | if(isParent) |
| | | { |
| | | id2children.removeID(buffer, parentIDKeyBytes, oldID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.removeID(buffer, parentIDKeyBytes, oldID); |
| | | id2children.removeID(buffer, parentIDKeyBytes, oldID); |
| | | isParent = false; |
| | | } |
| | | } |
| | | |
| | | if (!newID.equals(oldID) || modifyDNOperation == null) |
| | | { |
| | | // All the subordinates will be renumbered so we have to rebuild |
| | | // id2c and id2s with the new ID. |
| | | byte[] oldIDKeyBytes = JebFormat.entryIDToDatabase(oldID.longValue()); |
| | | id2children.delete(buffer, oldIDKeyBytes); |
| | | id2subtree.delete(buffer, oldIDKeyBytes); |
| | | |
| | | // Reindex the entry with the new ID. |
| | | indexRemoveEntry(buffer, oldEntry, oldID); |
| | | indexInsertEntry(buffer, newEntry, newID); |
| | | } |
| | | else |
| | | { |
| | | // Update the indexes if needed. |
| | | indexModifications(buffer, oldEntry, newEntry, oldID, |
| | | modifyDNOperation.getModifications()); |
| | | } |
| | | |
| | | // Add the new ID to id2children and id2subtree of new apex parent entry. |
| | | if(newSuperiorDN != null && isApexEntryMoved) |
| | | { |
| | | EntryID parentID; |
| | | byte[] parentIDKeyBytes; |
| | | boolean isParent = true; |
| | | for (DN dn = newSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | parentID = dn2id.get(txn, dn, LockMode.DEFAULT); |
| | | parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | if(isParent) |
| | | { |
| | | id2children.insertID(buffer, parentIDKeyBytes, newID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.insertID(buffer, parentIDKeyBytes, newID); |
| | | } |
| | | } |
| | | |
| | | // Remove the entry from the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | | { |
| | | entryCache.removeEntry(oldDN); |
| | | id2subtree.removeID(buffer, parentIDKeyBytes, oldID); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Update the database for a subordinate entry of the target entry |
| | | * of a Modify DN operation specifying a new superior. |
| | | * |
| | | * @param txn The database transaction to be used for the updates. |
| | | * @param buffer The index buffer used to buffer up the index changes. |
| | | * @param oldID The original ID of the subordinate entry. |
| | | * @param newID The new ID of the subordinate entry, or the original ID if |
| | | * the ID has not changed. |
| | | * @param oldEntry The original contents of the subordinate entry. |
| | | * @param newDN The new DN of the subordinate entry. |
| | | * @throws JebException If an error occurs in the JE backend. |
| | | * @throws DirectoryException If a Directory Server error occurs. |
| | | * @throws DatabaseException If an error occurs in the JE database. |
| | | */ |
| | | private void renameSubordinateEntry(Transaction txn, IndexBuffer buffer, |
| | | EntryID oldID, EntryID newID, |
| | | Entry oldEntry, DN newDN) |
| | | throws JebException, DirectoryException, DatabaseException |
| | | if (!newID.equals(oldID) || modifyDNOperation == null) |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | Entry newEntry = oldEntry.duplicate(false); |
| | | newEntry.setDN(newDN); |
| | | List<Modification> modifications = |
| | | Collections.unmodifiableList(new ArrayList<Modification>(0)); |
| | | // All the subordinates will be renumbered so we have to rebuild |
| | | // id2c and id2s with the new ID. |
| | | byte[] oldIDKeyBytes = JebFormat.entryIDToDatabase(oldID.longValue()); |
| | | id2children.delete(buffer, oldIDKeyBytes); |
| | | id2subtree.delete(buffer, oldIDKeyBytes); |
| | | |
| | | // Create a new entry that is a copy of the old entry but with the new DN. |
| | | // Also invoke any subordinate modify DN plugins on the entry. |
| | | // FIXME -- At the present time, we don't support subordinate modify DN |
| | | // plugins that make changes to subordinate entries and therefore |
| | | // provide an unmodifiable list for the modifications element. |
| | | // FIXME -- This will need to be updated appropriately if we decided that |
| | | // these plugins should be invoked for synchronization |
| | | // operations. |
| | | if (! modifyDNOperation.isSynchronizationOperation()) |
| | | // Reindex the entry with the new ID. |
| | | indexRemoveEntry(buffer, oldEntry, oldID); |
| | | indexInsertEntry(buffer, newEntry, newID); |
| | | } |
| | | else |
| | | { |
| | | // Update the indexes if needed. |
| | | indexModifications(buffer, oldEntry, newEntry, oldID, |
| | | modifyDNOperation.getModifications()); |
| | | } |
| | | |
| | | // Add the new ID to id2children and id2subtree of new apex parent entry. |
| | | if(newSuperiorDN != null && isApexEntryMoved) |
| | | { |
| | | EntryID parentID; |
| | | byte[] parentIDKeyBytes; |
| | | boolean isParent = true; |
| | | for (DN dn = newSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | PluginConfigManager pluginManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | PluginResult.SubordinateModifyDN pluginResult = |
| | | pluginManager.invokeSubordinateModifyDNPlugins( |
| | | modifyDNOperation, oldEntry, newEntry, modifications); |
| | | |
| | | if (!pluginResult.continueProcessing()) |
| | | parentID = dn2id.get(txn, dn, LockMode.DEFAULT); |
| | | parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | if(isParent) |
| | | { |
| | | Message message = ERR_JEB_MODIFYDN_ABORTED_BY_SUBORDINATE_PLUGIN.get( |
| | | oldDN.toString(), newDN.toString()); |
| | | id2children.insertID(buffer, parentIDKeyBytes, newID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.insertID(buffer, parentIDKeyBytes, newID); |
| | | } |
| | | } |
| | | |
| | | // Remove the entry from the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | | { |
| | | entryCache.removeEntry(oldDN); |
| | | } |
| | | } |
| | | |
| | | private void renameSubordinateEntry(Transaction txn, IndexBuffer buffer, |
| | | DN oldSuperiorDN, DN newSuperiorDN, |
| | | EntryID oldID, EntryID newID, |
| | | Entry oldEntry, DN newDN, |
| | | boolean isApexEntryMoved, |
| | | ModifyDNOperation modifyDNOperation) |
| | | throws DirectoryException, DatabaseException |
| | | { |
| | | DN oldDN = oldEntry.getDN(); |
| | | Entry newEntry = oldEntry.duplicate(false); |
| | | newEntry.setDN(newDN); |
| | | List<Modification> modifications = |
| | | Collections.unmodifiableList(new ArrayList<Modification>(0)); |
| | | |
| | | // Create a new entry that is a copy of the old entry but with the new DN. |
| | | // Also invoke any subordinate modify DN plugins on the entry. |
| | | // FIXME -- At the present time, we don't support subordinate modify DN |
| | | // plugins that make changes to subordinate entries and therefore |
| | | // provide an unmodifiable list for the modifications element. |
| | | // FIXME -- This will need to be updated appropriately if we decided that |
| | | // these plugins should be invoked for synchronization |
| | | // operations. |
| | | if (! modifyDNOperation.isSynchronizationOperation()) |
| | | { |
| | | PluginConfigManager pluginManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | PluginResult.SubordinateModifyDN pluginResult = |
| | | pluginManager.invokeSubordinateModifyDNPlugins( |
| | | modifyDNOperation, oldEntry, newEntry, modifications); |
| | | |
| | | if (!pluginResult.continueProcessing()) |
| | | { |
| | | Message message = ERR_JEB_MODIFYDN_ABORTED_BY_SUBORDINATE_PLUGIN.get( |
| | | oldDN.toString(), newDN.toString()); |
| | | throw new DirectoryException( |
| | | DirectoryServer.getServerErrorResultCode(), message); |
| | | } |
| | | |
| | | if (! modifications.isEmpty()) |
| | | { |
| | | MessageBuilder invalidReason = new MessageBuilder(); |
| | | if (! newEntry.conformsToSchema(null, false, false, false, |
| | | invalidReason)) |
| | | { |
| | | Message message = |
| | | ERR_JEB_MODIFYDN_ABORTED_BY_SUBORDINATE_SCHEMA_ERROR.get( |
| | | oldDN.toString(), |
| | | newDN.toString(), |
| | | invalidReason.toString()); |
| | | throw new DirectoryException( |
| | | DirectoryServer.getServerErrorResultCode(), message); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (! modifications.isEmpty()) |
| | | // Remove the old DN from dn2id. |
| | | dn2id.remove(txn, oldDN); |
| | | |
| | | // Put the new DN in dn2id. |
| | | if (!dn2id.insert(txn, newDN, newID)) |
| | | { |
| | | Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(newDN.toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | } |
| | | |
| | | // Remove old ID from id2entry and put the new entry |
| | | // (old entry with new DN) in id2entry. |
| | | if (!newID.equals(oldID)) |
| | | { |
| | | id2entry.remove(txn, oldID); |
| | | } |
| | | id2entry.put(txn, newID, newEntry); |
| | | |
| | | // Update any referral records. |
| | | dn2uri.replaceEntry(txn, oldEntry, newEntry); |
| | | |
| | | if(isApexEntryMoved) |
| | | { |
| | | // 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, LockMode.DEFAULT); |
| | | byte[] parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | id2subtree.removeID(buffer, parentIDKeyBytes, oldID); |
| | | } |
| | | } |
| | | |
| | | if (!newID.equals(oldID)) |
| | | { |
| | | // All the subordinates will be renumbered so we have to rebuild |
| | | // id2c and id2s with the new ID. |
| | | byte[] oldIDKeyBytes = JebFormat.entryIDToDatabase(oldID.longValue()); |
| | | id2children.delete(buffer, oldIDKeyBytes); |
| | | id2subtree.delete(buffer, oldIDKeyBytes); |
| | | |
| | | // Add new ID to the id2c and id2s of our new parent and |
| | | // new ID to id2s up the tree. |
| | | EntryID newParentID; |
| | | byte[] parentIDKeyBytes; |
| | | boolean isParent = true; |
| | | for (DN superiorDN = newDN; superiorDN != null; |
| | | superiorDN = getParentWithinBase(superiorDN)) |
| | | { |
| | | newParentID = dn2id.get(txn, superiorDN, LockMode.DEFAULT); |
| | | parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(newParentID.longValue()); |
| | | if(isParent) |
| | | { |
| | | MessageBuilder invalidReason = new MessageBuilder(); |
| | | if (! newEntry.conformsToSchema(null, false, false, false, |
| | | invalidReason)) |
| | | { |
| | | Message message = |
| | | ERR_JEB_MODIFYDN_ABORTED_BY_SUBORDINATE_SCHEMA_ERROR.get( |
| | | oldDN.toString(), |
| | | newDN.toString(), |
| | | invalidReason.toString()); |
| | | throw new DirectoryException( |
| | | DirectoryServer.getServerErrorResultCode(), message); |
| | | } |
| | | id2children.insertID(buffer, parentIDKeyBytes, newID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.insertID(buffer, parentIDKeyBytes, newID); |
| | | } |
| | | |
| | | // Remove the old DN from dn2id. |
| | | dn2id.remove(txn, oldDN); |
| | | |
| | | // Put the new DN in dn2id. |
| | | if (!dn2id.insert(txn, newDN, newID)) |
| | | // Reindex the entry with the new ID. |
| | | indexRemoveEntry(buffer, oldEntry, oldID); |
| | | indexInsertEntry(buffer, newEntry, newID); |
| | | } |
| | | else |
| | | { |
| | | // Update the indexes if needed. |
| | | if(! modifications.isEmpty()) |
| | | { |
| | | Message message = ERR_JEB_MODIFYDN_ALREADY_EXISTS.get(newDN.toString()); |
| | | throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, |
| | | message); |
| | | indexModifications(buffer, oldEntry, newEntry, oldID, modifications); |
| | | } |
| | | |
| | | // Remove old ID from id2entry and put the new entry |
| | | // (old entry with new DN) in id2entry. |
| | | if (!newID.equals(oldID)) |
| | | { |
| | | id2entry.remove(txn, oldID); |
| | | } |
| | | id2entry.put(txn, newID, newEntry); |
| | | |
| | | // Update any referral records. |
| | | dn2uri.replaceEntry(txn, oldEntry, newEntry); |
| | | |
| | | if(isApexEntryMoved) |
| | | { |
| | | // Remove the old ID from id2subtree of old apex superior entries. |
| | | for (DN dn = oldSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | // Add the new ID to the id2s of new apex superior entries. |
| | | for(DN dn = newSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | EntryID parentID = dn2id.get(txn, dn, LockMode.DEFAULT); |
| | | byte[] parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | id2subtree.removeID(buffer, parentIDKeyBytes, oldID); |
| | | } |
| | | } |
| | | |
| | | if (!newID.equals(oldID)) |
| | | { |
| | | // All the subordinates will be renumbered so we have to rebuild |
| | | // id2c and id2s with the new ID. |
| | | byte[] oldIDKeyBytes = JebFormat.entryIDToDatabase(oldID.longValue()); |
| | | id2children.delete(buffer, oldIDKeyBytes); |
| | | id2subtree.delete(buffer, oldIDKeyBytes); |
| | | |
| | | // Add new ID to the id2c and id2s of our new parent and |
| | | // new ID to id2s up the tree. |
| | | EntryID newParentID; |
| | | byte[] parentIDKeyBytes; |
| | | boolean isParent = true; |
| | | for (DN superiorDN = newDN; superiorDN != null; |
| | | superiorDN = getParentWithinBase(superiorDN)) |
| | | { |
| | | newParentID = dn2id.get(txn, superiorDN, LockMode.DEFAULT); |
| | | parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(newParentID.longValue()); |
| | | if(isParent) |
| | | { |
| | | id2children.insertID(buffer, parentIDKeyBytes, newID); |
| | | isParent = false; |
| | | } |
| | | id2subtree.insertID(buffer, parentIDKeyBytes, newID); |
| | | } |
| | | |
| | | // Reindex the entry with the new ID. |
| | | indexRemoveEntry(buffer, oldEntry, oldID); |
| | | indexInsertEntry(buffer, newEntry, newID); |
| | | } |
| | | else |
| | | { |
| | | // Update the indexes if needed. |
| | | if(! modifications.isEmpty()) |
| | | { |
| | | indexModifications(buffer, oldEntry, newEntry, oldID, modifications); |
| | | } |
| | | |
| | | if(isApexEntryMoved) |
| | | { |
| | | // Add the new ID to the id2s of new apex superior entries. |
| | | for(DN dn = newSuperiorDN; dn != null; dn = getParentWithinBase(dn)) |
| | | { |
| | | EntryID parentID = dn2id.get(txn, dn, LockMode.DEFAULT); |
| | | byte[] parentIDKeyBytes = |
| | | JebFormat.entryIDToDatabase(parentID.longValue()); |
| | | id2subtree.insertID(buffer, parentIDKeyBytes, newID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Remove the entry from the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | | { |
| | | entryCache.removeEntry(oldDN); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * This method is called after the transaction has successfully |
| | | * committed. |
| | | */ |
| | | public void postCommitAction() |
| | | // Remove the entry from the entry cache. |
| | | EntryCache entryCache = DirectoryServer.getEntryCache(); |
| | | if (entryCache != null) |
| | | { |
| | | // No implementation needed. |
| | | entryCache.removeEntry(oldDN); |
| | | } |
| | | } |
| | | |
| | |
| | | * @param entryID The ID of the entry to be inserted into the indexes. |
| | | * @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. |
| | | */ |
| | | private void indexInsertEntry(IndexBuffer buffer, Entry entry, |
| | | EntryID entryID) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException |
| | | { |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | |
| | | * @param entryID The ID of the entry to be removed from the indexes. |
| | | * @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. |
| | | */ |
| | | private void indexRemoveEntry(IndexBuffer buffer, Entry entry, |
| | | EntryID entryID) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException |
| | | { |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | | { |
| | |
| | | * @param mods The sequence of modifications made to the entry. |
| | | * @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. |
| | | */ |
| | | private void indexModifications(IndexBuffer buffer, Entry oldEntry, |
| | | Entry newEntry, |
| | | EntryID entryID, List<Modification> mods) |
| | | throws DatabaseException, DirectoryException, JebException |
| | | throws DatabaseException, DirectoryException |
| | | { |
| | | // Process in index configuration order. |
| | | for (AttributeIndex index : attrIndexMap.values()) |
| | |
| | | id2entry.setDataConfig(entryDataConfig); |
| | | |
| | | this.config = cfg; |
| | | this.subtreeDeleteSizeLimit = config.getSubtreeDeleteSizeLimit(); |
| | | this.subtreeDeleteBatchSize = config.getSubtreeDeleteBatchSize(); |
| | | return new ConfigChangeResult(ResultCode.SUCCESS, |
| | | adminActionRequired, messages); |
| | | } |
| | |
| | | public void unlock() { |
| | | exclusiveLock.unlock(); |
| | | } |
| | | |
| | | /** |
| | | * Get the subtree delete batch size. |
| | | * |
| | | * @return The subtree delete batch size. |
| | | */ |
| | | public int getSubtreeDeleteBatchSize() |
| | | { |
| | | return subtreeDeleteBatchSize; |
| | | } |
| | | |
| | | } |