use of org.apache.qpid.proton.amqp.messaging.Released in project activemq-artemis by apache.
the class AmqpSender method processDeliveryUpdates.
@Override
public void processDeliveryUpdates(AmqpConnection connection, Delivery updated) throws IOException {
List<Delivery> toRemove = new ArrayList<>();
for (Delivery delivery : pending) {
DeliveryState state = delivery.getRemoteState();
if (state == null) {
continue;
}
doDeliveryUpdateInspection(delivery);
Outcome outcome = null;
if (state instanceof TransactionalState) {
LOG.trace("State of delivery is Transactional, retrieving outcome: {}", state);
outcome = ((TransactionalState) state).getOutcome();
} else if (state instanceof Outcome) {
outcome = (Outcome) state;
} else {
LOG.warn("Message send updated with unsupported state: {}", state);
outcome = null;
}
AsyncResult request = (AsyncResult) delivery.getContext();
Exception deliveryError = null;
if (outcome instanceof Accepted) {
LOG.trace("Outcome of delivery was accepted: {}", delivery);
if (request != null && !request.isComplete()) {
request.onSuccess();
}
} else if (outcome instanceof Rejected) {
LOG.trace("Outcome of delivery was rejected: {}", delivery);
ErrorCondition remoteError = ((Rejected) outcome).getError();
if (remoteError == null) {
remoteError = getEndpoint().getRemoteCondition();
}
deliveryError = AmqpSupport.convertToException(remoteError);
} else if (outcome instanceof Released) {
LOG.trace("Outcome of delivery was released: {}", delivery);
deliveryError = new IOException("Delivery failed: released by receiver");
} else if (outcome instanceof Modified) {
LOG.trace("Outcome of delivery was modified: {}", delivery);
deliveryError = new IOException("Delivery failed: failure at remote");
}
if (deliveryError != null) {
if (request != null && !request.isComplete()) {
request.onFailure(deliveryError);
} else {
connection.fireClientException(deliveryError);
}
}
tagGenerator.returnTag(delivery.getTag());
delivery.settle();
toRemove.add(delivery);
}
pending.removeAll(toRemove);
}
use of org.apache.qpid.proton.amqp.messaging.Released in project activemq-artemis by apache.
the class ProtonServerSenderContext method onMessage.
@Override
public void onMessage(Delivery delivery) throws ActiveMQAMQPException {
if (closed) {
return;
}
OperationContext oldContext = sessionSPI.recoverContext();
try {
Message message = ((MessageReference) delivery.getContext()).getMessage();
boolean preSettle = sender.getRemoteSenderSettleMode() == SenderSettleMode.SETTLED;
DeliveryState remoteState;
connection.lock();
try {
remoteState = delivery.getRemoteState();
} finally {
connection.unlock();
}
boolean settleImmediate = true;
if (remoteState instanceof Accepted) {
// acking again would show an exception but would have no negative effect but best to handle anyway.
if (delivery.isSettled()) {
return;
}
// from dealer, a perf hit but a must
try {
sessionSPI.ack(null, brokerConsumer, message);
} catch (Exception e) {
log.warn(e.toString(), e);
throw ActiveMQAMQPProtocolMessageBundle.BUNDLE.errorAcknowledgingMessage(message.toString(), e.getMessage());
}
} else if (remoteState instanceof TransactionalState) {
// When the message arrives with a TransactionState disposition the ack should
// enlist the message into the transaction associated with the given txn ID.
TransactionalState txState = (TransactionalState) remoteState;
ProtonTransactionImpl tx = (ProtonTransactionImpl) this.sessionSPI.getTransaction(txState.getTxnId(), false);
if (txState.getOutcome() != null) {
settleImmediate = false;
Outcome outcome = txState.getOutcome();
if (outcome instanceof Accepted) {
if (!delivery.remotelySettled()) {
TransactionalState txAccepted = new TransactionalState();
txAccepted.setOutcome(Accepted.getInstance());
txAccepted.setTxnId(txState.getTxnId());
connection.lock();
try {
delivery.disposition(txAccepted);
} finally {
connection.unlock();
}
}
// from dealer, a perf hit but a must
try {
sessionSPI.ack(tx, brokerConsumer, message);
tx.addDelivery(delivery, this);
} catch (Exception e) {
throw ActiveMQAMQPProtocolMessageBundle.BUNDLE.errorAcknowledgingMessage(message.toString(), e.getMessage());
}
}
}
} else if (remoteState instanceof Released) {
try {
sessionSPI.cancel(brokerConsumer, message, false);
} catch (Exception e) {
throw ActiveMQAMQPProtocolMessageBundle.BUNDLE.errorCancellingMessage(message.toString(), e.getMessage());
}
} else if (remoteState instanceof Rejected) {
try {
sessionSPI.reject(brokerConsumer, message);
} catch (Exception e) {
throw ActiveMQAMQPProtocolMessageBundle.BUNDLE.errorCancellingMessage(message.toString(), e.getMessage());
}
} else if (remoteState instanceof Modified) {
try {
Modified modification = (Modified) remoteState;
if (Boolean.TRUE.equals(modification.getUndeliverableHere())) {
message.rejectConsumer(brokerConsumer.sequentialID());
}
if (Boolean.TRUE.equals(modification.getDeliveryFailed())) {
sessionSPI.cancel(brokerConsumer, message, true);
} else {
sessionSPI.cancel(brokerConsumer, message, false);
}
} catch (Exception e) {
throw ActiveMQAMQPProtocolMessageBundle.BUNDLE.errorCancellingMessage(message.toString(), e.getMessage());
}
} else {
log.debug("Received null or unknown disposition for delivery update: " + remoteState);
return;
}
if (!preSettle) {
protonSession.replaceTag(delivery.getTag());
}
if (settleImmediate) {
settle(delivery);
}
} finally {
sessionSPI.afterIO(new IOCallback() {
@Override
public void done() {
connection.flush();
}
@Override
public void onError(int errorCode, String errorMessage) {
connection.flush();
}
});
sessionSPI.resetContext(oldContext);
}
}
use of org.apache.qpid.proton.amqp.messaging.Released in project azure-service-bus-java by Azure.
the class CoreMessageSender method onSendComplete.
@Override
public void onSendComplete(final Delivery delivery) {
final DeliveryState outcome = delivery.getRemoteState();
final String deliveryTag = new String(delivery.getTag());
TRACE_LOGGER.debug("Received ack for delivery. path:{}, linkName:{}, deliveryTag:{}, outcome:{}", CoreMessageSender.this.sendPath, this.sendLink.getName(), deliveryTag, outcome);
final SendWorkItem<Void> pendingSendWorkItem = this.pendingSendsData.remove(deliveryTag);
if (pendingSendWorkItem != null) {
if (outcome instanceof Accepted) {
this.lastKnownLinkError = null;
this.retryPolicy.resetRetryCount(this.getClientId());
pendingSendWorkItem.cancelTimeoutTask(false);
AsyncUtil.completeFuture(pendingSendWorkItem.getWork(), null);
} else if (outcome instanceof Rejected) {
Rejected rejected = (Rejected) outcome;
ErrorCondition error = rejected.getError();
Exception exception = ExceptionUtil.toException(error);
if (ExceptionUtil.isGeneralError(error.getCondition())) {
this.lastKnownLinkError = exception;
this.lastKnownErrorReportedAt = Instant.now();
}
Duration retryInterval = this.retryPolicy.getNextRetryInterval(this.getClientId(), exception, pendingSendWorkItem.getTimeoutTracker().remaining());
if (retryInterval == null) {
this.cleanupFailedSend(pendingSendWorkItem, exception);
} else {
TRACE_LOGGER.warn("Send failed for delivery '{}'. Will retry after '{}'", deliveryTag, retryInterval);
pendingSendWorkItem.setLastKnownException(exception);
Timer.schedule(() -> {
CoreMessageSender.this.reSendAsync(deliveryTag, pendingSendWorkItem, false);
}, retryInterval, TimerType.OneTimeRun);
}
} else if (outcome instanceof Released) {
this.cleanupFailedSend(pendingSendWorkItem, new OperationCancelledException(outcome.toString()));
} else {
this.cleanupFailedSend(pendingSendWorkItem, new ServiceBusException(false, outcome.toString()));
}
} else {
TRACE_LOGGER.warn("Delivery mismatch. path:{}, linkName:{}, delivery:{}", this.sendPath, this.sendLink.getName(), deliveryTag);
}
}
use of org.apache.qpid.proton.amqp.messaging.Released in project vertx-proton by vert-x3.
the class ProtonPublisherIntTest method testAlternativeDispositionStates.
@Test(timeout = 20000)
public void testAlternativeDispositionStates(TestContext context) throws Exception {
server.close();
final Async receivedMessagesAsync = context.async();
final Async ackedMessagesAsync = context.async();
int messageCount = 4;
List<DeliveryState> deliveryStates = Collections.synchronizedList(new ArrayList<>());
List<Delivery> deliveries = Collections.synchronizedList(new ArrayList<>());
AtomicInteger msgNum = new AtomicInteger(1);
ProtonServer protonServer = null;
try {
protonServer = createServer((serverConnection) -> {
serverConnection.openHandler(result -> {
serverConnection.open();
});
serverConnection.sessionOpenHandler(session -> session.open());
serverConnection.senderOpenHandler(serverSender -> {
LOG.trace("Server sender opened");
serverSender.open();
serverSender.sendQueueDrainHandler(s -> {
for (int i = msgNum.get(); i <= messageCount; i = msgNum.incrementAndGet()) {
int j = i;
serverSender.send(message(String.valueOf(i)), del -> {
LOG.trace("Server received disposition for msg: " + j);
DeliveryState state = del.getRemoteState();
context.assertNotNull(state, "Expected message to have a delivery state");
deliveryStates.add(state);
if (deliveryStates.size() == messageCount) {
ackedMessagesAsync.complete();
}
});
}
});
});
});
// ===== Client Handling =====
AtomicInteger counter = new AtomicInteger(0);
ProtonClient client = ProtonClient.create(vertx);
client.connect("localhost", protonServer.actualPort(), res -> {
context.assertTrue(res.succeeded());
ProtonConnection connection = res.result();
// Create consumer stream of Delivery, which must be manually accepted
ProtonPublisher<Delivery> publisher = ProtonStreams.createDeliveryConsumer(connection, "myAddress");
publisher.subscribe(new Subscriber<Delivery>() {
Subscription sub = null;
@Override
public void onSubscribe(Subscription s) {
sub = s;
LOG.trace("Flowing initial credit");
sub.request(messageCount);
}
@Override
public void onNext(Delivery d) {
int count = counter.incrementAndGet();
deliveries.add(d);
if (count == messageCount) {
LOG.trace("Got all messages, completing async");
receivedMessagesAsync.complete();
}
}
@Override
public void onError(Throwable t) {
if (!receivedMessagesAsync.isCompleted()) {
context.fail("onError called");
}
}
@Override
public void onComplete() {
context.fail("onComplete called");
}
});
connection.open();
});
receivedMessagesAsync.awaitSuccess();
// Small delay to ensure there is a window for any
// unexpected automatic accept to occur
Thread.sleep(50);
// Verify no messages acked yet
context.assertTrue(deliveryStates.isEmpty(), "Unexpected acks count");
// Ack them all
deliveries.get(0).disposition(Accepted.getInstance(), true);
deliveries.get(1).disposition(Released.getInstance(), true);
deliveries.get(2).disposition(new Rejected(), true);
deliveries.get(3).disposition(new Modified(), true);
ackedMessagesAsync.awaitSuccess();
} finally {
if (protonServer != null) {
protonServer.close();
}
}
// Verify the messages were all acked as expected
context.assertEquals(deliveryStates.size(), messageCount, "Unexpected state count: " + deliveryStates);
context.assertTrue(deliveryStates.get(0) instanceof Accepted, "Unexpected state: " + deliveryStates.get(0));
context.assertTrue(deliveryStates.get(1) instanceof Released, "Unexpected state: " + deliveryStates.get(1));
context.assertTrue(deliveryStates.get(2) instanceof Rejected, "Unexpected state: " + deliveryStates.get(2));
context.assertTrue(deliveryStates.get(3) instanceof Modified, "Unexpected state: " + deliveryStates.get(3));
}
use of org.apache.qpid.proton.amqp.messaging.Released in project vertx-proton by vert-x3.
the class ProtonSubscriberIntTest method testAlternativeDispositionStates.
@Test(timeout = 20000)
public void testAlternativeDispositionStates(TestContext context) throws Exception {
server.close();
final Async serverLinkOpenAsync = context.async();
final Async serverReceivedMessageAsync = context.async();
final Async serverLinkCloseAsync = context.async();
int messageCount = 4;
ProtonServer protonServer = null;
try {
protonServer = createServer((serverConnection) -> {
serverConnection.openHandler(result -> {
serverConnection.open();
});
serverConnection.sessionOpenHandler(session -> session.open());
AtomicInteger counter = new AtomicInteger(0);
serverConnection.receiverOpenHandler(serverReceiver -> {
LOG.trace("Server receiver opened");
serverReceiver.setAutoAccept(false);
serverReceiver.handler((delivery, msg) -> {
int msgNum = counter.incrementAndGet();
// We got the message that was sent, validate it
if (LOG.isTraceEnabled()) {
LOG.trace("Server got msg: " + getMessageBody(context, msg));
}
validateMessage(context, msgNum, String.valueOf(msgNum), msg);
switch(msgNum) {
case 1:
{
delivery.disposition(Accepted.getInstance(), true);
break;
}
case 2:
{
delivery.disposition(Released.getInstance(), true);
break;
}
case 3:
{
delivery.disposition(new Rejected(), true);
break;
}
case 4:
{
delivery.disposition(new Modified(), true);
break;
}
}
if (msgNum == messageCount) {
// Received all the messages
serverReceivedMessageAsync.complete();
}
});
serverReceiver.closeHandler(x -> {
serverReceiver.close();
serverLinkCloseAsync.complete();
});
// Set the local terminus details [naively]
serverReceiver.setTarget(serverReceiver.getRemoteTarget().copy());
serverReceiver.open();
serverLinkOpenAsync.complete();
});
});
// ===== Client Handling =====
ProtonClient client = ProtonClient.create(vertx);
client.connect("localhost", protonServer.actualPort(), res -> {
context.assertTrue(res.succeeded());
ProtonConnection connection = res.result();
connection.open();
ProtonSubscriber<Tracker> subscriber = ProtonStreams.createTrackerProducer(connection, "myAddress");
Tracker tracker1 = Tracker.create(message("1"), t -> {
context.assertTrue(t.isAccepted(), "msg should be accepted");
context.assertTrue(t.isRemotelySettled(), "msg should be remotely settled");
});
Tracker tracker2 = Tracker.create(message("2"), t -> {
context.assertFalse(t.isAccepted(), "msg should not be accepted");
context.assertTrue(t.getRemoteState() instanceof Released, "unexpected remote state: " + t.getRemoteState());
context.assertTrue(t.isRemotelySettled(), "msg should be remotely settled");
});
Tracker tracker3 = Tracker.create(message("3"), t -> {
context.assertFalse(t.isAccepted(), "msg should not be accepted");
context.assertTrue(t.getRemoteState() instanceof Rejected, "unexpected remote state: " + t.getRemoteState());
context.assertTrue(t.isRemotelySettled(), "msg should be remotely settled");
});
Tracker tracker4 = Tracker.create(message("4"), t -> {
context.assertFalse(t.isAccepted(), "msg should not be accepted");
context.assertTrue(t.getRemoteState() instanceof Modified, "unexpected remote state: " + t.getRemoteState());
context.assertTrue(t.isRemotelySettled(), "msg should be remotely settled");
});
Publisher<Tracker> producer = Flowable.just(tracker1, tracker2, tracker3, tracker4);
producer.subscribe(subscriber);
});
serverLinkOpenAsync.awaitSuccess();
serverReceivedMessageAsync.awaitSuccess();
serverLinkCloseAsync.awaitSuccess();
} finally {
if (protonServer != null) {
protonServer.close();
}
}
}
Aggregations