package com.hwlcn.ldap.ldap.sdk.extensions; import com.hwlcn.ldap.ldap.sdk.Control; import com.hwlcn.ldap.ldap.sdk.ExtendedRequest; import com.hwlcn.ldap.ldap.sdk.ExtendedResult; import com.hwlcn.ldap.ldap.sdk.LDAPConnection; import com.hwlcn.ldap.ldap.sdk.LDAPException; import com.hwlcn.ldap.ldap.sdk.ResultCode; import com.hwlcn.ldap.ldap.sdk.controls.TransactionSpecificationRequestControl; import com.hwlcn.core.annotation.NotMutable; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.util.ThreadSafetyLevel; import static com.hwlcn.ldap.ldap.sdk.extensions.ExtOpMessages.*; /** * This class provides an implementation of the start transaction extended * request as defined in * <A HREF="http://www.ietf.org/rfc/rfc5805.txt">RFC 5805</A>. It may be used * to begin a transaction that allows multiple write operations to be processed * as a single atomic unit. The {@link com.hwlcn.ldap.ldap.sdk.extensions.StartTransactionExtendedResult} that is * returned will include a transaction ID. For each operation that is performed * as part of the transaction, this transaction ID should be included in the * corresponding request through the * {@link com.hwlcn.ldap.ldap.sdk.controls.TransactionSpecificationRequestControl}. Finally, after all requests * for the transaction have been submitted to the server, the * {@link com.hwlcn.ldap.ldap.sdk.extensions.EndTransactionExtendedRequest} should be used to commit that * transaction, or it may also be used to abort the transaction if it is decided * that it is no longer needed. * <BR><BR> * <H2>Example</H2> * The following example demonstrates the process for using LDAP transactions. * It will modify two different entries as a single atomic unit. In each case, * it will use the post-read control to retrieve a copy of the updated entry. * <PRE> * // Send the start transaction operation and get the transaction ID. * StartTransactionExtendedRequest startTxnRequest = * new StartTransactionExtendedRequest(); * StartTransactionExtendedResult startTxnResult = * (StartTransactionExtendedResult) * connection.processExtendedOperation(startTxnRequest); * if (startTxnResult.getResultCode() != ResultCode.SUCCESS) * { * throw new LDAPException(startTxnResult); * } * ASN1OctetString txnID = startTxnResult.getTransactionID(); * * // At this point, we have a transaction available for use. If any error * // occurs, we will want to make sure that the transaction is aborted, so * // use a try/finally block to handle that. * boolean shouldAbort = true; * try * { * // Create and send the first modify request as part of the transaction. * // Make sure to include the transaction specification control and the * // post-read request control in the modify request. * ModifyRequest modifyRequest1 = new ModifyRequest( * "cn=first,dc=example,dc=com", * new Modification(ModificationType.REPLACE, "description", "first"); * modifyRequest1.addControl(new TransactionSpecificationControl(txnID)); * modifyRequest1.addControl(new PostReadRequestControl()); * LDAPResult modifyResult1 = connection.modify(modifyRequest1); * * // Create and send the second modify request as part of the transaction. * // Again, make sure to include the appropriate controls in the request. * ModifyRequest modifyRequest2 = new ModifyRequest( * "cn=second,dc=example,dc=com", * new Modification(ModificationType.REPLACE, "description", "second"); * modifyRequest2.addControl(new TransactionSpecificationControl(txnID)); * modifyRequest2.addControl(new PostReadRequestControl()); * LDAPResult modifyResult2 = connection.modify(modifyRequest1); * * // Now we're ready to commit, which we can do with the end transaction * // request with the commit flag set to true. * EndTransactionExtendedRequest commitRequest = * new EndTransactionExtendedRequest(txnID, true); * EndTransactionExtendedResult commitResult = * (EndTransactionExtendedResult) * connection.processExtendedOperation(commitRequest); * if (commitResult.getResultCode() == ResultCode.SUCCESS) * { * System.out.println("The transaction was committed successfully."); * * // Everything was successful, so we don't need to abort anything. * shouldAbort = false; * * // Get the post-read response control for the first modify operation. * // It's the same process for the second, but this example is already * // long enough so we'll skip it. * Control[] controls = commitResult.getOperationResponseControls( * modifyResult1.getMessageID()); * if (controls != null) * { * for (Control c : controls) * { * if (c instanceof PostReadResponseControl) * { * PostReadResponseControl postReadResponse = * (PostReadResponseControl) c; * System.out.println("First entry after the modification:"); * System.out.println(postReadResponse.getEntry().toLDIFString()); * } * } * } * } * else * { * // The transaction failed for some reason. The response should tell us * // whether it failed because of one of the operations. * int failedOpMessageID = commitResult.getFailedOpMessageID(); * if (failedOpMessageID == modifyResult1.getMessageID()) * { * System.err.println("The transaction failed because of a failure " + * "encountered while processing the first modification."); * } * else if (failedOpMessageID == modifyResult2.getMessageID()) * { * System.err.println("The transaction failed because of a failure " + * "encountered while processing the second modification."); * } * else * { * System.err.println("The transaction failed for some reason other " + * "than either of the modify operations."); * } * * throw new LDAPException(commitResult); * } * } * finally * { * if (shouldAbort) * { * // Setting the commit flag to false in the end transaction request will * // will cause the transaction to be aborted rather than committed. * EndTransactionExtendedRequest abortRequest = * new EndTransactionExtendedRequest(txnID, false); * connection.processExtendedOperation(abortRequest); * } * } * </PRE> */ @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class StartTransactionExtendedRequest extends ExtendedRequest { public static final String START_TRANSACTION_REQUEST_OID = "1.3.6.1.1.21.1"; private static final long serialVersionUID = 7382735226826929629L; static { final TransactionSpecificationRequestControl c = null; } public StartTransactionExtendedRequest() { super(START_TRANSACTION_REQUEST_OID); } public StartTransactionExtendedRequest(final Control[] controls) { super(START_TRANSACTION_REQUEST_OID, controls); } public StartTransactionExtendedRequest(final ExtendedRequest extendedRequest) throws LDAPException { super(extendedRequest); if (extendedRequest.hasValue()) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_START_TXN_REQUEST_HAS_VALUE.get()); } } @Override() public StartTransactionExtendedResult process( final LDAPConnection connection, final int depth) throws LDAPException { final ExtendedResult extendedResponse = super.process(connection, depth); return new StartTransactionExtendedResult(extendedResponse); } @Override() public StartTransactionExtendedRequest duplicate() { return duplicate(getControls()); } @Override() public StartTransactionExtendedRequest duplicate(final Control[] controls) { final StartTransactionExtendedRequest r = new StartTransactionExtendedRequest(controls); r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); return r; } @Override() public String getExtendedRequestName() { return INFO_EXTENDED_REQUEST_NAME_START_TXN.get(); } @Override() public void toString(final StringBuilder buffer) { buffer.append("StartTransactionExtendedRequest("); final Control[] controls = getControls(); if (controls.length > 0) { buffer.append("controls={"); for (int i=0; i < controls.length; i++) { if (i > 0) { buffer.append(", "); } buffer.append(controls[i]); } buffer.append('}'); } buffer.append(')'); } }