Search in sources :

Example 16 with Rejected

use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.

the class VertxBasedAmqpProtocolAdapter method onCommandReceived.

/**
 * Invoked for every valid command that has been received from
 * an application.
 * <p>
 * This implementation simply forwards the command to the device
 * via the given link.
 *
 * @param tenantObject The tenant configuration object.
 * @param sender The link for sending the command to the device.
 * @param commandContext The context in which the adapter receives the command message.
 * @throws NullPointerException if any of the parameters is {@code null}.
 */
protected void onCommandReceived(final TenantObject tenantObject, final ProtonSender sender, final CommandContext commandContext) {
    Objects.requireNonNull(tenantObject);
    Objects.requireNonNull(sender);
    Objects.requireNonNull(commandContext);
    final Command command = commandContext.getCommand();
    final AtomicBoolean isCommandSettled = new AtomicBoolean(false);
    if (sender.sendQueueFull()) {
        log.debug("cannot send command to device: no credit available [{}]", command);
        commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "no credit available for sending command to device"));
        reportSentCommand(tenantObject, commandContext, ProcessingOutcome.UNDELIVERABLE);
    } else {
        final Message msg = ProtonHelper.message();
        msg.setAddress(String.format("%s/%s/%s", CommandConstants.COMMAND_ENDPOINT, command.getTenant(), command.getDeviceId()));
        msg.setCorrelationId(command.getCorrelationId());
        msg.setSubject(command.getName());
        MessageHelper.setPayload(msg, command.getContentType(), command.getPayload());
        if (command.isTargetedAtGateway()) {
            MessageHelper.addDeviceId(msg, command.getDeviceId());
        }
        if (!command.isOneWay()) {
            msg.setReplyTo(String.format("%s/%s/%s", CommandConstants.COMMAND_RESPONSE_ENDPOINT, command.getTenant(), Commands.getDeviceFacingReplyToId(command.getReplyToId(), command.getDeviceId(), command.getMessagingType())));
        }
        final Long timerId;
        if (getConfig().getSendMessageToDeviceTimeout() < 1) {
            timerId = null;
        } else {
            timerId = vertx.setTimer(getConfig().getSendMessageToDeviceTimeout(), tid -> {
                if (log.isDebugEnabled()) {
                    final String linkOrConnectionClosedInfo = HonoProtonHelper.isLinkOpenAndConnected(sender) ? "" : " (link or connection already closed)";
                    log.debug("waiting for delivery update timed out after {}ms{} [{}]", getConfig().getSendMessageToDeviceTimeout(), linkOrConnectionClosedInfo, command);
                }
                if (isCommandSettled.compareAndSet(false, true)) {
                    // timeout reached -> release command
                    commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "timeout waiting for delivery update from device"));
                    reportSentCommand(tenantObject, commandContext, ProcessingOutcome.UNDELIVERABLE);
                } else if (log.isTraceEnabled()) {
                    log.trace("command is already settled and downstream application was already notified [{}]", command);
                }
            });
        }
        sender.send(msg, delivery -> {
            if (timerId != null) {
                // disposition received -> cancel timer
                vertx.cancelTimer(timerId);
            }
            if (!isCommandSettled.compareAndSet(false, true)) {
                log.trace("command is already settled and downstream application was already notified [{}]", command);
            } else {
                // release the command message when the device either
                // rejects or does not settle the command request message.
                final DeliveryState remoteState = delivery.getRemoteState();
                ProcessingOutcome outcome = null;
                if (delivery.remotelySettled()) {
                    if (Accepted.class.isInstance(remoteState)) {
                        outcome = ProcessingOutcome.FORWARDED;
                        commandContext.accept();
                    } else if (Rejected.class.isInstance(remoteState)) {
                        outcome = ProcessingOutcome.UNPROCESSABLE;
                        final String cause = Optional.ofNullable(((Rejected) remoteState).getError()).map(ErrorCondition::getDescription).orElse(null);
                        commandContext.reject(cause);
                    } else if (Released.class.isInstance(remoteState)) {
                        outcome = ProcessingOutcome.UNDELIVERABLE;
                        commandContext.release();
                    } else if (Modified.class.isInstance(remoteState)) {
                        final Modified modified = (Modified) remoteState;
                        outcome = modified.getUndeliverableHere() ? ProcessingOutcome.UNPROCESSABLE : ProcessingOutcome.UNDELIVERABLE;
                        commandContext.modify(modified.getDeliveryFailed(), modified.getUndeliverableHere());
                    }
                } else {
                    log.debug("device did not settle command message [remote state: {}, {}]", remoteState, command);
                    final Map<String, Object> logItems = new HashMap<>(2);
                    logItems.put(Fields.EVENT, "device did not settle command");
                    logItems.put("remote state", remoteState);
                    commandContext.getTracingSpan().log(logItems);
                    commandContext.release(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "device did not settle command"));
                    outcome = ProcessingOutcome.UNDELIVERABLE;
                }
                reportSentCommand(tenantObject, commandContext, outcome);
            }
        });
        final Map<String, Object> items = new HashMap<>(4);
        items.put(Fields.EVENT, "command sent to device");
        if (sender.getRemoteTarget() != null) {
            items.put(Tags.MESSAGE_BUS_DESTINATION.getKey(), sender.getRemoteTarget().getAddress());
        }
        items.put(TracingHelper.TAG_QOS.getKey(), sender.getQoS().name());
        items.put(TracingHelper.TAG_CREDIT.getKey(), sender.getCredit());
        commandContext.getTracingSpan().log(items);
    }
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) ProtonReceiver(io.vertx.proton.ProtonReceiver) LifecycleChange(org.eclipse.hono.notification.deviceregistry.LifecycleChange) DeviceChangeNotification(org.eclipse.hono.notification.deviceregistry.DeviceChangeNotification) Tags(io.opentracing.tag.Tags) ProtonServer(io.vertx.proton.ProtonServer) HonoProtonHelper(org.eclipse.hono.util.HonoProtonHelper) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) Modified(org.apache.qpid.proton.amqp.messaging.Modified) DeviceCredentials(org.eclipse.hono.adapter.auth.device.DeviceCredentials) Map(java.util.Map) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) AuthorizationException(org.eclipse.hono.adapter.AuthorizationException) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Fields(io.opentracing.log.Fields) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) TracingHelper(org.eclipse.hono.tracing.TracingHelper) ProtonSaslAuthenticatorFactory(io.vertx.proton.sasl.ProtonSaslAuthenticatorFactory) AllDevicesOfTenantDeletedNotification(org.eclipse.hono.notification.deviceregistry.AllDevicesOfTenantDeletedNotification) TenantServiceBasedX509Authentication(org.eclipse.hono.adapter.auth.device.TenantServiceBasedX509Authentication) Predicate(java.util.function.Predicate) Collection(java.util.Collection) CommandContext(org.eclipse.hono.client.command.CommandContext) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) ProtonQoS(io.vertx.proton.ProtonQoS) MessageHelper(org.eclipse.hono.util.MessageHelper) Collectors(java.util.stream.Collectors) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Objects(java.util.Objects) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) List(java.util.List) QoS(org.eclipse.hono.service.metric.MetricsTags.QoS) TenantTraceSamplingHelper(org.eclipse.hono.tracing.TenantTraceSamplingHelper) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Optional(java.util.Optional) Span(io.opentracing.Span) ProtonSender(io.vertx.proton.ProtonSender) NotificationEventBusSupport(org.eclipse.hono.notification.NotificationEventBusSupport) ProtonLink(io.vertx.proton.ProtonLink) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) ProtonServerOptions(io.vertx.proton.ProtonServerOptions) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) ConnectionLimitManager(org.eclipse.hono.adapter.limiting.ConnectionLimitManager) Command(org.eclipse.hono.client.command.Command) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) AdapterDisabledException(org.eclipse.hono.adapter.AdapterDisabledException) ServiceInvocationException(org.eclipse.hono.client.ServiceInvocationException) OptionalInt(java.util.OptionalInt) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) Commands(org.eclipse.hono.client.command.Commands) Constants(org.eclipse.hono.util.Constants) CompositeFuture(io.vertx.core.CompositeFuture) ProtonSession(io.vertx.proton.ProtonSession) Symbol(org.apache.qpid.proton.amqp.Symbol) AdapterConnectionsExceededException(org.eclipse.hono.adapter.AdapterConnectionsExceededException) Target(org.apache.qpid.proton.amqp.transport.Target) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) Message(org.apache.qpid.proton.message.Message) HttpUtils(org.eclipse.hono.service.http.HttpUtils) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) TenantChangeNotification(org.eclipse.hono.notification.deviceregistry.TenantChangeNotification) Strings(org.eclipse.hono.util.Strings) UsernamePasswordAuthProvider(org.eclipse.hono.adapter.auth.device.UsernamePasswordAuthProvider) CredentialsApiAuthProvider(org.eclipse.hono.adapter.auth.device.CredentialsApiAuthProvider) AbstractProtocolAdapterBase(org.eclipse.hono.adapter.AbstractProtocolAdapterBase) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) Promise(io.vertx.core.Promise) ServerErrorException(org.eclipse.hono.client.ServerErrorException) ProtonHelper(io.vertx.proton.ProtonHelper) Sample(io.micrometer.core.instrument.Timer.Sample) Released(org.apache.qpid.proton.amqp.messaging.Released) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) Source(org.apache.qpid.proton.amqp.transport.Source) ConnectionAttemptOutcome(org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome) MemoryBasedConnectionLimitStrategy(org.eclipse.hono.adapter.limiting.MemoryBasedConnectionLimitStrategy) X509AuthProvider(org.eclipse.hono.adapter.auth.device.X509AuthProvider) Handler(io.vertx.core.Handler) Collections(java.util.Collections) DefaultConnectionLimitManager(org.eclipse.hono.adapter.limiting.DefaultConnectionLimitManager) Modified(org.apache.qpid.proton.amqp.messaging.Modified) Message(org.apache.qpid.proton.message.Message) HashMap(java.util.HashMap) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) Command(org.eclipse.hono.client.command.Command) UnsignedLong(org.apache.qpid.proton.amqp.UnsignedLong) TenantObject(org.eclipse.hono.util.TenantObject) ServerErrorException(org.eclipse.hono.client.ServerErrorException)

