use of io.opentracing.Span in project hono by eclipse.
the class AbstractVertxBasedMqttProtocolAdapterTest method testUploadTelemetryMessageIncludesRetainAnnotation.
/**
* Verifies that the adapter includes a message annotation in a downstream message if the device publishes a message
* with its <em>retain</em> flag set.
*
* @param ctx The vert.x test context.
*/
@Test
public void testUploadTelemetryMessageIncludesRetainAnnotation(final VertxTestContext ctx) {
// GIVEN an adapter with a downstream telemetry consumer
givenAnAdapter(properties);
givenATelemetrySenderForAnyTenant();
// WHEN a device publishes a message with its retain flag set
final MqttEndpoint endpoint = mockEndpoint();
when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
final Buffer payload = Buffer.buffer("hello");
final MqttPublishMessage messageFromDevice = mock(MqttPublishMessage.class);
when(messageFromDevice.qosLevel()).thenReturn(MqttQoS.AT_LEAST_ONCE);
when(messageFromDevice.messageId()).thenReturn(5555555);
when(messageFromDevice.topicName()).thenReturn("t/my-tenant/4712");
when(messageFromDevice.isRetain()).thenReturn(Boolean.TRUE);
when(messageFromDevice.payload()).thenReturn(payload);
final MqttContext context = newMqttContext(messageFromDevice, endpoint, span);
adapter.uploadTelemetryMessage(context, "my-tenant", "4712", payload).onComplete(ctx.succeeding(ok -> {
ctx.verify(() -> {
// THEN the device has received a PUBACK
verify(endpoint).publishAcknowledge(5555555);
// and the message has been sent downstream
// including the "retain" annotation
verify(telemetrySender).sendTelemetry(argThat(tenant -> tenant.getTenantId().equals("my-tenant")), argThat(assertion -> assertion.getDeviceId().equals("4712")), eq(QoS.AT_LEAST_ONCE), any(), any(), argThat(props -> props.get(MessageHelper.ANNOTATION_X_OPT_RETAIN).equals(Boolean.TRUE)), any());
verify(metrics).reportTelemetry(eq(MetricsTags.EndpointType.TELEMETRY), eq("my-tenant"), any(), eq(MetricsTags.ProcessingOutcome.FORWARDED), eq(MetricsTags.QoS.AT_LEAST_ONCE), eq(payload.length()), any());
});
ctx.completeNow();
}));
}
use of io.opentracing.Span in project hono by eclipse.
the class AbstractVertxBasedMqttProtocolAdapterTest method testMessageLimitExceededForACommandResponseMessage.
/**
* Verifies that a command response message is rejected due to the limit exceeded.
*
* @param ctx The vert.x test context.
*/
@Test
public void testMessageLimitExceededForACommandResponseMessage(final VertxTestContext ctx) {
// GIVEN an adapter
givenAnAdapter(properties);
final CommandResponseSender sender = givenACommandResponseSenderForAnyTenant();
// WHEN the message limit exceeds
when(resourceLimitChecks.isMessageLimitReached(any(TenantObject.class), anyLong(), any(SpanContext.class))).thenReturn(Future.succeededFuture(Boolean.TRUE));
// WHEN a device of "tenant" publishes a command response message
final MqttPublishMessage msg = mock(MqttPublishMessage.class);
when(msg.topicName()).thenReturn("e/tenant/device");
when(msg.qosLevel()).thenReturn(MqttQoS.AT_MOST_ONCE);
when(msg.payload()).thenReturn(Buffer.buffer("test"));
adapter.uploadMessage(newMqttContext(msg, mockEndpoint(), span), ResourceIdentifier.fromString(String.format("%s/tenant/device/res/%s/200", getCommandEndpoint(), Commands.encodeRequestIdParameters("cmd123", "to", "deviceId", MessagingType.amqp))), msg).onComplete(ctx.failing(t -> {
ctx.verify(() -> {
// THEN the request fails with a 429 error
assertThat(((ClientErrorException) t).getErrorCode()).isEqualTo(HttpUtils.HTTP_TOO_MANY_REQUESTS);
// AND the response is not being forwarded
verify(sender, never()).sendCommandResponse(any(TenantObject.class), any(RegistrationAssertion.class), any(CommandResponse.class), (SpanContext) any());
// AND has reported the message as unprocessable
verify(metrics).reportCommand(eq(MetricsTags.Direction.RESPONSE), eq("tenant"), any(), eq(MetricsTags.ProcessingOutcome.UNPROCESSABLE), anyInt(), any());
});
ctx.completeNow();
}));
}
use of io.opentracing.Span in project hono by eclipse.
the class AbstractVertxBasedMqttProtocolAdapter method uploadMessage.
private Future<Void> uploadMessage(final MqttContext ctx, final TenantObject tenantObject, final String deviceId, final Buffer payload, final MetricsTags.EndpointType endpoint) {
if (!isPayloadOfIndicatedType(payload, ctx.contentType())) {
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, String.format("Content-Type %s does not match payload", ctx.contentType())));
}
final Span currentSpan = TracingHelper.buildChildSpan(tracer, ctx.getTracingContext(), "upload " + endpoint, getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, tenantObject.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).withTag(TracingHelper.TAG_AUTHENTICATED.getKey(), ctx.authenticatedDevice() != null).start();
final Future<RegistrationAssertion> tokenTracker = getRegistrationAssertion(tenantObject.getTenantId(), deviceId, ctx.authenticatedDevice(), currentSpan.context());
final Future<TenantObject> tenantValidationTracker = CompositeFuture.all(isAdapterEnabled(tenantObject), checkMessageLimit(tenantObject, payload.length(), currentSpan.context())).map(tenantObject);
return CompositeFuture.all(tokenTracker, tenantValidationTracker).compose(ok -> {
final Map<String, Object> props = getDownstreamMessageProperties(ctx);
props.put(MessageHelper.APP_PROPERTY_QOS, ctx.getRequestedQos().ordinal());
addRetainAnnotation(ctx, props, currentSpan);
customizeDownstreamMessageProperties(props, ctx);
if (endpoint == EndpointType.EVENT) {
return getEventSender(tenantObject).sendEvent(tenantObject, tokenTracker.result(), ctx.contentType(), payload, props, currentSpan.context());
} else {
return getTelemetrySender(tenantObject).sendTelemetry(tenantObject, tokenTracker.result(), ctx.getRequestedQos(), ctx.contentType(), payload, props, currentSpan.context());
}
}).map(ok -> {
log.trace("successfully processed message [topic: {}, QoS: {}] from device [tenantId: {}, deviceId: {}]", ctx.message().topicName(), ctx.message().qosLevel(), tenantObject.getTenantId(), deviceId);
// check that the remote MQTT client is still connected before sending PUBACK
if (ctx.isAtLeastOnce() && ctx.deviceEndpoint().isConnected()) {
currentSpan.log(EVENT_SENDING_PUBACK);
ctx.acknowledge();
}
currentSpan.finish();
return ok;
}).recover(t -> {
if (ClientErrorException.class.isInstance(t)) {
final ClientErrorException e = (ClientErrorException) t;
log.debug("cannot process message [endpoint: {}] from device [tenantId: {}, deviceId: {}]: {} - {}", endpoint, tenantObject.getTenantId(), deviceId, e.getErrorCode(), e.getMessage());
} else {
log.debug("cannot process message [endpoint: {}] from device [tenantId: {}, deviceId: {}]", endpoint, tenantObject.getTenantId(), deviceId, t);
}
TracingHelper.logError(currentSpan, t);
currentSpan.finish();
return Future.failedFuture(t);
});
}
use of io.opentracing.Span in project hono by eclipse.
the class AbstractVertxBasedMqttProtocolAdapter method uploadCommandResponseMessage.
/**
* Uploads a command response message.
*
* @param ctx The context in which the MQTT message has been published.
* @param targetAddress The address that the response should be forwarded to.
* @return A future indicating the outcome of the operation.
* <p>
* The future will succeed if the message has been uploaded successfully.
* Otherwise, the future will fail with a {@link ServiceInvocationException}.
* @throws NullPointerException if any of the parameters are {@code null}.
*/
public final Future<Void> uploadCommandResponseMessage(final MqttContext ctx, final ResourceIdentifier targetAddress) {
Objects.requireNonNull(ctx);
Objects.requireNonNull(targetAddress);
final String[] addressPath = targetAddress.getResourcePath();
Integer status = null;
String reqId = null;
final Future<CommandResponse> commandResponseTracker;
if (addressPath.length <= CommandConstants.TOPIC_POSITION_RESPONSE_STATUS) {
commandResponseTracker = Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "command response topic has too few segments"));
} else {
try {
status = Integer.parseInt(addressPath[CommandConstants.TOPIC_POSITION_RESPONSE_STATUS]);
} catch (final NumberFormatException e) {
log.trace("got invalid status code [{}] [tenant-id: {}, device-id: {}]", addressPath[CommandConstants.TOPIC_POSITION_RESPONSE_STATUS], targetAddress.getTenantId(), targetAddress.getResourceId());
}
if (status != null) {
reqId = addressPath[CommandConstants.TOPIC_POSITION_RESPONSE_REQ_ID];
final CommandResponse commandResponse = CommandResponse.fromRequestId(reqId, targetAddress.getTenantId(), targetAddress.getResourceId(), ctx.message().payload(), ctx.contentType(), status);
commandResponseTracker = commandResponse != null ? Future.succeededFuture(commandResponse) : Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "command response topic contains invalid data"));
} else {
// status code could not be parsed
commandResponseTracker = Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "invalid status code"));
}
}
final Span currentSpan = TracingHelper.buildChildSpan(tracer, ctx.getTracingContext(), "upload Command response", getTypeName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_TENANT_ID, targetAddress.getTenantId()).withTag(TracingHelper.TAG_DEVICE_ID, targetAddress.getResourceId()).withTag(Constants.HEADER_COMMAND_RESPONSE_STATUS, status).withTag(Constants.HEADER_COMMAND_REQUEST_ID, reqId).withTag(TracingHelper.TAG_AUTHENTICATED.getKey(), ctx.authenticatedDevice() != null).start();
final int payloadSize = Optional.ofNullable(ctx.message().payload()).map(Buffer::length).orElse(0);
final Future<TenantObject> tenantTracker = getTenantConfiguration(targetAddress.getTenantId(), ctx.getTracingContext());
return CompositeFuture.all(tenantTracker, commandResponseTracker).compose(success -> {
final Future<RegistrationAssertion> deviceRegistrationTracker = getRegistrationAssertion(targetAddress.getTenantId(), targetAddress.getResourceId(), ctx.authenticatedDevice(), currentSpan.context());
final Future<Void> tenantValidationTracker = CompositeFuture.all(isAdapterEnabled(tenantTracker.result()), checkMessageLimit(tenantTracker.result(), payloadSize, currentSpan.context())).mapEmpty();
return CompositeFuture.all(deviceRegistrationTracker, tenantValidationTracker).compose(ok -> sendCommandResponse(tenantTracker.result(), deviceRegistrationTracker.result(), commandResponseTracker.result(), currentSpan.context()));
}).compose(delivery -> {
log.trace("successfully forwarded command response from device [tenant-id: {}, device-id: {}]", targetAddress.getTenantId(), targetAddress.getResourceId());
metrics.reportCommand(Direction.RESPONSE, targetAddress.getTenantId(), tenantTracker.result(), ProcessingOutcome.FORWARDED, payloadSize, ctx.getTimer());
// check that the remote MQTT client is still connected before sending PUBACK
if (ctx.isAtLeastOnce() && ctx.deviceEndpoint().isConnected()) {
currentSpan.log(EVENT_SENDING_PUBACK);
ctx.acknowledge();
}
currentSpan.finish();
return Future.<Void>succeededFuture();
}).recover(t -> {
TracingHelper.logError(currentSpan, t);
currentSpan.finish();
metrics.reportCommand(Direction.RESPONSE, targetAddress.getTenantId(), tenantTracker.result(), ProcessingOutcome.from(t), payloadSize, ctx.getTimer());
return Future.failedFuture(t);
});
}
use of io.opentracing.Span in project hono by eclipse.
the class AbstractVertxBasedMqttProtocolAdapterTest method verifyEventMessageUsesTtlValueGivenInPropertyBag.
/**
* Verifies that the TTL for a downstream event is set to the given <em>time-to-live</em> value in the
* <em>property-bag</em>.
*
* @param ctx The vert.x test context.
*/
@Test
public void verifyEventMessageUsesTtlValueGivenInPropertyBag(final VertxTestContext ctx) {
// Given an adapter
givenAnAdapter(properties);
givenAnEventSenderForAnyTenant();
// WHEN a "device" of "tenant" publishes an event message with a TTL value of 30 seconds.
final MqttPublishMessage msg = mock(MqttPublishMessage.class);
when(msg.topicName()).thenReturn("e/tenant/device/?hono-ttl=30¶m2=value2");
when(msg.qosLevel()).thenReturn(MqttQoS.AT_LEAST_ONCE);
adapter.uploadEventMessage(newMqttContext(msg, mockEndpoint(), span), "tenant", "device", Buffer.buffer("test")).onComplete(ctx.succeeding(t -> {
ctx.verify(() -> {
// THEN the TTL value of the amqp message is 30 seconds.
assertEventHasBeenSentDownstream("tenant", "device", null, 30L);
});
ctx.completeNow();
}));
}
Aggregations