use of org.apache.qpid.proton.amqp.messaging.Accepted in project hono by eclipse.
the class ForwardingEventDownstreamAdapterTest method testProcessMessageForwardsMessageToDownstreamSender.
/**
* Verifies that an event uploaded by an upstream client is forwarded to the
* downstream container.
*
* @param ctx The test context.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testProcessMessageForwardsMessageToDownstreamSender(final TestContext ctx) {
final UpstreamReceiver client = newClient();
final ProtonDelivery delivery = mock(ProtonDelivery.class);
final ProtonDelivery downstreamDelivery = mock(ProtonDelivery.class);
when(downstreamDelivery.getRemoteState()).thenReturn(ACCEPTED);
when(downstreamDelivery.remotelySettled()).thenReturn(true);
// GIVEN an adapter with a connection to a downstream container
final Async msgSent = ctx.async();
ProtonSender sender = newMockSender(false);
when(sender.send(any(Message.class), any(Handler.class))).then(invocation -> {
msgSent.complete();
final Handler handler = invocation.getArgument(1);
handler.handle(downstreamDelivery);
return null;
});
ForwardingEventDownstreamAdapter adapter = new ForwardingEventDownstreamAdapter(vertx, newMockSenderFactory(sender));
adapter.setMetrics(mock(MessagingMetrics.class));
adapter.setDownstreamConnectionFactory(newMockConnectionFactory(false));
adapter.start(Future.future());
adapter.addSender(client, sender);
// WHEN processing an event
Message msg = ProtonHelper.message(EVENT_MSG_CONTENT);
MessageHelper.addDeviceId(msg, DEVICE_ID);
adapter.processMessage(client, delivery, msg);
// THEN the message has been delivered to the downstream container
msgSent.await(1000);
// and disposition was returned
verify(delivery).disposition(any(Accepted.class), eq(Boolean.TRUE));
}
use of org.apache.qpid.proton.amqp.messaging.Accepted in project hono by eclipse.
the class AbstractRequestResponseClient method sendRequest.
/**
* Sends a request message via this client's sender link to the peer.
* <p>
* This method first checks if the sender has any credit left. If not, the result handler is failed immediately.
* Otherwise, the request message is sent and a timer is started which fails the result handler,
* if no response is received within <em>requestTimeoutMillis</em> milliseconds.
*
* @param request The message to send.
* @param resultHandler The handler to notify about the outcome of the request.
* @param cacheKey The key to use for caching the response (if the service allows caching).
*/
private final void sendRequest(final Message request, final Handler<AsyncResult<R>> resultHandler, final Object cacheKey) {
context.runOnContext(req -> {
if (sender.sendQueueFull()) {
LOG.debug("cannot send request to peer, no credit left for link [target: {}]", targetAddress);
resultHandler.handle(Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "no credit available for sending request")));
} else {
final Object correlationId = Optional.ofNullable(request.getCorrelationId()).orElse(request.getMessageId());
final TriTuple<Handler<AsyncResult<R>>, Object, Object> handler = TriTuple.of(resultHandler, cacheKey, null);
replyMap.put(correlationId, handler);
sender.send(request, deliveryUpdated -> {
if (Rejected.class.isInstance(deliveryUpdated.getRemoteState())) {
final Rejected rejected = (Rejected) deliveryUpdated.getRemoteState();
if (rejected.getError() != null) {
LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]: {}", targetAddress, request.getSubject(), correlationId, rejected.getError());
cancelRequest(correlationId, Future.failedFuture(StatusCodeMapper.from(rejected.getError())));
} else {
LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]", targetAddress, request.getSubject(), correlationId);
cancelRequest(correlationId, Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST)));
}
} else if (Accepted.class.isInstance(deliveryUpdated.getRemoteState())) {
LOG.trace("service has accepted request [target address: {}, subject: {}, correlation ID: {}]", targetAddress, request.getSubject(), correlationId);
} else {
LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]: {}", targetAddress, request.getSubject(), correlationId, deliveryUpdated.getRemoteState());
cancelRequest(correlationId, Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE)));
}
});
if (requestTimeoutMillis > 0) {
context.owner().setTimer(requestTimeoutMillis, tid -> {
cancelRequest(correlationId, Future.failedFuture(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "request timed out after " + requestTimeoutMillis + "ms")));
});
}
if (LOG.isDebugEnabled()) {
final String deviceId = MessageHelper.getDeviceId(request);
if (deviceId == null) {
LOG.debug("sent request [target address: {}, subject: {}, correlation ID: {}] to service", targetAddress, request.getSubject(), correlationId);
} else {
LOG.debug("sent request [target address: {}, subject: {}, correlation ID: {}, device ID: {}] to service", targetAddress, request.getSubject(), correlationId, deviceId);
}
}
}
});
}
use of org.apache.qpid.proton.amqp.messaging.Accepted in project hono by eclipse.
the class EventSenderImplTest method testSendMessageWaitsForAcceptedOutcome.
/**
* Verifies that the sender waits for the peer to settle and
* accept a message before succeeding the returned future.
*
* @param ctx The vert.x test context.
*/
@SuppressWarnings({ "unchecked" })
@Test
public void testSendMessageWaitsForAcceptedOutcome(final TestContext ctx) {
// GIVEN a sender that has credit
when(sender.sendQueueFull()).thenReturn(Boolean.FALSE);
MessageSender messageSender = new EventSenderImpl(config, sender, "tenant", "telemetry/tenant", context);
final AtomicReference<Handler<ProtonDelivery>> handlerRef = new AtomicReference<>();
doAnswer(invocation -> {
handlerRef.set(invocation.getArgument(1));
return mock(ProtonDelivery.class);
}).when(sender).send(any(Message.class), any(Handler.class));
// WHEN trying to send a message
final Future<ProtonDelivery> result = messageSender.send("device", "some payload", "application/text", "token");
// THEN the message has been sent
// and the result is not completed yet
verify(sender).send(any(Message.class), eq(handlerRef.get()));
assertFalse(result.isComplete());
// until it gets accepted by the peer
ProtonDelivery accepted = mock(ProtonDelivery.class);
when(accepted.remotelySettled()).thenReturn(Boolean.TRUE);
when(accepted.getRemoteState()).thenReturn(new Accepted());
handlerRef.get().handle(accepted);
assertTrue(result.succeeded());
}
use of org.apache.qpid.proton.amqp.messaging.Accepted in project hono by eclipse.
the class ForwardingTelemetryDownstreamAdapterTest method testProcessMessageForwardsMessageToDownstreamSender.
/**
* Verifies that pre-settled telemetry data uploaded by an upstream client is
* forwarded to the downstream container and is accepted and settled immediately.
*
* @param ctx The test context.
*/
@Test
public void testProcessMessageForwardsMessageToDownstreamSender(final TestContext ctx) {
final UpstreamReceiver client = TestSupport.newClient();
// GIVEN an adapter with a connection to a downstream container
final ProtonSender sender = TestSupport.newMockSender(false);
final ForwardingTelemetryDownstreamAdapter adapter = new ForwardingTelemetryDownstreamAdapter(vertx, TestSupport.newMockSenderFactory(sender));
adapter.setMetrics(mock(MessagingMetrics.class));
adapter.setDownstreamConnectionFactory(connectionFactory);
adapter.start(Future.future());
adapter.addSender(client, sender);
// WHEN processing a pre-settled telemetry message
final Message msg = ProtonHelper.message(TELEMETRY_MSG_CONTENT);
MessageHelper.addDeviceId(msg, DEVICE_ID);
final ProtonDelivery upstreamDelivery = mock(ProtonDelivery.class);
when(upstreamDelivery.remotelySettled()).thenReturn(Boolean.TRUE);
adapter.processMessage(client, upstreamDelivery, msg);
// THEN the message is being delivered to the downstream container
verify(sender).send(eq(msg));
// and the upstream delivery is settled with the accepted outcome
verify(upstreamDelivery).disposition(any(Accepted.class), eq(Boolean.TRUE));
}
Aggregations