use of com.unboundid.ldap.sdk.extensions.AbortedTransactionExtendedResult in project ldapsdk by pingidentity.
the class ResultUtils method formatUnsolicitedNotification.
/**
* Adds a multi-line string representation of the provided unsolicited
* notification to the given list.
*
* @param lines The list to which the lines should be added.
* @param notification The unsolicited notification to be formatted.
* @param comment Indicates whether to prefix each line with an
* octothorpe to indicate that it is a comment.
* @param indent The number of spaces to indent each line.
* @param maxWidth The maximum length of each line in characters,
* including the comment prefix and indent.
*/
public static void formatUnsolicitedNotification(@NotNull final List<String> lines, @NotNull final ExtendedResult notification, final boolean comment, final int indent, final int maxWidth) {
final String prefix = createPrefix(comment, indent);
final String indentPrefix = prefix + " ";
boolean includeRawValue = true;
final String oid = notification.getOID();
if (oid != null) {
if (oid.equals(NoticeOfDisconnectionExtendedResult.NOTICE_OF_DISCONNECTION_RESULT_OID)) {
wrap(lines, INFO_RESULT_UTILS_NOTICE_OF_DISCONNECTION_HEADER.get(), prefix, maxWidth);
wrap(lines, INFO_RESULT_UTILS_RESPONSE_EXTOP_OID.get(oid), indentPrefix, maxWidth);
} else if (oid.equals(AbortedTransactionExtendedResult.ABORTED_TRANSACTION_RESULT_OID)) {
wrap(lines, INFO_RESULT_UTILS_ABORTED_TXN_HEADER.get(), prefix, maxWidth);
wrap(lines, INFO_RESULT_UTILS_RESPONSE_EXTOP_OID.get(oid), indentPrefix, maxWidth);
try {
final AbortedTransactionExtendedResult r = new AbortedTransactionExtendedResult(notification);
final String txnID;
if (StaticUtils.isPrintableString(r.getTransactionID().getValue())) {
txnID = r.getTransactionID().stringValue();
} else {
txnID = "0x" + StaticUtils.toHex(r.getTransactionID().getValue());
}
wrap(lines, INFO_RESULT_UTILS_TXN_ID_HEADER.get(txnID), indentPrefix, maxWidth);
includeRawValue = false;
} catch (final Exception e) {
Debug.debugException(e);
}
} else {
wrap(lines, INFO_RESULT_UTILS_UNSOLICITED_NOTIFICATION_HEADER.get(), prefix, maxWidth);
wrap(lines, INFO_RESULT_UTILS_RESPONSE_EXTOP_OID.get(oid), indentPrefix, maxWidth);
}
} else {
wrap(lines, INFO_RESULT_UTILS_UNSOLICITED_NOTIFICATION_HEADER.get(), prefix, maxWidth);
}
wrap(lines, INFO_RESULT_UTILS_RESULT_CODE.get(String.valueOf(notification.getResultCode())), indentPrefix, maxWidth);
final String diagnosticMessage = notification.getDiagnosticMessage();
if (diagnosticMessage != null) {
wrap(lines, INFO_RESULT_UTILS_DIAGNOSTIC_MESSAGE.get(diagnosticMessage), indentPrefix, maxWidth);
}
final String matchedDN = notification.getMatchedDN();
if (matchedDN != null) {
wrap(lines, INFO_RESULT_UTILS_MATCHED_DN.get(matchedDN), indentPrefix, maxWidth);
}
final String[] referralURLs = notification.getReferralURLs();
if (referralURLs != null) {
for (final String referralURL : referralURLs) {
wrap(lines, INFO_RESULT_UTILS_REFERRAL_URL.get(referralURL), indentPrefix, maxWidth);
}
}
if (includeRawValue) {
final ASN1OctetString value = notification.getValue();
if ((value != null) && (value.getValueLength() > 0)) {
wrap(lines, INFO_RESULT_UTILS_RESPONSE_EXTOP_RAW_VALUE_HEADER.get(), indentPrefix, maxWidth);
// We'll ignore the maximum width for this portion of the output.
for (final String line : StaticUtils.stringToLines(StaticUtils.toHexPlusASCII(value.getValue(), 0))) {
lines.add(prefix + " " + line);
}
}
}
// If there are any controls, then display them. We'll interpret any
// controls that we can, but will fall back to a general display for any
// that we don't recognize or can't parse.
final Control[] controls = notification.getResponseControls();
if (controls != null) {
for (final Control c : controls) {
formatResponseControl(lines, c, comment, indent + 5, maxWidth);
}
}
}
use of com.unboundid.ldap.sdk.extensions.AbortedTransactionExtendedResult in project ldapsdk by pingidentity.
the class ResultUtilsTestCase method getFormatUnsolicitedNotificationData.
/**
* Retrieves a set of data for testing the
* {@code formatUnsolicitedNotification} method.
*
* @return The test data.
*
* @throws Exception If an unexpected problem occurs.
*/
@DataProvider(name = "formatUnsolicitedNotificationData")
public Iterator<Object[]> getFormatUnsolicitedNotificationData() throws Exception {
final LinkedList<Object[]> resultList = new LinkedList<Object[]>();
// A notice of disconnection.
resultList.add(new Object[] { new NoticeOfDisconnectionExtendedResult(ResultCode.SERVER_DOWN, "The server is shutting down."), Arrays.asList("# Notice of Disconnection Unsolicited Notification", "# Extended Result OID: 1.3.6.1.4.1.1466.20036", "# Result Code: 81 (server down)", "# Diagnostic Message: The server is shutting down.") });
// An aborted transaction with a printable transaction ID.
resultList.add(new Object[] { new AbortedTransactionExtendedResult(new ASN1OctetString("txnID"), ResultCode.OTHER, "The transaction was active for too long.", null, null, null), Arrays.asList("# Aborted Transaction Unsolicited Notification", "# Extended Result OID: 1.3.6.1.1.21.4", "# Transaction ID: txnID", "# Result Code: 80 (other)", "# Diagnostic Message: The transaction was active " + "for too long.") });
// An aborted transaction with a non-printable transaction ID.
resultList.add(new Object[] { new AbortedTransactionExtendedResult(new ASN1OctetString(new byte[] { 0x01, 0x23, 0x45 }), ResultCode.OTHER, "The transaction was active for too long.", null, null, null), Arrays.asList("# Aborted Transaction Unsolicited Notification", "# Extended Result OID: 1.3.6.1.1.21.4", "# Transaction ID: 0x012345", "# Result Code: 80 (other)", "# Diagnostic Message: The transaction was active " + "for too long.") });
// A generic unsolicited notification with neither an OID nor a value but
// with all other elements.
final String[] singleReferralURL = { "ldap://ds.example.com:389/dc=example,dc=com" };
final Control[] singleResponseControl = { new Control("1.2.3.4") };
resultList.add(new Object[] { new ExtendedResult(0, ResultCode.SUCCESS, "diag", "dc=example,dc=com", singleReferralURL, null, null, singleResponseControl), Arrays.asList("# Unsolicited Notification", "# Result Code: 0 (success)", "# Diagnostic Message: diag", "# Matched DN: dc=example,dc=com", "# Referral URL: " + "ldap://ds.example.com:389/dc=example,dc=com", "# Response Control:", "# OID: 1.2.3.4", "# Is Critical: false") });
// A generic unsolicited notification with all elements.
final String[] multipleReferralURLs = { "ldap://ds1.example.com:389/dc=example,dc=com", "ldap://ds2.example.com:389/dc=example,dc=com" };
final Control[] multipleResponseControls = { new Control("1.2.3.4"), new Control("1.2.3.5", true, new ASN1OctetString("control value")) };
resultList.add(new Object[] { new ExtendedResult(0, ResultCode.SUCCESS, "diag", "dc=example,dc=com", multipleReferralURLs, "5.6.7.8", new ASN1OctetString("extended operation value"), multipleResponseControls), Arrays.asList("# Unsolicited Notification", "# Extended Result OID: 5.6.7.8", "# Result Code: 0 (success)", "# Diagnostic Message: diag", "# Matched DN: dc=example,dc=com", "# Referral URL: " + "ldap://ds1.example.com:389/dc=example,dc=com", "# Referral URL: " + "ldap://ds2.example.com:389/dc=example,dc=com", "# Extended Result Raw Value:", "# 65 78 74 65 6e 64 65 64 20 6f 70 65 72 61 " + "74 69 extended operati", "# 6f 6e 20 76 61 6c 75 " + "65 on value", "# Response Control:", "# OID: 1.2.3.4", "# Is Critical: false", "# Response Control:", "# OID: 1.2.3.5", "# Is Critical: true", "# Raw Value:", "# 63 6f 6e 74 72 6f 6c 20 76 61 6c 75 " + "65 control value") });
return resultList.iterator();
}
use of com.unboundid.ldap.sdk.extensions.AbortedTransactionExtendedResult in project ldapsdk by pingidentity.
the class InMemoryRequestHandler method processTransactionRequest.
/**
* Determines whether the provided set of controls includes a transaction
* specification request control. If so, then it will verify that it
* references a valid transaction for the client. If the request is part of a
* valid transaction, then the transaction specification request control will
* be removed and the request will be stashed in the client connection state
* so that it can be retrieved and processed when the transaction is
* committed.
*
* @param messageID The message ID for the request to be processed.
* @param request The protocol op for the request to be processed.
* @param controls The set of controls for the request to be processed.
*
* @return The transaction ID for the associated transaction, or {@code null}
* if the request is not part of any transaction.
*
* @throws LDAPException If the transaction specification request control is
* present but does not refer to a valid transaction
* for the associated client connection.
*/
@SuppressWarnings("unchecked")
@Nullable()
private ASN1OctetString processTransactionRequest(final int messageID, @NotNull final ProtocolOp request, @NotNull final Map<String, Control> controls) throws LDAPException {
final TransactionSpecificationRequestControl txnControl = (TransactionSpecificationRequestControl) controls.remove(TransactionSpecificationRequestControl.TRANSACTION_SPECIFICATION_REQUEST_OID);
if (txnControl == null) {
return null;
}
// See if the client has an active transaction. If not, then fail.
final ASN1OctetString txnID = txnControl.getTransactionID();
final ObjectPair<ASN1OctetString, List<LDAPMessage>> txnInfo = (ObjectPair<ASN1OctetString, List<LDAPMessage>>) connectionState.get(TransactionExtendedOperationHandler.STATE_VARIABLE_TXN_INFO);
if (txnInfo == null) {
throw new LDAPException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION, ERR_MEM_HANDLER_TXN_CONTROL_WITHOUT_TXN.get(txnID.stringValue()));
}
// Make sure that the active transaction has a transaction ID that matches
// the transaction ID from the control. If not, then abort the existing
// transaction and fail.
final ASN1OctetString existingTxnID = txnInfo.getFirst();
if (!txnID.stringValue().equals(existingTxnID.stringValue())) {
connectionState.remove(TransactionExtendedOperationHandler.STATE_VARIABLE_TXN_INFO);
connection.sendUnsolicitedNotification(new AbortedTransactionExtendedResult(existingTxnID, ResultCode.CONSTRAINT_VIOLATION, ERR_MEM_HANDLER_TXN_ABORTED_BY_CONTROL_TXN_ID_MISMATCH.get(existingTxnID.stringValue(), txnID.stringValue()), null, null, null));
throw new LDAPException(ResultCode.UNAVAILABLE_CRITICAL_EXTENSION, ERR_MEM_HANDLER_TXN_CONTROL_ID_MISMATCH.get(txnID.stringValue(), existingTxnID.stringValue()));
}
// Stash the request in the transaction state information so that it will
// be processed when the transaction is committed.
txnInfo.getSecond().add(new LDAPMessage(messageID, request, new ArrayList<>(controls.values())));
return txnID;
}
use of com.unboundid.ldap.sdk.extensions.AbortedTransactionExtendedResult in project ldapsdk by pingidentity.
the class TransactionExtendedOperationHandler method handleStartTransaction.
/**
* Performs the appropriate processing for a start transaction extended
* request.
*
* @param handler The in-memory request handler that received the request.
* @param messageID The message ID for the associated request.
* @param request The extended request that was received.
*
* @return The result for the extended operation processing.
*/
@NotNull()
private static StartTransactionExtendedResult handleStartTransaction(@NotNull final InMemoryRequestHandler handler, final int messageID, @NotNull final ExtendedRequest request) {
// If there is already an active transaction on the associated connection,
// then make sure it gets aborted.
final Map<String, Object> connectionState = handler.getConnectionState();
final ObjectPair<?, ?> existingTxnInfo = (ObjectPair<?, ?>) connectionState.remove(STATE_VARIABLE_TXN_INFO);
if (existingTxnInfo != null) {
final ASN1OctetString txnID = (ASN1OctetString) existingTxnInfo.getFirst();
try {
handler.getClientConnection().sendUnsolicitedNotification(new AbortedTransactionExtendedResult(txnID, ResultCode.CONSTRAINT_VIOLATION, ERR_TXN_EXTOP_TXN_ABORTED_BY_NEW_START_TXN.get(txnID.stringValue()), null, null, null));
} catch (final LDAPException le) {
Debug.debugException(le);
return new StartTransactionExtendedResult(new ExtendedResult(le));
}
}
// request.
try {
new StartTransactionExtendedRequest(request);
} catch (final LDAPException le) {
Debug.debugException(le);
return new StartTransactionExtendedResult(messageID, ResultCode.PROTOCOL_ERROR, le.getMessage(), null, null, null, null);
}
// Create a new object with information to use for the transaction. It will
// include the transaction ID and a list of LDAP messages that are part of
// the transaction. Store it in the connection state.
final ASN1OctetString txnID = new ASN1OctetString(String.valueOf(TXN_ID_COUNTER.getAndIncrement()));
final List<LDAPMessage> requestList = new ArrayList<>(10);
final ObjectPair<ASN1OctetString, List<LDAPMessage>> txnInfo = new ObjectPair<>(txnID, requestList);
connectionState.put(STATE_VARIABLE_TXN_INFO, txnInfo);
// Return the response to the client.
return new StartTransactionExtendedResult(messageID, ResultCode.SUCCESS, INFO_TXN_EXTOP_CREATED_TXN.get(txnID.stringValue()), null, null, txnID, null);
}
use of com.unboundid.ldap.sdk.extensions.AbortedTransactionExtendedResult in project ldapsdk by pingidentity.
the class TransactionExtendedOperationHandler method handleEndTransaction.
/**
* Performs the appropriate processing for an end transaction extended
* request.
*
* @param handler The in-memory request handler that received the request.
* @param messageID The message ID for the associated request.
* @param request The extended request that was received.
*
* @return The result for the extended operation processing.
*/
@NotNull()
private static EndTransactionExtendedResult handleEndTransaction(@NotNull final InMemoryRequestHandler handler, final int messageID, @NotNull final ExtendedRequest request) {
// Get information about any transaction currently in progress on the
// connection. If there isn't one, then fail.
final Map<String, Object> connectionState = handler.getConnectionState();
final ObjectPair<?, ?> txnInfo = (ObjectPair<?, ?>) connectionState.remove(STATE_VARIABLE_TXN_INFO);
if (txnInfo == null) {
return new EndTransactionExtendedResult(messageID, ResultCode.CONSTRAINT_VIOLATION, ERR_TXN_EXTOP_END_NO_ACTIVE_TXN.get(), null, null, null, null, null);
}
// Make sure that we can decode the end transaction request.
final ASN1OctetString existingTxnID = (ASN1OctetString) txnInfo.getFirst();
final EndTransactionExtendedRequest endTxnRequest;
try {
endTxnRequest = new EndTransactionExtendedRequest(request);
} catch (final LDAPException le) {
Debug.debugException(le);
try {
handler.getClientConnection().sendUnsolicitedNotification(new AbortedTransactionExtendedResult(existingTxnID, ResultCode.PROTOCOL_ERROR, ERR_TXN_EXTOP_ABORTED_BY_MALFORMED_END_TXN.get(existingTxnID.stringValue()), null, null, null));
} catch (final LDAPException le2) {
Debug.debugException(le2);
}
return new EndTransactionExtendedResult(messageID, ResultCode.PROTOCOL_ERROR, le.getMessage(), null, null, null, null, null);
}
// Make sure that the transaction ID of the existing transaction matches the
// transaction ID from the end transaction request.
final ASN1OctetString targetTxnID = endTxnRequest.getTransactionID();
if (!existingTxnID.stringValue().equals(targetTxnID.stringValue())) {
// transaction has been aborted.
try {
handler.getClientConnection().sendUnsolicitedNotification(new AbortedTransactionExtendedResult(existingTxnID, ResultCode.CONSTRAINT_VIOLATION, ERR_TXN_EXTOP_ABORTED_BY_WRONG_END_TXN.get(existingTxnID.stringValue(), targetTxnID.stringValue()), null, null, null));
} catch (final LDAPException le) {
Debug.debugException(le);
return new EndTransactionExtendedResult(messageID, le.getResultCode(), le.getMessage(), le.getMatchedDN(), le.getReferralURLs(), null, null, le.getResponseControls());
}
return new EndTransactionExtendedResult(messageID, ResultCode.CONSTRAINT_VIOLATION, ERR_TXN_EXTOP_END_WRONG_TXN.get(targetTxnID.stringValue(), existingTxnID.stringValue()), null, null, null, null, null);
}
// If the transaction should be aborted, then we can just send the response.
if (!endTxnRequest.commit()) {
return new EndTransactionExtendedResult(messageID, ResultCode.SUCCESS, INFO_TXN_EXTOP_END_TXN_ABORTED.get(existingTxnID.stringValue()), null, null, null, null, null);
}
// If we've gotten here, then we'll try to commit the transaction. First,
// get a snapshot of the current state so that we can roll back to it if
// necessary.
final InMemoryDirectoryServerSnapshot snapshot = handler.createSnapshot();
boolean rollBack = true;
try {
// Create a map to hold information about response controls from
// operations processed as part of the transaction.
final List<?> requestMessages = (List<?>) txnInfo.getSecond();
final Map<Integer, Control[]> opResponseControls = new LinkedHashMap<>(StaticUtils.computeMapCapacity(requestMessages.size()));
// Iterate through the requests that have been submitted as part of the
// transaction and attempt to process them.
ResultCode resultCode = ResultCode.SUCCESS;
String diagnosticMessage = null;
String failedOpType = null;
Integer failedOpMessageID = null;
txnOpLoop: for (final Object o : requestMessages) {
final LDAPMessage m = (LDAPMessage) o;
switch(m.getProtocolOpType()) {
case LDAPMessage.PROTOCOL_OP_TYPE_ADD_REQUEST:
final LDAPMessage addResponseMessage = handler.processAddRequest(m.getMessageID(), m.getAddRequestProtocolOp(), m.getControls());
final AddResponseProtocolOp addResponseOp = addResponseMessage.getAddResponseProtocolOp();
final List<Control> addControls = addResponseMessage.getControls();
if ((addControls != null) && (!addControls.isEmpty())) {
final Control[] controls = new Control[addControls.size()];
addControls.toArray(controls);
opResponseControls.put(m.getMessageID(), controls);
}
if (addResponseOp.getResultCode() != ResultCode.SUCCESS_INT_VALUE) {
resultCode = ResultCode.valueOf(addResponseOp.getResultCode());
diagnosticMessage = addResponseOp.getDiagnosticMessage();
failedOpType = INFO_TXN_EXTOP_OP_TYPE_ADD.get();
failedOpMessageID = m.getMessageID();
break txnOpLoop;
}
break;
case LDAPMessage.PROTOCOL_OP_TYPE_DELETE_REQUEST:
final LDAPMessage deleteResponseMessage = handler.processDeleteRequest(m.getMessageID(), m.getDeleteRequestProtocolOp(), m.getControls());
final DeleteResponseProtocolOp deleteResponseOp = deleteResponseMessage.getDeleteResponseProtocolOp();
final List<Control> deleteControls = deleteResponseMessage.getControls();
if ((deleteControls != null) && (!deleteControls.isEmpty())) {
final Control[] controls = new Control[deleteControls.size()];
deleteControls.toArray(controls);
opResponseControls.put(m.getMessageID(), controls);
}
if (deleteResponseOp.getResultCode() != ResultCode.SUCCESS_INT_VALUE) {
resultCode = ResultCode.valueOf(deleteResponseOp.getResultCode());
diagnosticMessage = deleteResponseOp.getDiagnosticMessage();
failedOpType = INFO_TXN_EXTOP_OP_TYPE_DELETE.get();
failedOpMessageID = m.getMessageID();
break txnOpLoop;
}
break;
case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_REQUEST:
final LDAPMessage modifyResponseMessage = handler.processModifyRequest(m.getMessageID(), m.getModifyRequestProtocolOp(), m.getControls());
final ModifyResponseProtocolOp modifyResponseOp = modifyResponseMessage.getModifyResponseProtocolOp();
final List<Control> modifyControls = modifyResponseMessage.getControls();
if ((modifyControls != null) && (!modifyControls.isEmpty())) {
final Control[] controls = new Control[modifyControls.size()];
modifyControls.toArray(controls);
opResponseControls.put(m.getMessageID(), controls);
}
if (modifyResponseOp.getResultCode() != ResultCode.SUCCESS_INT_VALUE) {
resultCode = ResultCode.valueOf(modifyResponseOp.getResultCode());
diagnosticMessage = modifyResponseOp.getDiagnosticMessage();
failedOpType = INFO_TXN_EXTOP_OP_TYPE_MODIFY.get();
failedOpMessageID = m.getMessageID();
break txnOpLoop;
}
break;
case LDAPMessage.PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST:
final LDAPMessage modifyDNResponseMessage = handler.processModifyDNRequest(m.getMessageID(), m.getModifyDNRequestProtocolOp(), m.getControls());
final ModifyDNResponseProtocolOp modifyDNResponseOp = modifyDNResponseMessage.getModifyDNResponseProtocolOp();
final List<Control> modifyDNControls = modifyDNResponseMessage.getControls();
if ((modifyDNControls != null) && (!modifyDNControls.isEmpty())) {
final Control[] controls = new Control[modifyDNControls.size()];
modifyDNControls.toArray(controls);
opResponseControls.put(m.getMessageID(), controls);
}
if (modifyDNResponseOp.getResultCode() != ResultCode.SUCCESS_INT_VALUE) {
resultCode = ResultCode.valueOf(modifyDNResponseOp.getResultCode());
diagnosticMessage = modifyDNResponseOp.getDiagnosticMessage();
failedOpType = INFO_TXN_EXTOP_OP_TYPE_MODIFY_DN.get();
failedOpMessageID = m.getMessageID();
break txnOpLoop;
}
break;
}
}
if (resultCode == ResultCode.SUCCESS) {
diagnosticMessage = INFO_TXN_EXTOP_COMMITTED.get(existingTxnID.stringValue());
rollBack = false;
} else {
diagnosticMessage = ERR_TXN_EXTOP_COMMIT_FAILED.get(existingTxnID.stringValue(), failedOpType, failedOpMessageID, diagnosticMessage);
}
return new EndTransactionExtendedResult(messageID, resultCode, diagnosticMessage, null, null, failedOpMessageID, opResponseControls, null);
} finally {
if (rollBack) {
handler.restoreSnapshot(snapshot);
}
}
}
Aggregations