Example 17 with Rejected

use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.

the class VertxBasedAmqpProtocolAdapterTest method testMessageLimitExceededForADownstreamMessage.

private void testMessageLimitExceededForADownstreamMessage(final VertxTestContext ctx, final Message message, final Consumer<Void> postUploadAssertions) {
    final ProtonDelivery delivery = mock(ProtonDelivery.class);
    // AT LEAST ONCE
    when(delivery.remotelySettled()).thenReturn(false);
    final AmqpContext amqpContext = AmqpContext.fromMessage(delivery, message, span, null);
    // GIVEN an AMQP adapter
    givenAnAdapter(properties);
    givenATelemetrySenderForAnyTenant();
    // which is enabled for a tenant with exceeded message limit
    when(resourceLimitChecks.isMessageLimitReached(any(TenantObject.class), anyLong(), any(SpanContext.class))).thenReturn(Future.succeededFuture(Boolean.TRUE));
    // WHEN a device uploads a message to the adapter with AT_LEAST_ONCE delivery semantics
    adapter.onMessageReceived(amqpContext).onComplete(ctx.failing(t -> {
        ctx.verify(() -> {
            // THEN the message limit is exceeded
            assertThat(((ClientErrorException) t).getErrorCode()).isEqualTo(HttpUtils.HTTP_TOO_MANY_REQUESTS);
            // AND the client receives a corresponding REJECTED disposition
            verify(delivery).disposition(argThat(s -> {
                if (s instanceof Rejected) {
                    return AmqpError.RESOURCE_LIMIT_EXCEEDED.equals(((Rejected) s).getError().getCondition());
                } else {
                    return false;
                }
            }), eq(true));
            // AND
            postUploadAssertions.accept(null);
        });
        ctx.completeNow();
    }));
}
Also used : HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) LifecycleChange(org.eclipse.hono.notification.deviceregistry.LifecycleChange) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) MessagingType(org.eclipse.hono.util.MessagingType) EventBus(io.vertx.core.eventbus.EventBus) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Mockito.doAnswer(org.mockito.Mockito.doAnswer) Map(java.util.Map) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) CommandContext(org.eclipse.hono.client.command.CommandContext) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) EventConstants(org.eclipse.hono.util.EventConstants) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) Span(io.opentracing.Span) NotificationEventBusSupport(org.eclipse.hono.notification.NotificationEventBusSupport) Mockito.mock(org.mockito.Mockito.mock) VertxTestContext(io.vertx.junit5.VertxTestContext) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) Target(org.apache.qpid.proton.amqp.messaging.Target) ArgumentMatchers.anyBoolean(org.mockito.ArgumentMatchers.anyBoolean) Commands(org.eclipse.hono.client.command.Commands) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) Vertx(io.vertx.core.Vertx) Mockito.times(org.mockito.Mockito.times) ProtonHelper(io.vertx.proton.ProtonHelper) Released(org.apache.qpid.proton.amqp.messaging.Released) SpanContext(io.opentracing.SpanContext) Mockito.never(org.mockito.Mockito.never) ConnectionAttemptOutcome(org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome) ResourceLimitChecks(org.eclipse.hono.adapter.resourcelimits.ResourceLimitChecks) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) ProtonReceiver(io.vertx.proton.ProtonReceiver) ArgumentMatchers.argThat(org.mockito.ArgumentMatchers.argThat) DeviceChangeNotification(org.eclipse.hono.notification.deviceregistry.DeviceChangeNotification) Context(io.vertx.core.Context) ProtonServer(io.vertx.proton.ProtonServer) Timeout(io.vertx.junit5.Timeout) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) NotificationType(org.eclipse.hono.notification.NotificationType) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Binary(org.apache.qpid.proton.amqp.Binary) AllDevicesOfTenantDeletedNotification(org.eclipse.hono.notification.deviceregistry.AllDevicesOfTenantDeletedNotification) Data(org.apache.qpid.proton.amqp.messaging.Data) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) ProtonQoS(io.vertx.proton.ProtonQoS) Instant(java.time.Instant) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Test(org.junit.jupiter.api.Test) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) Buffer(io.vertx.core.buffer.Buffer) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Mockito.inOrder(org.mockito.Mockito.inOrder) ProtonSender(io.vertx.proton.ProtonSender) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) QoS(org.eclipse.hono.util.QoS) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) AbstractNotification(org.eclipse.hono.notification.AbstractNotification) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) ProtonDelivery(io.vertx.proton.ProtonDelivery) ConnectionLimitManager(org.eclipse.hono.adapter.limiting.ConnectionLimitManager) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ConnectionEventProducer(org.eclipse.hono.adapter.monitoring.ConnectionEventProducer) CommandResponseSender(org.eclipse.hono.client.command.CommandResponseSender) Constants(org.eclipse.hono.util.Constants) ProtocolAdapterTestSupport(org.eclipse.hono.adapter.test.ProtocolAdapterTestSupport) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) RecordImpl(org.apache.qpid.proton.engine.impl.RecordImpl) Message(org.apache.qpid.proton.message.Message) HttpUtils(org.eclipse.hono.service.http.HttpUtils) TenantChangeNotification(org.eclipse.hono.notification.deviceregistry.TenantChangeNotification) InOrder(org.mockito.InOrder) Promise(io.vertx.core.Promise) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) Mockito.verify(org.mockito.Mockito.verify) ApplicationProperties(org.apache.qpid.proton.amqp.messaging.ApplicationProperties) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) Source(org.apache.qpid.proton.amqp.transport.Source) Adapter(org.eclipse.hono.util.Adapter) Handler(io.vertx.core.Handler) Record(org.apache.qpid.proton.engine.Record) TenantObject(org.eclipse.hono.util.TenantObject) SpanContext(io.opentracing.SpanContext) ProtonDelivery(io.vertx.proton.ProtonDelivery) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected)

