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 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 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();
}));
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class RequestResponseClientTest method testCreateAndSendRequestFailsOnRejectedMessage.
private void testCreateAndSendRequestFailsOnRejectedMessage(final VertxTestContext ctx, final Symbol errorCondition, final Consumer<Throwable> failureAssertions) {
// WHEN sending a request message with some headers and payload
final JsonObject payload = new JsonObject().put("key", "value");
client.compose(c -> c.createAndSendRequest("get", null, payload.toBuffer(), "application/json", SimpleRequestResponseResult::from, span)).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
// THEN the result handler is failed with the expected error
failureAssertions.accept(t);
verify(sample).completed(isA(Rejected.class));
// and a timer has been set to time out the request
final ArgumentCaptor<Handler<Long>> timeoutHandlerCaptor = VertxMockSupport.argumentCaptorHandler();
verify(vertx).setTimer(eq(clientConfig.getRequestTimeout()), timeoutHandlerCaptor.capture());
// triggering the timer now that the request has been handled should not invoke the sampler timeout method
timeoutHandlerCaptor.getValue().handle(1L);
verify(sample, never()).timeout();
});
ctx.completeNow();
}));
// and the peer rejects the message
final Rejected rejected = new Rejected();
rejected.setError(ProtonHelper.condition(errorCondition, "request message cannot be processed"));
final ProtonDelivery delivery = mock(ProtonDelivery.class);
when(delivery.getRemoteState()).thenReturn(rejected);
final ArgumentCaptor<Handler<ProtonDelivery>> dispositionHandlerCaptor = VertxMockSupport.argumentCaptorHandler();
verify(sender).send(any(Message.class), dispositionHandlerCaptor.capture());
dispositionHandlerCaptor.getValue().handle(delivery);
}
Aggregations