use of com.unboundid.ldap.sdk.unboundidds.controls.SoftDeletedEntryAccessRequestControl in project ldapsdk by pingidentity.
the class SubtreeDeleter method delete.
/**
* Attempts to delete the specified subtree using the current settings.
*
* @param connection
* The {@link LDAPInterface} instance to use to communicate with
* the directory server. While this may be an individual
* {@link LDAPConnection}, it may be better as a connection
* pool with automatic retry enabled so that it's more likely to
* succeed in the event that a connection becomes invalid or an
* operation experiences a transient failure. It must not be
* {@code null}.
* @param baseDN
* The base DN for the subtree to delete. It must not be
* {@code null}.
*
* @return An object with information about the results of the subtree
* delete processing.
*/
@NotNull()
public SubtreeDeleterResult delete(@NotNull final LDAPInterface connection, @NotNull final DN baseDN) {
final AtomicReference<RootDSE> rootDSE = new AtomicReference<>();
final boolean useSetSubtreeAccessibility = useSetSubtreeAccessibilityOperationIfAvailable && supportsExtendedRequest(connection, rootDSE, SetSubtreeAccessibilityExtendedRequest.SET_SUBTREE_ACCESSIBILITY_REQUEST_OID) && supportsExtendedRequest(connection, rootDSE, WhoAmIExtendedRequest.WHO_AM_I_REQUEST_OID);
final boolean usePagedResults = useSimplePagedResultsControlIfAvailable && supportsControl(connection, rootDSE, SimplePagedResultsControl.PAGED_RESULTS_OID);
final boolean useSubentries = useSubentriesControlIfAvailable && supportsControl(connection, rootDSE, DraftLDUPSubentriesRequestControl.SUBENTRIES_REQUEST_OID);
final List<Control> searchControls = new ArrayList<>(10);
searchControls.addAll(additionalSearchControls);
final List<Control> deleteControls = new ArrayList<>(10);
deleteControls.addAll(additionalDeleteControls);
if (useHardDeleteControlIfAvailable && supportsControl(connection, rootDSE, HardDeleteRequestControl.HARD_DELETE_REQUEST_OID)) {
deleteControls.add(new HardDeleteRequestControl(false));
}
if (useManageDSAITControlIfAvailable && supportsControl(connection, rootDSE, ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID)) {
final ManageDsaITRequestControl c = new ManageDsaITRequestControl(false);
searchControls.add(c);
deleteControls.add(c);
}
if (usePermitUnindexedSearchControlIfAvailable && supportsControl(connection, rootDSE, PermitUnindexedSearchRequestControl.PERMIT_UNINDEXED_SEARCH_REQUEST_OID)) {
searchControls.add(new PermitUnindexedSearchRequestControl(false));
}
if (useReturnConflictEntriesRequestControlIfAvailable && supportsControl(connection, rootDSE, ReturnConflictEntriesRequestControl.RETURN_CONFLICT_ENTRIES_REQUEST_OID)) {
searchControls.add(new ReturnConflictEntriesRequestControl(false));
}
if (useSoftDeletedEntryAccessControlIfAvailable && supportsControl(connection, rootDSE, SoftDeletedEntryAccessRequestControl.SOFT_DELETED_ENTRY_ACCESS_REQUEST_OID)) {
searchControls.add(new SoftDeletedEntryAccessRequestControl(false, true, false));
}
return delete(connection, baseDN, deleteBaseEntry, useSetSubtreeAccessibility, usePagedResults, searchRequestSizeLimit, simplePagedResultsPageSize, useSubentries, searchControls, deleteControls, deleteRateLimiter);
}
use of com.unboundid.ldap.sdk.unboundidds.controls.SoftDeletedEntryAccessRequestControl in project ldapsdk by pingidentity.
the class LDAPSearch method getSearchControls.
/**
* Retrieves a list of the controls that should be used when processing search
* operations.
*
* @return A list of the controls that should be used when processing search
* operations.
*
* @throws LDAPException If a problem is encountered while generating the
* controls for a search request.
*/
@NotNull()
private List<Control> getSearchControls() {
final ArrayList<Control> controls = new ArrayList<>(10);
if (searchControl.isPresent()) {
controls.addAll(searchControl.getValues());
}
if (joinRequestControl != null) {
controls.add(joinRequestControl);
}
if (matchedValuesRequestControl != null) {
controls.add(matchedValuesRequestControl);
}
if (matchingEntryCountRequestControl != null) {
controls.add(matchingEntryCountRequestControl);
}
if (overrideSearchLimitsRequestControl != null) {
controls.add(overrideSearchLimitsRequestControl);
}
if (persistentSearchRequestControl != null) {
controls.add(persistentSearchRequestControl);
}
if (sortRequestControl != null) {
controls.add(sortRequestControl);
}
if (vlvRequestControl != null) {
controls.add(vlvRequestControl);
}
controls.addAll(routeToBackendSetRequestControls);
if (accountUsable.isPresent()) {
controls.add(new AccountUsableRequestControl(true));
}
if (getBackendSetID.isPresent()) {
controls.add(new GetBackendSetIDRequestControl(false));
}
if (getServerID.isPresent()) {
controls.add(new GetServerIDRequestControl(false));
}
if (includeReplicationConflictEntries.isPresent()) {
controls.add(new ReturnConflictEntriesRequestControl(true));
}
if (includeSoftDeletedEntries.isPresent()) {
final String valueStr = StaticUtils.toLowerCase(includeSoftDeletedEntries.getValue());
if (valueStr.equals("with-non-deleted-entries")) {
controls.add(new SoftDeletedEntryAccessRequestControl(true, true, false));
} else if (valueStr.equals("without-non-deleted-entries")) {
controls.add(new SoftDeletedEntryAccessRequestControl(true, false, false));
} else {
controls.add(new SoftDeletedEntryAccessRequestControl(true, false, true));
}
}
if (draftLDUPSubentries.isPresent()) {
controls.add(new DraftLDUPSubentriesRequestControl(true));
}
if (rfc3672Subentries.isPresent()) {
controls.add(new RFC3672SubentriesRequestControl(rfc3672Subentries.getValue()));
}
if (manageDsaIT.isPresent()) {
controls.add(new ManageDsaITRequestControl(true));
}
if (realAttributesOnly.isPresent()) {
controls.add(new RealAttributesOnlyRequestControl(true));
}
if (routeToServer.isPresent()) {
controls.add(new RouteToServerRequestControl(false, routeToServer.getValue(), false, false, false));
}
if (virtualAttributesOnly.isPresent()) {
controls.add(new VirtualAttributesOnlyRequestControl(true));
}
if (excludeBranch.isPresent()) {
final ArrayList<String> dns = new ArrayList<>(excludeBranch.getValues().size());
for (final DN dn : excludeBranch.getValues()) {
dns.add(dn.toString());
}
controls.add(new ExcludeBranchRequestControl(true, dns));
}
if (assertionFilter.isPresent()) {
controls.add(new AssertionRequestControl(assertionFilter.getValue(), true));
}
if (getEffectiveRightsAuthzID.isPresent()) {
final String[] attributes;
if (getEffectiveRightsAttribute.isPresent()) {
attributes = new String[getEffectiveRightsAttribute.getValues().size()];
for (int i = 0; i < attributes.length; i++) {
attributes[i] = getEffectiveRightsAttribute.getValues().get(i);
}
} else {
attributes = StaticUtils.NO_STRINGS;
}
controls.add(new GetEffectiveRightsRequestControl(true, getEffectiveRightsAuthzID.getValue(), attributes));
}
if (operationPurpose.isPresent()) {
controls.add(new OperationPurposeRequestControl(true, "ldapsearch", Version.NUMERIC_VERSION_STRING, "LDAPSearch.getSearchControls", operationPurpose.getValue()));
}
if (proxyAs.isPresent()) {
controls.add(new ProxiedAuthorizationV2RequestControl(proxyAs.getValue()));
}
if (proxyV1As.isPresent()) {
controls.add(new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue()));
}
if (suppressOperationalAttributeUpdates.isPresent()) {
final EnumSet<SuppressType> suppressTypes = EnumSet.noneOf(SuppressType.class);
for (final String s : suppressOperationalAttributeUpdates.getValues()) {
if (s.equalsIgnoreCase("last-access-time")) {
suppressTypes.add(SuppressType.LAST_ACCESS_TIME);
} else if (s.equalsIgnoreCase("last-login-time")) {
suppressTypes.add(SuppressType.LAST_LOGIN_TIME);
} else if (s.equalsIgnoreCase("last-login-ip")) {
suppressTypes.add(SuppressType.LAST_LOGIN_IP);
}
}
controls.add(new SuppressOperationalAttributeUpdateRequestControl(suppressTypes));
}
if (rejectUnindexedSearch.isPresent()) {
controls.add(new RejectUnindexedSearchRequestControl());
}
if (permitUnindexedSearch.isPresent()) {
controls.add(new PermitUnindexedSearchRequestControl());
}
return controls;
}
use of com.unboundid.ldap.sdk.unboundidds.controls.SoftDeletedEntryAccessRequestControl in project ldapsdk by pingidentity.
the class MoveSubtree method moveSubtreeWithRestrictedAccessibility.
/**
* Performs the real {@code moveSubtreeWithRestrictedAccessibility}
* processing. If a tool is available, this method will update state
* information in that tool so that it can be referenced by a shutdown hook
* in the event that processing is interrupted.
*
* @param tool A reference to a tool instance to be updated with
* state information.
* @param sourceConnection A connection established to the source server.
* It should be authenticated as a user with
* permission to perform all of the operations
* against the source server as referenced above.
* @param targetConnection A connection established to the target server.
* It should be authenticated as a user with
* permission to perform all of the operations
* against the target server as referenced above.
* @param baseDN The base DN for the subtree to move.
* @param sizeLimit The maximum number of entries to be moved. It
* may be less than or equal to zero to indicate
* that no client-side limit should be enforced
* (although the server may still enforce its own
* limit).
* @param opPurposeControl An optional operation purpose request control
* that may be included in all requests sent to the
* source and target servers.
* @param suppressRefInt Indicates whether to include a request control
* causing referential integrity updates to be
* suppressed on the source server.
* @param listener An optional listener that may be invoked during
* the course of moving entries from the source
* server to the target server.
*
* @return An object with information about the result of the attempted
* subtree move.
*/
@NotNull()
private static MoveSubtreeResult moveSubtreeWithRestrictedAccessibility(@Nullable final MoveSubtree tool, @NotNull final LDAPConnection sourceConnection, @NotNull final LDAPConnection targetConnection, @NotNull final String baseDN, final int sizeLimit, @Nullable final OperationPurposeRequestControl opPurposeControl, final boolean suppressRefInt, @Nullable final MoveSubtreeListener listener) {
// Ensure that the subtree is currently accessible in both the source and
// target servers.
final MoveSubtreeResult initialAccessibilityResult = checkInitialAccessibility(sourceConnection, targetConnection, baseDN, opPurposeControl);
if (initialAccessibilityResult != null) {
return initialAccessibilityResult;
}
final StringBuilder errorMsg = new StringBuilder();
final StringBuilder adminMsg = new StringBuilder();
final ReverseComparator<DN> reverseComparator = new ReverseComparator<>();
final TreeSet<DN> sourceEntryDNs = new TreeSet<>(reverseComparator);
final AtomicInteger entriesReadFromSource = new AtomicInteger(0);
final AtomicInteger entriesAddedToTarget = new AtomicInteger(0);
final AtomicInteger entriesDeletedFromSource = new AtomicInteger(0);
final AtomicReference<ResultCode> resultCode = new AtomicReference<>();
boolean sourceServerAltered = false;
boolean targetServerAltered = false;
SubtreeAccessibilityState currentSourceState = SubtreeAccessibilityState.ACCESSIBLE;
SubtreeAccessibilityState currentTargetState = SubtreeAccessibilityState.ACCESSIBLE;
processingBlock: {
// Identify the users authenticated on each connection.
final String sourceUserDN;
final String targetUserDN;
try {
sourceUserDN = getAuthenticatedUserDN(sourceConnection, true, opPurposeControl);
targetUserDN = getAuthenticatedUserDN(targetConnection, false, opPurposeControl);
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(le.getMessage(), errorMsg);
break processingBlock;
}
// Make the subtree hidden on the target server.
try {
setAccessibility(targetConnection, false, baseDN, SubtreeAccessibilityState.HIDDEN, targetUserDN, opPurposeControl);
currentTargetState = SubtreeAccessibilityState.HIDDEN;
setInterruptMessage(tool, WARN_MOVE_SUBTREE_INTERRUPT_MSG_TARGET_HIDDEN.get(baseDN, targetConnection.getConnectedAddress(), targetConnection.getConnectedPort()));
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(le.getMessage(), errorMsg);
break processingBlock;
}
// Make the subtree read-only on the source server.
try {
setAccessibility(sourceConnection, true, baseDN, SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED, sourceUserDN, opPurposeControl);
currentSourceState = SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED;
setInterruptMessage(tool, WARN_MOVE_SUBTREE_INTERRUPT_MSG_SOURCE_READ_ONLY.get(baseDN, targetConnection.getConnectedAddress(), targetConnection.getConnectedPort(), sourceConnection.getConnectedAddress(), sourceConnection.getConnectedPort()));
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(le.getMessage(), errorMsg);
break processingBlock;
}
// Perform a search to find all entries in the target subtree, and include
// a search listener that will add each entry to the target server as it
// is returned from the source server.
final Control[] searchControls;
if (opPurposeControl == null) {
searchControls = new Control[] { new DraftLDUPSubentriesRequestControl(true), new ManageDsaITRequestControl(true), new ReturnConflictEntriesRequestControl(true), new SoftDeletedEntryAccessRequestControl(true, true, false), new RealAttributesOnlyRequestControl(true) };
} else {
searchControls = new Control[] { new DraftLDUPSubentriesRequestControl(true), new ManageDsaITRequestControl(true), new ReturnConflictEntriesRequestControl(true), new SoftDeletedEntryAccessRequestControl(true, true, false), new RealAttributesOnlyRequestControl(true), opPurposeControl };
}
final MoveSubtreeAccessibilitySearchListener searchListener = new MoveSubtreeAccessibilitySearchListener(tool, baseDN, sourceConnection, targetConnection, resultCode, errorMsg, entriesReadFromSource, entriesAddedToTarget, sourceEntryDNs, opPurposeControl, listener);
final SearchRequest searchRequest = new SearchRequest(searchListener, searchControls, baseDN, SearchScope.SUB, DereferencePolicy.NEVER, sizeLimit, 0, false, Filter.createPresenceFilter("objectClass"), "*", "+");
SearchResult searchResult;
try {
searchResult = sourceConnection.search(searchRequest);
} catch (final LDAPSearchException lse) {
Debug.debugException(lse);
searchResult = lse.getSearchResult();
}
if (entriesAddedToTarget.get() > 0) {
targetServerAltered = true;
}
if (searchResult.getResultCode() != ResultCode.SUCCESS) {
resultCode.compareAndSet(null, searchResult.getResultCode());
append(ERR_MOVE_SUBTREE_SEARCH_FAILED.get(baseDN, searchResult.getDiagnosticMessage()), errorMsg);
final AtomicInteger deleteCount = new AtomicInteger(0);
if (targetServerAltered) {
deleteEntries(targetConnection, false, sourceEntryDNs, opPurposeControl, false, null, deleteCount, resultCode, errorMsg);
entriesAddedToTarget.addAndGet(0 - deleteCount.get());
if (entriesAddedToTarget.get() == 0) {
targetServerAltered = false;
} else {
append(ERR_MOVE_SUBTREE_TARGET_NOT_DELETED_ADMIN_ACTION.get(baseDN), adminMsg);
}
}
break processingBlock;
}
// If an error occurred during add processing, then fail.
if (resultCode.get() != null) {
final AtomicInteger deleteCount = new AtomicInteger(0);
if (targetServerAltered) {
deleteEntries(targetConnection, false, sourceEntryDNs, opPurposeControl, false, null, deleteCount, resultCode, errorMsg);
entriesAddedToTarget.addAndGet(0 - deleteCount.get());
if (entriesAddedToTarget.get() == 0) {
targetServerAltered = false;
} else {
append(ERR_MOVE_SUBTREE_TARGET_NOT_DELETED_ADMIN_ACTION.get(baseDN), adminMsg);
}
}
break processingBlock;
}
// Make the subtree read-only on the target server.
try {
setAccessibility(targetConnection, true, baseDN, SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED, targetUserDN, opPurposeControl);
currentTargetState = SubtreeAccessibilityState.READ_ONLY_BIND_ALLOWED;
setInterruptMessage(tool, WARN_MOVE_SUBTREE_INTERRUPT_MSG_TARGET_READ_ONLY.get(baseDN, sourceConnection.getConnectedAddress(), sourceConnection.getConnectedPort(), targetConnection.getConnectedAddress(), targetConnection.getConnectedPort()));
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(le.getMessage(), errorMsg);
break processingBlock;
}
// Make the subtree hidden on the source server.
try {
setAccessibility(sourceConnection, true, baseDN, SubtreeAccessibilityState.HIDDEN, sourceUserDN, opPurposeControl);
currentSourceState = SubtreeAccessibilityState.HIDDEN;
setInterruptMessage(tool, WARN_MOVE_SUBTREE_INTERRUPT_MSG_SOURCE_HIDDEN.get(baseDN, sourceConnection.getConnectedAddress(), sourceConnection.getConnectedPort(), targetConnection.getConnectedAddress(), targetConnection.getConnectedPort()));
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(le.getMessage(), errorMsg);
break processingBlock;
}
// Make the subtree accessible on the target server.
try {
setAccessibility(targetConnection, true, baseDN, SubtreeAccessibilityState.ACCESSIBLE, targetUserDN, opPurposeControl);
currentTargetState = SubtreeAccessibilityState.ACCESSIBLE;
setInterruptMessage(tool, WARN_MOVE_SUBTREE_INTERRUPT_MSG_TARGET_ACCESSIBLE.get(baseDN, sourceConnection.getConnectedAddress(), sourceConnection.getConnectedPort(), targetConnection.getConnectedAddress(), targetConnection.getConnectedPort()));
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(le.getMessage(), errorMsg);
break processingBlock;
}
// Delete each of the entries in the source server. The map should
// already be sorted in reverse order (as a result of the comparator used
// when creating it), so it will guarantee children are deleted before
// their parents.
final boolean deleteSuccessful = deleteEntries(sourceConnection, true, sourceEntryDNs, opPurposeControl, suppressRefInt, listener, entriesDeletedFromSource, resultCode, errorMsg);
sourceServerAltered = (entriesDeletedFromSource.get() != 0);
if (!deleteSuccessful) {
append(ERR_MOVE_SUBTREE_SOURCE_NOT_DELETED_ADMIN_ACTION.get(baseDN), adminMsg);
break processingBlock;
}
// Make the subtree accessible on the source server.
try {
setAccessibility(sourceConnection, true, baseDN, SubtreeAccessibilityState.ACCESSIBLE, sourceUserDN, opPurposeControl);
currentSourceState = SubtreeAccessibilityState.ACCESSIBLE;
setInterruptMessage(tool, null);
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(le.getMessage(), errorMsg);
break processingBlock;
}
}
// then accessible, then generate an admin action message.
if (currentSourceState != SubtreeAccessibilityState.ACCESSIBLE) {
if (!sourceServerAltered) {
try {
setAccessibility(sourceConnection, true, baseDN, SubtreeAccessibilityState.ACCESSIBLE, null, opPurposeControl);
currentSourceState = SubtreeAccessibilityState.ACCESSIBLE;
} catch (final LDAPException le) {
Debug.debugException(le);
}
}
if (currentSourceState != SubtreeAccessibilityState.ACCESSIBLE) {
append(ERR_MOVE_SUBTREE_SOURCE_LEFT_INACCESSIBLE.get(currentSourceState, baseDN), adminMsg);
}
}
// then accessible, then generate an admin action message.
if (currentTargetState != SubtreeAccessibilityState.ACCESSIBLE) {
if (!targetServerAltered) {
try {
setAccessibility(targetConnection, false, baseDN, SubtreeAccessibilityState.ACCESSIBLE, null, opPurposeControl);
currentTargetState = SubtreeAccessibilityState.ACCESSIBLE;
} catch (final LDAPException le) {
Debug.debugException(le);
}
}
if (currentTargetState != SubtreeAccessibilityState.ACCESSIBLE) {
append(ERR_MOVE_SUBTREE_TARGET_LEFT_INACCESSIBLE.get(currentTargetState, baseDN), adminMsg);
}
}
// Construct the result to return to the client.
resultCode.compareAndSet(null, ResultCode.SUCCESS);
final String errorMessage;
if (errorMsg.length() > 0) {
errorMessage = errorMsg.toString();
} else {
errorMessage = null;
}
final String adminActionRequired;
if (adminMsg.length() > 0) {
adminActionRequired = adminMsg.toString();
} else {
adminActionRequired = null;
}
return new MoveSubtreeResult(resultCode.get(), errorMessage, adminActionRequired, sourceServerAltered, targetServerAltered, entriesReadFromSource.get(), entriesAddedToTarget.get(), entriesDeletedFromSource.get());
}
use of com.unboundid.ldap.sdk.unboundidds.controls.SoftDeletedEntryAccessRequestControl in project ldapsdk by pingidentity.
the class MoveSubtree method moveEntryWithInteractiveTransaction.
/**
* <BLOCKQUOTE>
* <B>NOTE:</B> The use of interactive transactions is strongly discouraged
* because it can create conditions which are prone to deadlocks between
* operations that may significantly affect performance and will result in
* the cancellation of one or both operations. Use one of the
* {@code moveSubtreeWithRestrictedAccessibility} methods instead.
* </BLOCKQUOTE>
* Moves a single leaf entry using a pair of interactive transactions. The
* logic used to accomplish this is as follows:
* <OL>
* <LI>Start an interactive transaction in the source server.</LI>
* <LI>Start an interactive transaction in the target server.</LI>
* <LI>Read the entry from the source server. The search request will have
* a subtree scope with a size limit of one, a filter of
* "(objectClass=*)", will request all user and operational attributes,
* and will include the following request controls: interactive
* transaction specification, ManageDsaIT, LDAP subentries, return
* conflict entries, soft-deleted entry access, real attributes only,
* and operation purpose.</LI>
* <LI>Add the entry to the target server. The add request will include the
* following controls: interactive transaction specification, ignore
* NO-USER-MODIFICATION, and operation purpose.</LI>
* <LI>Delete the entry from the source server. The delete request will
* include the following controls: interactive transaction
* specification, ManageDsaIT, and operation purpose.</LI>
* <LI>Commit the interactive transaction in the target server.</LI>
* <LI>Commit the interactive transaction in the source server.</LI>
* </OL>
* Conditions which could result in an incomplete move include:
* <UL>
* <LI>The commit in the target server succeeds but the commit in the
* source server fails. In this case, the entry may end up in both
* servers, requiring manual cleanup. If this occurs, then the result
* returned from this method will indicate this condition.</LI>
* <LI>The account used to read entries from the source server does not have
* permission to see all attributes in all entries. In this case, the
* target server will include only a partial representation of the entry
* in the source server. To avoid this problem, ensure that the account
* used to read from the source server has sufficient access rights to
* see all attributes in the entry to move.</LI>
* <LI>The source server participates in replication and a change occurs to
* the entry in a different server in the replicated environment while
* the move is in progress. In this case, those changes may not be
* reflected in the target server. To avoid this problem, it is
* strongly recommended that all write access in the replication
* environment containing the source server be directed to the source
* server during the time that the move is in progress (e.g., using a
* failover load-balancing algorithm in the Directory Proxy
* Server).</LI>
* </UL>
*
* @param sourceConnection A connection established to the source server.
* It should be authenticated as a user with
* permission to perform all of the operations
* against the source server as referenced above.
* @param targetConnection A connection established to the target server.
* It should be authenticated as a user with
* permission to perform all of the operations
* against the target server as referenced above.
* @param entryDN The base DN for the subtree to move.
* @param opPurposeControl An optional operation purpose request control
* that may be included in all requests sent to the
* source and target servers.
* @param suppressRefInt Indicates whether to include a request control
* causing referential integrity updates to be
* suppressed on the source server.
* @param listener An optional listener that may be invoked during
* the course of moving entries from the source
* server to the target server.
*
* @return An object with information about the result of the attempted
* subtree move.
*
* @deprecated The use of interactive transactions is strongly discouraged
* because it can create conditions which are prone to deadlocks
* between operations that may significantly affect performance
* and will result in the cancellation of one or both operations.
*/
@Deprecated()
@SuppressWarnings("deprecation")
@NotNull()
public static MoveSubtreeResult moveEntryWithInteractiveTransaction(@NotNull final LDAPConnection sourceConnection, @NotNull final LDAPConnection targetConnection, @NotNull final String entryDN, @Nullable final OperationPurposeRequestControl opPurposeControl, final boolean suppressRefInt, @Nullable final MoveSubtreeListener listener) {
final StringBuilder errorMsg = new StringBuilder();
final StringBuilder adminMsg = new StringBuilder();
final ReverseComparator<DN> reverseComparator = new ReverseComparator<>();
final TreeSet<DN> sourceEntryDNs = new TreeSet<>(reverseComparator);
final AtomicInteger entriesReadFromSource = new AtomicInteger(0);
final AtomicInteger entriesAddedToTarget = new AtomicInteger(0);
final AtomicInteger entriesDeletedFromSource = new AtomicInteger(0);
final AtomicReference<ResultCode> resultCode = new AtomicReference<>();
ASN1OctetString sourceTxnID = null;
ASN1OctetString targetTxnID = null;
boolean sourceServerAltered = false;
boolean targetServerAltered = false;
processingBlock: try {
// Start an interactive transaction in the source server.
final com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationRequestControl sourceTxnControl;
try {
final com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedRequest startTxnRequest;
if (opPurposeControl == null) {
startTxnRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedRequest(entryDN);
} else {
startTxnRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedRequest(entryDN, new Control[] { opPurposeControl });
}
final com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedResult startTxnResult = (com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedResult) sourceConnection.processExtendedOperation(startTxnRequest);
if (startTxnResult.getResultCode() == ResultCode.SUCCESS) {
sourceTxnID = startTxnResult.getTransactionID();
sourceTxnControl = new com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationRequestControl(sourceTxnID, true, true);
} else {
resultCode.compareAndSet(null, startTxnResult.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_START_SOURCE_TXN.get(startTxnResult.getDiagnosticMessage()), errorMsg);
break processingBlock;
}
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_START_SOURCE_TXN.get(StaticUtils.getExceptionMessage(le)), errorMsg);
break processingBlock;
}
// Start an interactive transaction in the target server.
final com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationRequestControl targetTxnControl;
try {
final com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedRequest startTxnRequest;
if (opPurposeControl == null) {
startTxnRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedRequest(entryDN);
} else {
startTxnRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedRequest(entryDN, new Control[] { opPurposeControl });
}
final com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedResult startTxnResult = (com.unboundid.ldap.sdk.unboundidds.extensions.StartInteractiveTransactionExtendedResult) targetConnection.processExtendedOperation(startTxnRequest);
if (startTxnResult.getResultCode() == ResultCode.SUCCESS) {
targetTxnID = startTxnResult.getTransactionID();
targetTxnControl = new com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationRequestControl(targetTxnID, true, true);
} else {
resultCode.compareAndSet(null, startTxnResult.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_START_TARGET_TXN.get(startTxnResult.getDiagnosticMessage()), errorMsg);
break processingBlock;
}
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_START_TARGET_TXN.get(StaticUtils.getExceptionMessage(le)), errorMsg);
break processingBlock;
}
// Perform a search to find all entries in the target subtree, and include
// a search listener that will add each entry to the target server as it
// is returned from the source server.
final Control[] searchControls;
if (opPurposeControl == null) {
searchControls = new Control[] { sourceTxnControl, new DraftLDUPSubentriesRequestControl(true), new ManageDsaITRequestControl(true), new ReturnConflictEntriesRequestControl(true), new SoftDeletedEntryAccessRequestControl(true, true, false), new RealAttributesOnlyRequestControl(true) };
} else {
searchControls = new Control[] { sourceTxnControl, new DraftLDUPSubentriesRequestControl(true), new ManageDsaITRequestControl(true), new ReturnConflictEntriesRequestControl(true), new SoftDeletedEntryAccessRequestControl(true, true, false), new RealAttributesOnlyRequestControl(true), opPurposeControl };
}
final MoveSubtreeTxnSearchListener searchListener = new MoveSubtreeTxnSearchListener(targetConnection, resultCode, errorMsg, entriesReadFromSource, entriesAddedToTarget, sourceEntryDNs, targetTxnControl, opPurposeControl, listener);
final SearchRequest searchRequest = new SearchRequest(searchListener, searchControls, entryDN, SearchScope.SUB, DereferencePolicy.NEVER, 1, 0, false, Filter.createPresenceFilter("objectClass"), "*", "+");
SearchResult searchResult;
try {
searchResult = sourceConnection.search(searchRequest);
} catch (final LDAPSearchException lse) {
Debug.debugException(lse);
searchResult = lse.getSearchResult();
}
if (searchResult.getResultCode() == ResultCode.SUCCESS) {
try {
final com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl txnResult = com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl.get(searchResult);
if ((txnResult == null) || (!txnResult.transactionValid())) {
resultCode.compareAndSet(null, ResultCode.LOCAL_ERROR);
append(ERR_MOVE_ENTRY_SEARCH_TXN_NO_LONGER_VALID.get(), errorMsg);
break processingBlock;
}
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_DECODE_SEARCH_TXN_CONTROL.get(StaticUtils.getExceptionMessage(le)), errorMsg);
break processingBlock;
}
} else {
resultCode.compareAndSet(null, searchResult.getResultCode());
append(ERR_MOVE_SUBTREE_SEARCH_FAILED.get(entryDN, searchResult.getDiagnosticMessage()), errorMsg);
try {
final com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl txnResult = com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl.get(searchResult);
if ((txnResult != null) && (!txnResult.transactionValid())) {
sourceTxnID = null;
}
} catch (final LDAPException le) {
Debug.debugException(le);
}
if (!searchListener.targetTransactionValid()) {
targetTxnID = null;
}
break processingBlock;
}
// If an error occurred during add processing, then fail.
if (resultCode.get() == null) {
targetServerAltered = true;
} else {
break processingBlock;
}
// Delete each of the entries in the source server. The map should
// already be sorted in reverse order (as a result of the comparator used
// when creating it), so it will guarantee children are deleted before
// their parents.
final ArrayList<Control> deleteControlList = new ArrayList<>(4);
deleteControlList.add(sourceTxnControl);
deleteControlList.add(new ManageDsaITRequestControl(true));
if (opPurposeControl != null) {
deleteControlList.add(opPurposeControl);
}
if (suppressRefInt) {
deleteControlList.add(new SuppressReferentialIntegrityUpdatesRequestControl(false));
}
final Control[] deleteControls = new Control[deleteControlList.size()];
deleteControlList.toArray(deleteControls);
for (final DN dn : sourceEntryDNs) {
if (listener != null) {
try {
listener.doPreDeleteProcessing(dn);
} catch (final Exception e) {
Debug.debugException(e);
resultCode.compareAndSet(null, ResultCode.LOCAL_ERROR);
append(ERR_MOVE_SUBTREE_PRE_DELETE_FAILURE.get(dn.toString(), StaticUtils.getExceptionMessage(e)), errorMsg);
break processingBlock;
}
}
LDAPResult deleteResult;
try {
deleteResult = sourceConnection.delete(new DeleteRequest(dn, deleteControls));
} catch (final LDAPException le) {
Debug.debugException(le);
deleteResult = le.toLDAPResult();
}
if (deleteResult.getResultCode() == ResultCode.SUCCESS) {
sourceServerAltered = true;
entriesDeletedFromSource.incrementAndGet();
try {
final com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl txnResult = com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl.get(deleteResult);
if ((txnResult == null) || (!txnResult.transactionValid())) {
resultCode.compareAndSet(null, ResultCode.LOCAL_ERROR);
append(ERR_MOVE_ENTRY_DELETE_TXN_NO_LONGER_VALID.get(dn.toString()), errorMsg);
break processingBlock;
}
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_DECODE_DELETE_TXN_CONTROL.get(dn.toString(), StaticUtils.getExceptionMessage(le)), errorMsg);
break processingBlock;
}
} else {
resultCode.compareAndSet(null, deleteResult.getResultCode());
append(ERR_MOVE_SUBTREE_DELETE_FAILURE.get(dn.toString(), deleteResult.getDiagnosticMessage()), errorMsg);
try {
final com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl txnResult = com.unboundid.ldap.sdk.unboundidds.controls.InteractiveTransactionSpecificationResponseControl.get(deleteResult);
if ((txnResult != null) && (!txnResult.transactionValid())) {
sourceTxnID = null;
}
} catch (final LDAPException le) {
Debug.debugException(le);
}
break processingBlock;
}
if (listener != null) {
try {
listener.doPostDeleteProcessing(dn);
} catch (final Exception e) {
Debug.debugException(e);
resultCode.compareAndSet(null, ResultCode.LOCAL_ERROR);
append(ERR_MOVE_SUBTREE_POST_DELETE_FAILURE.get(dn.toString(), StaticUtils.getExceptionMessage(e)), errorMsg);
break processingBlock;
}
}
}
// Commit the transaction in the target server.
try {
final com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest commitRequest;
if (opPurposeControl == null) {
commitRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(targetTxnID, true);
} else {
commitRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(targetTxnID, true, new Control[] { opPurposeControl });
}
final ExtendedResult commitResult = targetConnection.processExtendedOperation(commitRequest);
if (commitResult.getResultCode() == ResultCode.SUCCESS) {
targetTxnID = null;
} else {
resultCode.compareAndSet(null, commitResult.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_COMMIT_TARGET_TXN.get(commitResult.getDiagnosticMessage()), errorMsg);
break processingBlock;
}
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_COMMIT_TARGET_TXN.get(StaticUtils.getExceptionMessage(le)), errorMsg);
break processingBlock;
}
// Commit the transaction in the source server.
try {
final com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest commitRequest;
if (opPurposeControl == null) {
commitRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(sourceTxnID, true);
} else {
commitRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(sourceTxnID, true, new Control[] { opPurposeControl });
}
final ExtendedResult commitResult = sourceConnection.processExtendedOperation(commitRequest);
if (commitResult.getResultCode() == ResultCode.SUCCESS) {
sourceTxnID = null;
} else {
resultCode.compareAndSet(null, commitResult.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_COMMIT_SOURCE_TXN.get(commitResult.getDiagnosticMessage()), errorMsg);
break processingBlock;
}
} catch (final LDAPException le) {
Debug.debugException(le);
resultCode.compareAndSet(null, le.getResultCode());
append(ERR_MOVE_ENTRY_CANNOT_COMMIT_SOURCE_TXN.get(StaticUtils.getExceptionMessage(le)), errorMsg);
append(ERR_MOVE_ENTRY_EXISTS_IN_BOTH_SERVERS.get(entryDN), adminMsg);
break processingBlock;
}
} finally {
// If the transaction is still active in the target server, then abort it.
if (targetTxnID != null) {
try {
final com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest abortRequest;
if (opPurposeControl == null) {
abortRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(targetTxnID, false);
} else {
abortRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(targetTxnID, false, new Control[] { opPurposeControl });
}
final ExtendedResult abortResult = targetConnection.processExtendedOperation(abortRequest);
if (abortResult.getResultCode() == ResultCode.INTERACTIVE_TRANSACTION_ABORTED) {
targetServerAltered = false;
entriesAddedToTarget.set(0);
append(INFO_MOVE_ENTRY_TARGET_ABORT_SUCCEEDED.get(), errorMsg);
} else {
append(ERR_MOVE_ENTRY_TARGET_ABORT_FAILURE.get(abortResult.getDiagnosticMessage()), errorMsg);
append(ERR_MOVE_ENTRY_TARGET_ABORT_FAILURE_ADMIN_ACTION.get(entryDN), adminMsg);
}
} catch (final Exception e) {
Debug.debugException(e);
append(ERR_MOVE_ENTRY_TARGET_ABORT_FAILURE.get(StaticUtils.getExceptionMessage(e)), errorMsg);
append(ERR_MOVE_ENTRY_TARGET_ABORT_FAILURE_ADMIN_ACTION.get(entryDN), adminMsg);
}
}
// If the transaction is still active in the source server, then abort it.
if (sourceTxnID != null) {
try {
final com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest abortRequest;
if (opPurposeControl == null) {
abortRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(sourceTxnID, false);
} else {
abortRequest = new com.unboundid.ldap.sdk.unboundidds.extensions.EndInteractiveTransactionExtendedRequest(sourceTxnID, false, new Control[] { opPurposeControl });
}
final ExtendedResult abortResult = sourceConnection.processExtendedOperation(abortRequest);
if (abortResult.getResultCode() == ResultCode.INTERACTIVE_TRANSACTION_ABORTED) {
sourceServerAltered = false;
entriesDeletedFromSource.set(0);
append(INFO_MOVE_ENTRY_SOURCE_ABORT_SUCCEEDED.get(), errorMsg);
} else {
append(ERR_MOVE_ENTRY_SOURCE_ABORT_FAILURE.get(abortResult.getDiagnosticMessage()), errorMsg);
append(ERR_MOVE_ENTRY_SOURCE_ABORT_FAILURE_ADMIN_ACTION.get(entryDN), adminMsg);
}
} catch (final Exception e) {
Debug.debugException(e);
append(ERR_MOVE_ENTRY_SOURCE_ABORT_FAILURE.get(StaticUtils.getExceptionMessage(e)), errorMsg);
append(ERR_MOVE_ENTRY_SOURCE_ABORT_FAILURE_ADMIN_ACTION.get(entryDN), adminMsg);
}
}
}
// Construct the result to return to the client.
resultCode.compareAndSet(null, ResultCode.SUCCESS);
final String errorMessage;
if (errorMsg.length() > 0) {
errorMessage = errorMsg.toString();
} else {
errorMessage = null;
}
final String adminActionRequired;
if (adminMsg.length() > 0) {
adminActionRequired = adminMsg.toString();
} else {
adminActionRequired = null;
}
return new MoveSubtreeResult(resultCode.get(), errorMessage, adminActionRequired, sourceServerAltered, targetServerAltered, entriesReadFromSource.get(), entriesAddedToTarget.get(), entriesDeletedFromSource.get());
}
Aggregations