Example 18 with Rejected

use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.

the class VertxBasedAmqpProtocolAdapterTest method testUploadEventRejectsPresettledMessage.

/**
 * Verifies that the adapter rejects presettled messages with an event address.
 *
 * @param ctx The vert.x test context.
 */
@Test
public void testUploadEventRejectsPresettledMessage(final VertxTestContext ctx) {
    // GIVEN an adapter
    givenAnAdapter(properties);
    givenAnEventSenderForAnyTenant();
    // with an enabled tenant
    givenAConfiguredTenant(TEST_TENANT_ID, true);
    // WHEN a device uploads an event using a presettled message
    final Device gateway = new Device(TEST_TENANT_ID, "device");
    final ProtonDelivery delivery = mock(ProtonDelivery.class);
    // AT MOST ONCE
    when(delivery.remotelySettled()).thenReturn(true);
    final String to = ResourceIdentifier.fromString(EventConstants.EVENT_ENDPOINT).toString();
    final Buffer payload = Buffer.buffer("some payload");
    adapter.onMessageReceived(AmqpContext.fromMessage(delivery, getFakeMessage(to, payload), span, gateway)).onComplete(ctx.failing(t -> {
        ctx.verify(() -> {
            // THEN the adapter does not forward the event
            assertNoEventHasBeenSentDownstream();
            // AND notifies the device by sending back a REJECTED disposition
            verify(delivery).disposition(any(Rejected.class), eq(true));
        });
        ctx.completeNow();
    }));
}
Also used : Buffer(io.vertx.core.buffer.Buffer) HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) LifecycleChange(org.eclipse.hono.notification.deviceregistry.LifecycleChange) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) MessagingType(org.eclipse.hono.util.MessagingType) EventBus(io.vertx.core.eventbus.EventBus) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Mockito.doAnswer(org.mockito.Mockito.doAnswer) Map(java.util.Map) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) CommandContext(org.eclipse.hono.client.command.CommandContext) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) EventConstants(org.eclipse.hono.util.EventConstants) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) Span(io.opentracing.Span) NotificationEventBusSupport(org.eclipse.hono.notification.NotificationEventBusSupport) Mockito.mock(org.mockito.Mockito.mock) VertxTestContext(io.vertx.junit5.VertxTestContext) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) Target(org.apache.qpid.proton.amqp.messaging.Target) ArgumentMatchers.anyBoolean(org.mockito.ArgumentMatchers.anyBoolean) Commands(org.eclipse.hono.client.command.Commands) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) Vertx(io.vertx.core.Vertx) Mockito.times(org.mockito.Mockito.times) ProtonHelper(io.vertx.proton.ProtonHelper) Released(org.apache.qpid.proton.amqp.messaging.Released) SpanContext(io.opentracing.SpanContext) Mockito.never(org.mockito.Mockito.never) ConnectionAttemptOutcome(org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome) ResourceLimitChecks(org.eclipse.hono.adapter.resourcelimits.ResourceLimitChecks) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) ProtonReceiver(io.vertx.proton.ProtonReceiver) ArgumentMatchers.argThat(org.mockito.ArgumentMatchers.argThat) DeviceChangeNotification(org.eclipse.hono.notification.deviceregistry.DeviceChangeNotification) Context(io.vertx.core.Context) ProtonServer(io.vertx.proton.ProtonServer) Timeout(io.vertx.junit5.Timeout) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) NotificationType(org.eclipse.hono.notification.NotificationType) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Binary(org.apache.qpid.proton.amqp.Binary) AllDevicesOfTenantDeletedNotification(org.eclipse.hono.notification.deviceregistry.AllDevicesOfTenantDeletedNotification) Data(org.apache.qpid.proton.amqp.messaging.Data) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) ProtonQoS(io.vertx.proton.ProtonQoS) Instant(java.time.Instant) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Test(org.junit.jupiter.api.Test) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) Buffer(io.vertx.core.buffer.Buffer) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Mockito.inOrder(org.mockito.Mockito.inOrder) ProtonSender(io.vertx.proton.ProtonSender) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) QoS(org.eclipse.hono.util.QoS) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) AbstractNotification(org.eclipse.hono.notification.AbstractNotification) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) ProtonDelivery(io.vertx.proton.ProtonDelivery) ConnectionLimitManager(org.eclipse.hono.adapter.limiting.ConnectionLimitManager) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ConnectionEventProducer(org.eclipse.hono.adapter.monitoring.ConnectionEventProducer) CommandResponseSender(org.eclipse.hono.client.command.CommandResponseSender) Constants(org.eclipse.hono.util.Constants) ProtocolAdapterTestSupport(org.eclipse.hono.adapter.test.ProtocolAdapterTestSupport) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) RecordImpl(org.apache.qpid.proton.engine.impl.RecordImpl) Message(org.apache.qpid.proton.message.Message) HttpUtils(org.eclipse.hono.service.http.HttpUtils) TenantChangeNotification(org.eclipse.hono.notification.deviceregistry.TenantChangeNotification) InOrder(org.mockito.InOrder) Promise(io.vertx.core.Promise) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) Mockito.verify(org.mockito.Mockito.verify) ApplicationProperties(org.apache.qpid.proton.amqp.messaging.ApplicationProperties) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) Source(org.apache.qpid.proton.amqp.transport.Source) Adapter(org.eclipse.hono.util.Adapter) Handler(io.vertx.core.Handler) Record(org.apache.qpid.proton.engine.Record) ProtonDelivery(io.vertx.proton.ProtonDelivery) Device(org.eclipse.hono.auth.Device) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) Test(org.junit.jupiter.api.Test)

