use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class ProtonBasedCredentialsClientTest method testGetCredentialsFailsWithRejectedRequest.
/**
* Verifies that the client fails if the credentials service cannot be reached.
*
* @param ctx The vert.x test context.
*/
@Test
public void testGetCredentialsFailsWithRejectedRequest(final VertxTestContext ctx) {
// GIVEN a client
givenAClient(cache);
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 getting credentials
client.get("tenant", CredentialsConstants.SECRETS_TYPE_HASHED_PASSWORD, "test-auth", span.context()).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
assertThat(t).isInstanceOf(ClientErrorException.class);
assertThat(((ClientErrorException) t).getErrorCode()).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 ProtonBasedMappingAndDelegatingCommandHandler method mapAndDelegateIncomingCommandMessage.
/**
* Delegates an incoming command to the protocol adapter instance that the target
* device is connected to.
* <p>
* Determines the target gateway (if applicable) and protocol adapter instance for an incoming command
* and delegates the command to the resulting protocol adapter instance.
*
* @param tenantId The tenant that the command target must belong to.
* @param messageDelivery The delivery of the command message.
* @param message The command message.
* @throws NullPointerException if any of the parameters is {@code null}.
*/
public void mapAndDelegateIncomingCommandMessage(final String tenantId, final ProtonDelivery messageDelivery, final Message message) {
Objects.requireNonNull(tenantId);
Objects.requireNonNull(messageDelivery);
Objects.requireNonNull(message);
final Timer.Sample timer = getMetrics().startTimer();
// this is the place where a command message on the "command/${tenant}" address arrives *first*
if (!ResourceIdentifier.isValid(message.getAddress())) {
log.debug("command message has no valid address");
final Rejected rejected = new Rejected();
rejected.setError(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "missing or invalid command target address"));
messageDelivery.disposition(rejected, true);
return;
}
final ResourceIdentifier targetAddress = ResourceIdentifier.fromString(message.getAddress());
final String deviceId = targetAddress.getResourceId();
if (!tenantId.equals(targetAddress.getTenantId())) {
log.debug("command message address contains invalid tenant [expected: {}, found: {}]", tenantId, targetAddress.getTenantId());
final Rejected rejected = new Rejected();
rejected.setError(new ErrorCondition(AmqpError.UNAUTHORIZED_ACCESS, "unauthorized to send command to tenant"));
messageDelivery.disposition(rejected, true);
return;
} else if (Strings.isNullOrEmpty(deviceId)) {
log.debug("invalid command message address: {}", message.getAddress());
final Rejected rejected = new Rejected();
rejected.setError(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "invalid command target address"));
messageDelivery.disposition(rejected, true);
return;
}
final ProtonBasedCommand command = ProtonBasedCommand.from(message);
if (command.isValid()) {
log.trace("received valid command message: {}", command);
} else {
log.debug("received invalid command message: {}", command);
}
final SpanContext spanContext = TracingHelper.extractSpanContext(tracer, message);
final Span currentSpan = createSpan(tenantId, deviceId, spanContext);
command.logToSpan(currentSpan);
final ProtonBasedCommandContext commandContext = new ProtonBasedCommandContext(command, messageDelivery, currentSpan);
if (command.isValid()) {
mapAndDelegateIncomingCommand(commandContext, timer);
} else {
// command message is invalid
commandContext.reject("malformed command message");
reportInvalidCommand(commandContext, timer);
}
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class ProtonBasedMappingAndDelegatingCommandHandlerTest method testMapWithCommandHandlerOnAnotherInstanceWithInvalidMessage.
/**
* Verifies the behaviour of the <em>mapAndDelegateIncomingCommandMessage</em> method in a scenario where
* the command shall get handled by some adapter instance, and the command message is invalid.
*/
@Test
public void testMapWithCommandHandlerOnAnotherInstanceWithInvalidMessage() {
final String deviceId = "4711";
// GIVEN a deviceId commandHandler registered for some adapter instance
final String someAdapterInstance = "someAdapterInstance";
when(commandTargetMapper.getTargetGatewayAndAdapterInstance(anyString(), anyString(), any())).thenReturn(Future.succeededFuture(createTargetAdapterInstanceJson(deviceId, someAdapterInstance)));
// WHEN mapping and delegating the invalid command message
final Message message = getValidCommandMessage(deviceId);
// make the message invalid
message.setSubject(null);
final ProtonDelivery delivery = mock(ProtonDelivery.class);
mappingAndDelegatingCommandHandler.mapAndDelegateIncomingCommandMessage(tenantId, delivery, message);
// THEN the delivery gets REJECTED
verify(delivery).disposition(any(Rejected.class), eq(true));
}
use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.
the class ProtonBasedCommandRouterClientTest method testRegisterCommandConsumerFailsWithRejectedRequest.
/**
* Verifies that a client invocation of the <em>register-command-consumer</em> operation fails
* if the command router service cannot be reached.
*
* @param ctx The vert.x test context.
*/
@Test
public void testRegisterCommandConsumerFailsWithRejectedRequest(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 registering the command consumer
client.registerCommandConsumer("tenant", "deviceId", "adapterInstanceId", null, 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 ProtonBasedCommandContext method updateDelivery.
private void updateDelivery(final DeliveryState deliveryState) {
final Span span = getTracingSpan();
if (delivery.isSettled()) {
final String msg = String.format("cannot complete incoming delivery of command message with outcome '%s' - delivery already settled locally; local state: %s", deliveryState, delivery.getLocalState());
TracingHelper.logError(getTracingSpan(), msg);
LOG.info("{} [{}]", msg, getCommand());
} else {
final boolean wasAlreadyRemotelySettled = delivery.remotelySettled();
// if delivery is already settled remotely call "disposition" anyway to update local state and settle locally
delivery.disposition(deliveryState, true);
if (wasAlreadyRemotelySettled) {
final String msg = String.format("cannot complete incoming delivery of command message with outcome '%s' - delivery already settled remotely; remote state: %s", deliveryState, delivery.getRemoteState());
TracingHelper.logError(getTracingSpan(), msg);
LOG.info("{} [{}]", msg, getCommand());
} else if (Accepted.class.isInstance(deliveryState)) {
LOG.trace("accepted command message [{}]", getCommand());
span.log("accepted command for device");
} else if (Released.class.isInstance(deliveryState)) {
LOG.debug("released command message [{}]", getCommand());
TracingHelper.logError(span, "released command for device");
} else if (Modified.class.isInstance(deliveryState)) {
final Modified modified = (Modified) deliveryState;
LOG.debug("modified command message [{}]", getCommand());
TracingHelper.logError(span, "modified command for device" + (Boolean.TRUE.equals(modified.getDeliveryFailed()) ? "; delivery failed" : "") + (Boolean.TRUE.equals(modified.getUndeliverableHere()) ? "; undeliverable here" : ""));
} else if (Rejected.class.isInstance(deliveryState)) {
final ErrorCondition errorCondition = ((Rejected) deliveryState).getError();
LOG.debug("rejected command message [error: {}, command: {}]", errorCondition, getCommand());
TracingHelper.logError(span, "rejected command for device" + ((errorCondition != null && errorCondition.getDescription() != null) ? "; error: " + errorCondition.getDescription() : ""));
} else {
LOG.warn("unexpected delivery state [{}] when settling command message [{}]", deliveryState, getCommand());
TracingHelper.logError(span, "unexpected delivery state: " + deliveryState);
}
}
span.finish();
}
Aggregations