use of org.eclipse.hono.util.RegistrationAssertion 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 org.eclipse.hono.util.RegistrationAssertion 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 org.eclipse.hono.util.RegistrationAssertion in project hono by eclipse.
the class LoraProtocolAdapterTest method setGatewayDeviceCommandEndpoint.
private void setGatewayDeviceCommandEndpoint(final CommandEndpoint commandEndpoint) {
when(registrationClient.assertRegistration(eq(TEST_TENANT_ID), eq(TEST_GATEWAY_ID), eq(null), (SpanContext) any())).thenAnswer(invocation -> {
final String deviceId = invocation.getArgument(1);
final RegistrationAssertion regAssertion = new RegistrationAssertion(deviceId);
regAssertion.setCommandEndpoint(commandEndpoint);
return Future.succeededFuture(regAssertion);
});
}
use of org.eclipse.hono.util.RegistrationAssertion in project hono by eclipse.
the class ProtonBasedDownstreamSenderTest method testDownstreamTelemetryMessageHasCreationTimeAndTtl.
/**
* Verifies that a downstream telemetry message always contains a creation-time
* and a time-to-live as defined at the tenant level.
*
* @param qosLevel The quality of service used for sending the message.
* @param expectedTtl The time to live (in millis) expected to be set on the message.
*/
@ParameterizedTest
@CsvSource(value = { "AT_MOST_ONCE,10000", "AT_LEAST_ONCE,20000" })
public void testDownstreamTelemetryMessageHasCreationTimeAndTtl(final QoS qosLevel, final long expectedTtl) {
final TenantObject tenant = TenantObject.from(Constants.DEFAULT_TENANT, true);
tenant.setResourceLimits(new ResourceLimits().setMaxTtlTelemetryQoS0(10L).setMaxTtlTelemetryQoS1(20L));
final RegistrationAssertion device = new RegistrationAssertion("4711");
// WHEN sending a message without specifying any properties
sender.sendTelemetry(tenant, device, qosLevel, "text/plain", Buffer.buffer("hello"), null, span.context());
// THEN the message contains a creation-time
verify(protonSender).send(argThat(message -> message.getCreationTime() > 0 && message.getTtl() == expectedTtl), VertxMockSupport.anyHandler());
}
use of org.eclipse.hono.util.RegistrationAssertion in project hono by eclipse.
the class ProtonBasedDownstreamSenderTest method testThatTtlIsSetInMilliseconds.
/**
* Verifies that a TTL values in the <em>properties</em> parameter of
* {@link org.eclipse.hono.client.telemetry.EventSender#sendEvent(TenantObject, RegistrationAssertion, String, Buffer, Map, io.opentracing.SpanContext)}
* are correctly taken as seconds and set as milliseconds at the message.
*/
@Test
public void testThatTtlIsSetInMilliseconds() {
final TenantObject tenant = TenantObject.from(Constants.DEFAULT_TENANT, true);
final RegistrationAssertion device = new RegistrationAssertion("4711");
// WHEN sending an event with properties that contain a TTL in seconds
final Map<String, Object> props = new HashMap<>();
props.put(MessageHelper.SYS_HEADER_PROPERTY_TTL, 2);
sender.sendEvent(tenant, device, "text/plain", Buffer.buffer("hello"), props, span.context());
// THEN the TTL at the message is correctly set in milliseconds
verify(protonSender).send(argThat(message -> message.getTtl() == 2000), VertxMockSupport.anyHandler());
}
Aggregations