Example 19 with Rejected

use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.

the class VertxBasedAmqpProtocolAdapterTest method testUploadEventFailsForGatewayOfDifferentTenant.

/**
 * Verifies that a request from a gateway to upload an event on behalf of a device that belongs
 * to another tenant than the gateway fails.
 *
 * @param ctx The vert.x test context.
 */
@Test
public void testUploadEventFailsForGatewayOfDifferentTenant(final VertxTestContext ctx) {
    // GIVEN an adapter
    givenAnAdapter(properties);
    givenAnEventSenderForAnyTenant();
    // with an enabled tenant
    givenAConfiguredTenant(TEST_TENANT_ID, true);
    // WHEN a gateway uploads an event on behalf of a device of another tenant
    final Device gateway = new Device(TEST_TENANT_ID, "gw");
    final ProtonDelivery delivery = mock(ProtonDelivery.class);
    // AT LEAST ONCE
    when(delivery.remotelySettled()).thenReturn(false);
    final String to = ResourceIdentifier.from(EventConstants.EVENT_ENDPOINT, "other-tenant", TEST_DEVICE).toString();
    final Buffer payload = Buffer.buffer("some payload");
    adapter.onMessageReceived(AmqpContext.fromMessage(delivery, getFakeMessage(to, payload), span, gateway)).onComplete(ctx.failing(t -> {
        ctx.verify(() -> {
            // THEN the adapter does not send the event
            assertNoEventHasBeenSentDownstream();
            // AND notifies the device by sending back a REJECTED disposition
            verify(delivery).disposition(any(Rejected.class), eq(true));
        });
        ctx.completeNow();
    }));
}
Also used : Buffer(io.vertx.core.buffer.Buffer) HttpURLConnection(java.net.HttpURLConnection) ProtonConnection(io.vertx.proton.ProtonConnection) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) LifecycleChange(org.eclipse.hono.notification.deviceregistry.LifecycleChange) ArgumentMatchers.eq(org.mockito.ArgumentMatchers.eq) ProcessingOutcome(org.eclipse.hono.service.metric.MetricsTags.ProcessingOutcome) MessagingType(org.eclipse.hono.util.MessagingType) EventBus(io.vertx.core.eventbus.EventBus) ExtendWith(org.junit.jupiter.api.extension.ExtendWith) Mockito.doAnswer(org.mockito.Mockito.doAnswer) Map(java.util.Map) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) AmqpError(org.apache.qpid.proton.amqp.transport.AmqpError) CommandContext(org.eclipse.hono.client.command.CommandContext) RegistrationAssertion(org.eclipse.hono.util.RegistrationAssertion) EventConstants(org.eclipse.hono.util.EventConstants) VertxMockSupport(org.eclipse.hono.test.VertxMockSupport) Span(io.opentracing.Span) NotificationEventBusSupport(org.eclipse.hono.notification.NotificationEventBusSupport) Mockito.mock(org.mockito.Mockito.mock) VertxTestContext(io.vertx.junit5.VertxTestContext) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) Target(org.apache.qpid.proton.amqp.messaging.Target) ArgumentMatchers.anyBoolean(org.mockito.ArgumentMatchers.anyBoolean) Commands(org.eclipse.hono.client.command.Commands) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) AsyncResult(io.vertx.core.AsyncResult) CommandConstants(org.eclipse.hono.util.CommandConstants) Direction(org.eclipse.hono.service.metric.MetricsTags.Direction) Vertx(io.vertx.core.Vertx) Mockito.times(org.mockito.Mockito.times) ProtonHelper(io.vertx.proton.ProtonHelper) Released(org.apache.qpid.proton.amqp.messaging.Released) SpanContext(io.opentracing.SpanContext) Mockito.never(org.mockito.Mockito.never) ConnectionAttemptOutcome(org.eclipse.hono.service.metric.MetricsTags.ConnectionAttemptOutcome) ResourceLimitChecks(org.eclipse.hono.adapter.resourcelimits.ResourceLimitChecks) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) ProtonReceiver(io.vertx.proton.ProtonReceiver) ArgumentMatchers.argThat(org.mockito.ArgumentMatchers.argThat) DeviceChangeNotification(org.eclipse.hono.notification.deviceregistry.DeviceChangeNotification) Context(io.vertx.core.Context) ProtonServer(io.vertx.proton.ProtonServer) Timeout(io.vertx.junit5.Timeout) EndpointType(org.eclipse.hono.service.metric.MetricsTags.EndpointType) NotificationType(org.eclipse.hono.notification.NotificationType) TracingMockSupport(org.eclipse.hono.test.TracingMockSupport) ResourceIdentifier(org.eclipse.hono.util.ResourceIdentifier) Binary(org.apache.qpid.proton.amqp.Binary) AllDevicesOfTenantDeletedNotification(org.eclipse.hono.notification.deviceregistry.AllDevicesOfTenantDeletedNotification) Data(org.apache.qpid.proton.amqp.messaging.Data) MetricsTags(org.eclipse.hono.service.metric.MetricsTags) ProtonQoS(io.vertx.proton.ProtonQoS) Instant(java.time.Instant) MessageHelper(org.eclipse.hono.util.MessageHelper) VertxExtension(io.vertx.junit5.VertxExtension) Future(io.vertx.core.Future) Device(org.eclipse.hono.auth.Device) Test(org.junit.jupiter.api.Test) ErrorCondition(org.apache.qpid.proton.amqp.transport.ErrorCondition) Buffer(io.vertx.core.buffer.Buffer) CommandConsumer(org.eclipse.hono.client.command.CommandConsumer) Mockito.inOrder(org.mockito.Mockito.inOrder) ProtonSender(io.vertx.proton.ProtonSender) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) QoS(org.eclipse.hono.util.QoS) ArgumentMatchers.any(org.mockito.ArgumentMatchers.any) AbstractNotification(org.eclipse.hono.notification.AbstractNotification) ArgumentMatchers.anyLong(org.mockito.ArgumentMatchers.anyLong) ProtonDelivery(io.vertx.proton.ProtonDelivery) ConnectionLimitManager(org.eclipse.hono.adapter.limiting.ConnectionLimitManager) HashMap(java.util.HashMap) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ConnectionEventProducer(org.eclipse.hono.adapter.monitoring.ConnectionEventProducer) CommandResponseSender(org.eclipse.hono.client.command.CommandResponseSender) Constants(org.eclipse.hono.util.Constants) ProtocolAdapterTestSupport(org.eclipse.hono.adapter.test.ProtocolAdapterTestSupport) TelemetryConstants(org.eclipse.hono.util.TelemetryConstants) ArgumentCaptor(org.mockito.ArgumentCaptor) RecordImpl(org.apache.qpid.proton.engine.impl.RecordImpl) Message(org.apache.qpid.proton.message.Message) HttpUtils(org.eclipse.hono.service.http.HttpUtils) TenantChangeNotification(org.eclipse.hono.notification.deviceregistry.TenantChangeNotification) InOrder(org.mockito.InOrder) Promise(io.vertx.core.Promise) Mockito.when(org.mockito.Mockito.when) Truth.assertThat(com.google.common.truth.Truth.assertThat) Mockito.verify(org.mockito.Mockito.verify) ApplicationProperties(org.apache.qpid.proton.amqp.messaging.ApplicationProperties) CommandResponse(org.eclipse.hono.client.command.CommandResponse) TenantObject(org.eclipse.hono.util.TenantObject) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) Source(org.apache.qpid.proton.amqp.transport.Source) Adapter(org.eclipse.hono.util.Adapter) Handler(io.vertx.core.Handler) Record(org.apache.qpid.proton.engine.Record) ProtonDelivery(io.vertx.proton.ProtonDelivery) Device(org.eclipse.hono.auth.Device) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) Test(org.junit.jupiter.api.Test)

