use of org.apache.qpid.proton.amqp.messaging.Rejected 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.Rejected in project hono by eclipse.
the class ProtonBasedInternalCommandConsumer method handleCommandMessage.
void handleCommandMessage(final ProtonDelivery delivery, final Message msg) {
final ProtonBasedCommand command;
try {
command = ProtonBasedCommand.fromRoutedCommandMessage(msg);
} catch (final IllegalArgumentException e) {
log.debug("address of command message is invalid: {}", msg.getAddress());
final Rejected rejected = new Rejected();
rejected.setError(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "invalid command target address"));
delivery.disposition(rejected, true);
return;
}
final CommandHandlerWrapper commandHandler = commandHandlers.getCommandHandler(command.getTenant(), command.getGatewayOrDeviceId());
if (commandHandler != null && commandHandler.getGatewayId() != null) {
// Gateway information set in command handler means a gateway has subscribed for commands for a specific device.
// This information isn't getting set in the message (by the Command Router) and therefore has to be adopted manually here.
command.setGatewayId(commandHandler.getGatewayId());
}
final SpanContext spanContext = TracingHelper.extractSpanContext(tracer, msg);
final SpanContext followsFromSpanContext = commandHandler != null ? commandHandler.getConsumerCreationSpanContext() : null;
final Span currentSpan = CommandContext.createSpan(tracer, command, spanContext, followsFromSpanContext, getClass().getSimpleName());
currentSpan.setTag(MessageHelper.APP_PROPERTY_ADAPTER_INSTANCE_ID, adapterInstanceId);
final CommandContext commandContext = new ProtonBasedCommandContext(command, delivery, currentSpan);
if (commandHandler != null) {
log.trace("using [{}] for received command [{}]", commandHandler, command);
// command.isValid() check not done here - it is to be done in the command handler
commandHandler.handleCommand(commandContext);
} else {
log.info("no command handler found for command [{}]", command);
commandContext.release(new NoConsumerException("no command handler found for command"));
}
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class ProtonBasedInternalCommandSender method sendCommand.
@Override
public Future<Void> sendCommand(final CommandContext commandContext, final String adapterInstanceId) {
Objects.requireNonNull(commandContext);
Objects.requireNonNull(adapterInstanceId);
return getOrCreateSenderLink(getTargetAddress(adapterInstanceId)).recover(thr -> Future.failedFuture(StatusCodeMapper.toServerError(thr))).compose(sender -> {
final Span span = newChildSpan(commandContext.getTracingContext(), "delegate Command request");
final Command command = commandContext.getCommand();
final Message message = adoptOrCreateMessage(command);
TracingHelper.setDeviceTags(span, command.getTenant(), command.getDeviceId());
if (command.isTargetedAtGateway()) {
MessageHelper.addProperty(message, MessageHelper.APP_PROPERTY_CMD_VIA, command.getGatewayId());
TracingHelper.TAG_GATEWAY_ID.set(span, command.getGatewayId());
}
return sender.sendAndWaitForRawOutcome(message, span);
}).map(delivery -> {
final DeliveryState remoteState = delivery.getRemoteState();
LOG.trace("command [{}] sent to downstream peer; remote state of delivery: {}", commandContext.getCommand(), remoteState);
if (Accepted.class.isInstance(remoteState)) {
commandContext.accept();
} else if (Rejected.class.isInstance(remoteState)) {
final Rejected rejected = (Rejected) remoteState;
commandContext.reject(Optional.ofNullable(rejected.getError()).map(ErrorCondition::getDescription).orElse(null));
} else if (Released.class.isInstance(remoteState)) {
commandContext.release();
} else if (Modified.class.isInstance(remoteState)) {
final Modified modified = (Modified) remoteState;
commandContext.modify(modified.getDeliveryFailed(), modified.getUndeliverableHere());
}
return (Void) null;
}).onFailure(thr -> {
LOG.debug("failed to send command [{}] to downstream peer", commandContext.getCommand(), thr);
if (thr instanceof NoConsumerException) {
TracingHelper.logError(commandContext.getTracingSpan(), "no credit - target adapter instance '" + adapterInstanceId + "' may be offline in which case the device hasn't subscribed again yet");
}
commandContext.release(thr);
});
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class ProtonBasedCommandRouterClientTest method testUnregisterCommandConsumerFailsWithRejectedRequest.
/**
* Verifies that a client invocation of the <em>unregister-command-consumer</em> operation fails
* if the command router service cannot be reached.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUnregisterCommandConsumerFailsWithRejectedRequest(final VertxTestContext ctx) {
// GIVEN a remote peer returning a Rejected disposition
final ProtonDelivery update = mock(ProtonDelivery.class);
when(update.getRemoteState()).thenReturn(new Rejected());
when(update.remotelySettled()).thenReturn(true);
when(sender.send(any(Message.class), VertxMockSupport.anyHandler())).thenAnswer(invocation -> {
final Handler<ProtonDelivery> dispositionHandler = invocation.getArgument(1);
dispositionHandler.handle(update);
return mock(ProtonDelivery.class);
});
// WHEN unregistering the command consumer
client.unregisterCommandConsumer("tenant", "deviceId", "adapterInstanceId", span.context()).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
assertThat(ServiceInvocationException.extractStatusCode(t)).isEqualTo(HttpURLConnection.HTTP_BAD_REQUEST);
// THEN the invocation fails and the span is marked as erroneous
verify(span).setTag(eq(Tags.ERROR.getKey()), eq(Boolean.TRUE));
// and the span is finished
verify(span).finish();
});
ctx.completeNow();
}));
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class ProtonBasedCommandRouterClientTest method testSetLastKnownGatewaysFailsWithRejectedRequest.
/**
* Verifies that a client invocation of the <em>set-last-known-gateway</em> operation fails
* if the command router service cannot be reached.
*
* @param ctx The vert.x test context.
*/
@Test
public void testSetLastKnownGatewaysFailsWithRejectedRequest(final VertxTestContext ctx) {
// GIVEN a remote peer returning a Rejected disposition
final ProtonDelivery update = mock(ProtonDelivery.class);
when(update.getRemoteState()).thenReturn(new Rejected());
when(update.remotelySettled()).thenReturn(true);
when(sender.send(any(Message.class), VertxMockSupport.anyHandler())).thenAnswer(invocation -> {
final Handler<ProtonDelivery> dispositionHandler = invocation.getArgument(1);
dispositionHandler.handle(update);
return mock(ProtonDelivery.class);
});
// WHEN setting last known gateway information
client.setLastKnownGateways("tenant", Map.of("deviceId", "gatewayId"), span.context()).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
assertThat(ServiceInvocationException.extractStatusCode(t)).isEqualTo(HttpURLConnection.HTTP_BAD_REQUEST);
// THEN the invocation fails and the span is marked as erroneous
verify(span).setTag(eq(Tags.ERROR.getKey()), eq(Boolean.TRUE));
// and the span is finished
verify(span).finish();
});
ctx.completeNow();
}));
}
Aggregations