use of org.apache.qpid.server.protocol.v1_0.type.transaction.Discharge in project qpid-broker-j by apache.
the class TxnCoordinatorReceivingLinkEndpoint method receiveDelivery.
@Override
protected Error receiveDelivery(Delivery delivery) {
// Only interested in the amqp-value section that holds the message to the coordinator
try (QpidByteBuffer payload = delivery.getPayload()) {
List<EncodingRetainingSection<?>> sections = getSectionDecoder().parseAll(payload);
boolean amqpValueSectionFound = false;
for (EncodingRetainingSection section : sections) {
try {
if (section instanceof AmqpValueSection) {
if (amqpValueSectionFound) {
throw new ConnectionScopedRuntimeException("Received more than one AmqpValue sections");
}
amqpValueSectionFound = true;
Object command = section.getValue();
Session_1_0 session = getSession();
AMQPConnection_1_0<?> connection = session.getConnection();
connection.receivedComplete();
if (command instanceof Declare) {
final IdentifiedTransaction txn = connection.createIdentifiedTransaction();
_createdTransactions.put(txn.getId(), txn.getServerTransaction());
long notificationRepeatPeriod = getSession().getContextValue(Long.class, Session.TRANSACTION_TIMEOUT_NOTIFICATION_REPEAT_PERIOD);
connection.registerTransactionTickers(txn.getServerTransaction(), this::doTimeoutAction, notificationRepeatPeriod);
Declared state = new Declared();
state.setTxnId(Session_1_0.integerToTransactionId(txn.getId()));
updateDisposition(delivery.getDeliveryTag(), state, true);
} else if (command instanceof Discharge) {
Discharge discharge = (Discharge) command;
Error error = discharge(discharge.getTxnId(), Boolean.TRUE.equals(discharge.getFail()));
final DeliveryState outcome;
if (error == null) {
outcome = new Accepted();
} else if (Arrays.asList(getSource().getOutcomes()).contains(Rejected.REJECTED_SYMBOL)) {
final Rejected rejected = new Rejected();
rejected.setError(error);
outcome = rejected;
error = null;
} else {
outcome = null;
}
if (error == null) {
updateDisposition(delivery.getDeliveryTag(), outcome, true);
}
return error;
} else {
throw new ConnectionScopedRuntimeException(String.format("Received unknown command '%s'", command.getClass().getSimpleName()));
}
}
} finally {
section.dispose();
}
}
if (!amqpValueSectionFound) {
throw new ConnectionScopedRuntimeException("Received no AmqpValue section");
}
} catch (AmqpErrorException e) {
return e.getError();
}
return null;
}
use of org.apache.qpid.server.protocol.v1_0.type.transaction.Discharge in project qpid-broker-j by apache.
the class TxnCoordinatorReceivingLinkEndpoint method discharge.
private Error discharge(Binary transactionIdAsBinary, boolean fail) {
Error error = null;
Integer transactionId = null;
ServerTransaction txn = null;
try {
transactionId = Session_1_0.transactionIdToInteger(transactionIdAsBinary);
txn = _createdTransactions.get(transactionId);
} catch (UnknownTransactionException | IllegalArgumentException e) {
// handle error below
}
if (txn != null) {
AMQPConnection_1_0<?> connection = getSession().getConnection();
if (fail) {
txn.rollback();
connection.incrementTransactionRollbackCounter();
} else if (!(txn instanceof LocalTransaction && ((LocalTransaction) txn).isRollbackOnly())) {
txn.commit();
} else {
txn.rollback();
connection.incrementTransactionRollbackCounter();
error = new Error();
error.setCondition(TransactionError.TRANSACTION_ROLLBACK);
error.setDescription("The transaction was marked as rollback only due to an earlier issue (e.g. a published message was sent settled but could not be enqueued)");
}
_createdTransactions.remove(transactionId);
connection.unregisterTransactionTickers(txn);
connection.removeTransaction(transactionId);
connection.decrementTransactionOpenCounter();
} else {
error = new Error();
error.setCondition(TransactionError.UNKNOWN_ID);
error.setDescription("Unknown transactionId " + transactionIdAsBinary.toString());
}
return error;
}
use of org.apache.qpid.server.protocol.v1_0.type.transaction.Discharge in project qpid-broker-j by apache.
the class AnonymousTerminusTest method transferPreSettledInTransactionToUnknownDestinationWhenRejectOutcomeSupportedByTxController.
@SpecificationTest(section = "Using the Anonymous Terminus for Message Routing. 2.2.2 Routing Errors", description = "It is possible that a message sent to a routing node has an address in the to field" + " of properties which, if used in the address field of target of an attach," + " would result in an unsuccessful link establishment (for example," + " if the address cannot be resolved to a node). In this case the routing node" + " MUST communicate the error back to the sender of the message." + " [...]" + " <Not in spec yet>" + " AMQP-140" + " If a message cannot be routed to the destination implied in the \"to:\" field," + " and the source does not allow for the rejected outcome" + " [...] when messages are being sent within a transaction and have been sent pre-settled." + " In this case the behaviour defined for transactions (of essentially marking" + " the transaction as rollback only) should take precedence. " + "" + " AMQP spec 4.3 Discharging a Transaction" + " If the coordinator is unable to complete the discharge, the coordinator MUST convey" + " the error to the controller as a transaction-error. If the source for the link to" + " the coordinator supports the rejected outcome, then the message MUST be rejected" + " with this outcome carrying the transaction-error.")
@Test
public void transferPreSettledInTransactionToUnknownDestinationWhenRejectOutcomeSupportedByTxController() throws Exception {
try (FrameTransport transport = new FrameTransport(_brokerAddress).connect()) {
final UnsignedInteger linkHandle = UnsignedInteger.ONE;
final Interaction interaction = openInteractionWithAnonymousRelayCapability(transport);
final InteractionTransactionalState txnState = interaction.createTransactionalState(UnsignedInteger.ZERO);
interaction.begin().consumeResponse(Begin.class).txnAttachCoordinatorLink(txnState).txnDeclare(txnState).attachRole(Role.SENDER).attachHandle(linkHandle).attachSourceOutcomes(Accepted.ACCEPTED_SYMBOL, Rejected.REJECTED_SYMBOL).attach().consumeResponse(Attach.class).consumeResponse(Flow.class).transferHandle(linkHandle).transferPayload(generateMessagePayloadToDestination("Unknown")).transferDeliveryTag(_deliveryTag).transferTransactionalState(txnState.getCurrentTransactionId()).transferSettled(Boolean.TRUE).transferDeliveryId(UnsignedInteger.valueOf(1)).transfer();
final Discharge discharge = new Discharge();
discharge.setTxnId(txnState.getCurrentTransactionId());
discharge.setFail(false);
interaction.transferHandle(txnState.getHandle()).transferDeliveryId(UnsignedInteger.valueOf(2)).transferSettled(Boolean.FALSE).transferDeliveryTag(new Binary(("transaction-" + 2).getBytes(StandardCharsets.UTF_8))).transferPayloadData(discharge).transfer();
Disposition dischargeTransactionDisposition = getDispositionForDeliveryId(interaction, UnsignedInteger.valueOf(2));
assertThat(dischargeTransactionDisposition.getSettled(), is(equalTo(true)));
assertThat(dischargeTransactionDisposition.getState(), is(instanceOf(Rejected.class)));
Rejected rejected = (Rejected) dischargeTransactionDisposition.getState();
Error error = rejected.getError();
assertThat(error, is(notNullValue()));
assertThat(error.getCondition(), is(equalTo(TransactionError.TRANSACTION_ROLLBACK)));
}
}
use of org.apache.qpid.server.protocol.v1_0.type.transaction.Discharge in project qpid-broker-j by apache.
the class AnonymousTerminusTest method transferPreSettledInTransactionToUnknownDestinationWhenRejectOutcomeNotSupportedByTxController.
@SpecificationTest(section = "Using the Anonymous Terminus for Message Routing. 2.2.2 Routing Errors", description = "It is possible that a message sent to a routing node has an address in the to field" + " of properties which, if used in the address field of target of an attach," + " would result in an unsuccessful link establishment (for example," + " if the address cannot be resolved to a node). In this case the routing node" + " MUST communicate the error back to the sender of the message." + " [...]" + " <Not in spec yet>" + " AMQP-140" + " If a message cannot be routed to the destination implied in the \"to:\" field," + " and the source does not allow for the rejected outcome" + " [...] when messages are being sent within a transaction and have been sent pre-settled." + " In this case the behaviour defined for transactions (of essentially marking" + " the transaction as rollback only) should take precedence. " + "" + " AMQP spec 4.3 Discharging a Transaction" + " If the coordinator is unable to complete the discharge, the coordinator MUST convey" + " the error to the controller as a transaction-error." + " [...]" + " If the source does not support the rejected outcome, the transactional resource MUST" + " detach the link to the coordinator, with the detach performative carrying" + " the transaction-error")
@Test
public void transferPreSettledInTransactionToUnknownDestinationWhenRejectOutcomeNotSupportedByTxController() throws Exception {
try (FrameTransport transport = new FrameTransport(_brokerAddress).connect()) {
final UnsignedInteger linkHandle = UnsignedInteger.ONE;
final Interaction interaction = openInteractionWithAnonymousRelayCapability(transport);
final InteractionTransactionalState txnState = interaction.createTransactionalState(UnsignedInteger.ZERO);
interaction.begin().consumeResponse(Begin.class).attachRole(Role.SENDER).attachName("testTransactionCoordinator-" + txnState.getHandle()).attachHandle(txnState.getHandle()).attachInitialDeliveryCount(UnsignedInteger.ZERO).attachTarget(new Coordinator()).attachSourceOutcomes(Accepted.ACCEPTED_SYMBOL).attach().consumeResponse(Attach.class).consumeResponse(Flow.class).txnDeclare(txnState).attachRole(Role.SENDER).attachHandle(linkHandle).attachTarget(new Target()).attachName("link-" + linkHandle).attachSourceOutcomes(Accepted.ACCEPTED_SYMBOL, Rejected.REJECTED_SYMBOL).attach().consumeResponse(Attach.class).consumeResponse(Flow.class).transferHandle(linkHandle).transferPayload(generateMessagePayloadToDestination("Unknown")).transferDeliveryTag(_deliveryTag).transferTransactionalState(txnState.getCurrentTransactionId()).transferSettled(Boolean.TRUE).transfer();
final Discharge discharge = new Discharge();
discharge.setTxnId(txnState.getCurrentTransactionId());
discharge.setFail(false);
interaction.transferHandle(txnState.getHandle()).transferSettled(Boolean.FALSE).transferDeliveryId(UnsignedInteger.valueOf(4)).transferDeliveryTag(new Binary(("transaction-" + 4).getBytes(StandardCharsets.UTF_8))).transferPayloadData(discharge).transfer();
Detach transactionCoordinatorDetach = interaction.consumeResponse().getLatestResponse(Detach.class);
Error transactionCoordinatorDetachError = transactionCoordinatorDetach.getError();
assertThat(transactionCoordinatorDetachError, is(notNullValue()));
assertThat(transactionCoordinatorDetachError.getCondition(), is(equalTo(TransactionError.TRANSACTION_ROLLBACK)));
}
}
use of org.apache.qpid.server.protocol.v1_0.type.transaction.Discharge in project qpid-broker-j by apache.
the class AnonymousTerminusTest method transferUnsettledInTransactionToUnknownDestinationWhenRejectedOutcomeNotSupportedBySource.
@Test
public void transferUnsettledInTransactionToUnknownDestinationWhenRejectedOutcomeNotSupportedBySource() throws Exception {
try (FrameTransport transport = new FrameTransport(_brokerAddress).connect()) {
final Interaction interaction = openInteractionWithAnonymousRelayCapability(transport);
final UnsignedInteger linkHandle = UnsignedInteger.ONE;
final InteractionTransactionalState txnState = interaction.createTransactionalState(UnsignedInteger.ZERO);
interaction.begin().consumeResponse(Begin.class).txnAttachCoordinatorLink(txnState).txnDeclare(txnState).attachRole(Role.SENDER).attachSourceOutcomes(Accepted.ACCEPTED_SYMBOL).attachHandle(linkHandle).attach().consumeResponse(Attach.class).consumeResponse(Flow.class).transferHandle(linkHandle).transferPayload(generateMessagePayloadToDestination("Unknown")).transferDeliveryId(UnsignedInteger.valueOf(1)).transferDeliveryTag(_deliveryTag).transferTransactionalState(txnState.getCurrentTransactionId()).transferSettled(Boolean.FALSE).transfer();
Detach senderLinkDetach = interaction.consumeResponse().getLatestResponse(Detach.class);
Error senderLinkDetachError = senderLinkDetach.getError();
assertThat(senderLinkDetachError, is(notNullValue()));
assertThat(senderLinkDetachError.getCondition(), is(equalTo(AmqpError.NOT_FOUND)));
assertThat(senderLinkDetachError.getInfo(), is(notNullValue()));
assertThat(senderLinkDetachError.getInfo().get(DELIVERY_TAG), is(equalTo(_deliveryTag)));
final Discharge discharge = new Discharge();
discharge.setTxnId(txnState.getCurrentTransactionId());
discharge.setFail(false);
interaction.transferHandle(txnState.getHandle()).transferSettled(Boolean.FALSE).transferDeliveryId(UnsignedInteger.valueOf(2)).transferDeliveryTag(new Binary(("transaction-" + 2).getBytes(StandardCharsets.UTF_8))).transferPayloadData(discharge).transfer();
Disposition dischargeTransactionDisposition = getDispositionForDeliveryId(interaction, UnsignedInteger.valueOf(2));
assertThat(dischargeTransactionDisposition.getSettled(), is(equalTo(true)));
assertThat(dischargeTransactionDisposition.getState(), is(instanceOf(Rejected.class)));
Rejected rejected = (Rejected) dischargeTransactionDisposition.getState();
Error error = rejected.getError();
assertThat(error, is(notNullValue()));
assertThat(error.getCondition(), is(equalTo(TransactionError.TRANSACTION_ROLLBACK)));
}
}
Aggregations