Example 20 with Rejected

use of org.apache.qpid.proton.amqp.messaging.Rejected in project hono by eclipse.

the class RequestResponseClient 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.
 * <p>
 * The given span is never finished by this method.
 *
 * @param request The message to send.
 * @param responseMapper A function mapping a raw AMQP message and a proton delivery to the response type.
 * @param currentSpan The <em>Opentracing</em> span used to trace the request execution.
 * @return A future indicating the outcome of the operation.
 *         The future will be failed with a {@link ServerErrorException} if the request cannot be sent to
 *         the remote service, e.g. because there is no connection to the service or there are no credits
 *         available for sending the request or the request timed out.
 */
private Future<R> sendRequest(final Message request, final BiFunction<Message, ProtonDelivery, R> responseMapper, final Span currentSpan) {
    final String requestTargetAddress = Optional.ofNullable(request.getAddress()).orElse(linkTargetAddress);
    Tags.MESSAGE_BUS_DESTINATION.set(currentSpan, requestTargetAddress);
    Tags.SPAN_KIND.set(currentSpan, Tags.SPAN_KIND_CLIENT);
    Tags.HTTP_METHOD.set(currentSpan, request.getSubject());
    if (tenantId != null) {
        currentSpan.setTag(MessageHelper.APP_PROPERTY_TENANT_ID, tenantId);
    }
    return connection.executeOnContext((Promise<R> res) -> {
        if (sender.sendQueueFull()) {
            LOG.debug("cannot send request to peer, no credit left for link [link target: {}]", linkTargetAddress);
            res.fail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "no credit available for sending request"));
            sampler.queueFull(tenantId);
        } else {
            final Map<String, Object> details = new HashMap<>(3);
            final Object correlationId = Optional.ofNullable(request.getCorrelationId()).orElse(request.getMessageId());
            if (correlationId instanceof String) {
                details.put(TracingHelper.TAG_CORRELATION_ID.getKey(), correlationId);
            }
            details.put(TracingHelper.TAG_CREDIT.getKey(), sender.getCredit());
            details.put(TracingHelper.TAG_QOS.getKey(), sender.getQoS().toString());
            currentSpan.log(details);
            final TriTuple<Promise<R>, BiFunction<Message, ProtonDelivery, R>, Span> handler = TriTuple.of(res, responseMapper, currentSpan);
            TracingHelper.injectSpanContext(connection.getTracer(), currentSpan.context(), request);
            replyMap.put(correlationId, handler);
            final SendMessageSampler.Sample sample = sampler.start(tenantId);
            sender.send(request, deliveryUpdated -> {
                final Promise<R> failedResult = Promise.promise();
                final DeliveryState remoteState = deliveryUpdated.getRemoteState();
                sample.completed(remoteState);
                if (Rejected.class.isInstance(remoteState)) {
                    final Rejected rejected = (Rejected) remoteState;
                    if (rejected.getError() != null) {
                        LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]: {}", requestTargetAddress, request.getSubject(), correlationId, rejected.getError());
                        failedResult.fail(StatusCodeMapper.fromTransferError(rejected.getError()));
                        cancelRequest(correlationId, failedResult.future());
                    } else {
                        LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}]", requestTargetAddress, request.getSubject(), correlationId);
                        failedResult.fail(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST));
                        cancelRequest(correlationId, failedResult.future());
                    }
                } else if (Accepted.class.isInstance(remoteState)) {
                    LOG.trace("service has accepted request [target address: {}, subject: {}, correlation ID: {}]", requestTargetAddress, request.getSubject(), correlationId);
                    currentSpan.log("request accepted by peer");
                    // if no reply-to is set, the request is assumed to be one-way (no response is expected)
                    if (request.getReplyTo() == null) {
                        if (replyMap.remove(correlationId) != null) {
                            res.complete();
                        } else {
                            LOG.trace("accepted request won't be acted upon, request already cancelled [target address: {}, subject: {}, correlation ID: {}]", requestTargetAddress, request.getSubject(), correlationId);
                        }
                    }
                } else if (Released.class.isInstance(remoteState)) {
                    LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}], remote state: {}", requestTargetAddress, request.getSubject(), correlationId, remoteState);
                    failedResult.fail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE));
                    cancelRequest(correlationId, failedResult.future());
                } else if (Modified.class.isInstance(remoteState)) {
                    LOG.debug("service did not accept request [target address: {}, subject: {}, correlation ID: {}], remote state: {}", requestTargetAddress, request.getSubject(), correlationId, remoteState);
                    final Modified modified = (Modified) deliveryUpdated.getRemoteState();
                    failedResult.fail(modified.getUndeliverableHere() ? new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND) : new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE));
                    cancelRequest(correlationId, failedResult.future());
                } else if (remoteState == null) {
                    // possible scenario here: sender link got closed while waiting on the delivery update
                    final String furtherInfo = !sender.isOpen() ? ", sender link was closed in between" : "";
                    LOG.warn("got undefined delivery state for service request{} [target address: {}, subject: {}, correlation ID: {}]", furtherInfo, requestTargetAddress, request.getSubject(), correlationId);
                    failedResult.fail(new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE));
                    cancelRequest(correlationId, failedResult.future());
                }
            });
            if (requestTimeoutMillis > 0) {
                connection.getVertx().setTimer(requestTimeoutMillis, tid -> {
                    if (cancelRequest(correlationId, () -> new ServerErrorException(HttpURLConnection.HTTP_UNAVAILABLE, "request timed out after " + requestTimeoutMillis + "ms"))) {
                        sample.timeout();
                    }
                });
            }
            if (LOG.isDebugEnabled()) {
                final String deviceId = MessageHelper.getDeviceId(request);
                if (deviceId == null) {
                    LOG.debug("sent request [target address: {}, subject: {}, correlation ID: {}] to service", requestTargetAddress, request.getSubject(), correlationId);
                } else {
                    LOG.debug("sent request [target address: {}, subject: {}, correlation ID: {}, device ID: {}] to service", requestTargetAddress, request.getSubject(), correlationId, deviceId);
                }
            }
        }
    });
}
Also used : Modified(org.apache.qpid.proton.amqp.messaging.Modified) HashMap(java.util.HashMap) Rejected(org.apache.qpid.proton.amqp.messaging.Rejected) Span(io.opentracing.Span) Accepted(org.apache.qpid.proton.amqp.messaging.Accepted) Promise(io.vertx.core.Promise) DeliveryState(org.apache.qpid.proton.amqp.transport.DeliveryState) BiFunction(java.util.function.BiFunction) ClientErrorException(org.eclipse.hono.client.ClientErrorException) ServerErrorException(org.eclipse.hono.client.ServerErrorException) SendMessageSampler(org.eclipse.hono.client.SendMessageSampler)

Aggregations

Rejected (org.apache.qpid.proton.amqp.messaging.Rejected)59 Message (org.apache.qpid.proton.message.Message)32 ProtonDelivery (io.vertx.proton.ProtonDelivery)28 Handler (io.vertx.core.Handler)27 DeliveryState (org.apache.qpid.proton.amqp.transport.DeliveryState)23 ErrorCondition (org.apache.qpid.proton.amqp.transport.ErrorCondition)23 Accepted (org.apache.qpid.proton.amqp.messaging.Accepted)21 Span (io.opentracing.Span)19 Test (org.junit.jupiter.api.Test)19 ProtonHelper (io.vertx.proton.ProtonHelper)18 ProtonSender (io.vertx.proton.ProtonSender)18 Released (org.apache.qpid.proton.amqp.messaging.Released)18 Future (io.vertx.core.Future)17 HttpURLConnection (java.net.HttpURLConnection)17 MessageHelper (org.eclipse.hono.util.MessageHelper)17 ProtonQoS (io.vertx.proton.ProtonQoS)16 ClientErrorException (org.eclipse.hono.client.ClientErrorException)16 ProtonReceiver (io.vertx.proton.ProtonReceiver)15 ArgumentMatchers.anyString (org.mockito.ArgumentMatchers.anyString)15 Truth.assertThat (com.google.common.truth.Truth.assertThat)14