use of io.opentracing.SpanContext in project hono by eclipse.
the class CommandRouterServiceImpl method activateCommandRouting.
private void activateCommandRouting(final Pair<String, Integer> attempt, final SpanContext tracingContext) {
if (!running.get()) {
// component has been stopped, no need to create command consumer in this case
tenantsInProcess.remove(attempt.one());
return;
}
final Span span = tracer.buildSpan("re-enable command routing for tenant").addReference(References.FOLLOWS_FROM, tracingContext).withTag(TracingHelper.TAG_TENANT_ID, attempt.one()).start();
final var logEntries = new HashMap<String, Object>(2);
logEntries.put("attempt#", attempt.two());
tenantClient.get(attempt.one(), span.context()).map(tenantObject -> commandConsumerFactoryProvider.getClient(tenantObject)).map(factory -> factory.createCommandConsumer(attempt.one(), span.context())).onSuccess(ok -> {
logEntries.put(Fields.MESSAGE, "successfully created command consumer");
span.log(logEntries);
reenabledTenants.add(attempt.one());
}).onFailure(t -> {
logEntries.put(Fields.MESSAGE, "failed to create command consumer");
logEntries.put(Fields.ERROR_OBJECT, t);
TracingHelper.logError(span, logEntries);
if (t instanceof ServerErrorException) {
// add to end of queue in order to retry at a later time
LOG.info("failed to create command consumer [attempt#: {}]", attempt.two(), t);
span.log("marking tenant for later re-try to create command consumer");
tenantsToEnable.addLast(Pair.of(attempt.one(), attempt.two() + 1));
}
}).onComplete(r -> {
span.finish();
tenantsInProcess.remove(attempt.one());
processTenantQueue(tracingContext);
});
}
use of io.opentracing.SpanContext 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 io.opentracing.SpanContext in project hono by eclipse.
the class CommandTargetMapperImpl method getTargetGatewayAndAdapterInstance.
@Override
public final Future<JsonObject> getTargetGatewayAndAdapterInstance(final String tenantId, final String deviceId, final SpanContext context) {
Objects.requireNonNull(tenantId);
Objects.requireNonNull(deviceId);
final Span span = TracingHelper.buildChildSpan(tracer, context, "get target gateway and adapter instance", CommandTargetMapper.class.getSimpleName()).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CONSUMER).withTag(TracingHelper.TAG_TENANT_ID, tenantId).withTag(TracingHelper.TAG_DEVICE_ID, deviceId).start();
return registrationClient.assertRegistration(tenantId, deviceId, null, span.context()).map(RegistrationAssertion::getAuthorizedGateways).recover(t -> {
LOG.debug("Error retrieving gateways authorized to act on behalf of device [tenant-id: {}, device-id: {}]", tenantId, deviceId, t);
return Future.failedFuture(ServiceInvocationException.extractStatusCode(t) == HttpURLConnection.HTTP_NOT_FOUND ? new DeviceDisabledOrNotRegisteredException(tenantId, HttpURLConnection.HTTP_NOT_FOUND) : t);
}).compose(viaGateways -> {
return deviceConnectionInfo.getCommandHandlingAdapterInstances(tenantId, deviceId, new HashSet<>(viaGateways), span).compose(resultJson -> determineTargetInstanceJson(resultJson, deviceId, viaGateways, span));
}).onFailure(t -> {
LOG.debug("Error getting target gateway and adapter instance", t);
TracingHelper.logError(span, t);
Tags.HTTP_STATUS.set(span, ServiceInvocationException.extractStatusCode(t));
}).onComplete(ar -> span.finish());
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class EdgeDeviceAutoProvisioner method performAutoProvisioning.
/**
* Auto-provisions the edge device using the given device id and the given registration data.
*
* @param tenantId The id of the tenant for which the edge device should be provisioned.
* @param tenant The tenant information.
* @param deviceId The id of the edge device which should be provisioned, may be {@code null}.
* @param gatewayId The id of the edge device's gateway.
* @param device The registration data for the device to be auto-provisioned.
* @param spanContext The tracing context to be used by this operation.
*
* @return A future indicating the outcome of the operation.
*
* @throws NullPointerException if any argument except deviceId is {@code null}.
*/
public Future<Device> performAutoProvisioning(final String tenantId, final Tenant tenant, final String deviceId, final String gatewayId, final Device device, final SpanContext spanContext) {
Objects.requireNonNull(tenantId);
Objects.requireNonNull(tenant);
Objects.requireNonNull(gatewayId);
Objects.requireNonNull(device);
Objects.requireNonNull(spanContext);
final Span span = TracingHelper.buildChildSpan(tracer, spanContext, "auto-provision edge device connected via gateway", Constants.PROTOCOL_ADAPTER_TYPE_DEVICE_REGISTRY).withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT).withTag(TracingHelper.TAG_GATEWAY_ID, gatewayId).start();
TracingHelper.setDeviceTags(span, tenantId, deviceId);
return deviceManagementService.createDevice(tenantId, Optional.of(deviceId), device, span).recover(thr -> ServiceInvocationException.extractStatusCode(thr) == HttpURLConnection.HTTP_CONFLICT ? Future.succeededFuture(OperationResult.empty(HttpURLConnection.HTTP_CONFLICT)) : Future.failedFuture(thr)).compose(addEdgeDeviceResult -> {
if (addEdgeDeviceResult.isError()) {
if (addEdgeDeviceResult.getStatus() != HttpURLConnection.HTTP_CONFLICT) {
return Future.failedFuture(StatusCodeMapper.from(addEdgeDeviceResult.getStatus(), String.format("failed to add edge device (status %d)", addEdgeDeviceResult.getStatus())));
}
// handle HTTP_CONFLICT, meaning the device already exists
span.log("device already exists");
LOG.debug("device [{}] for gateway [{}] already created by concurrent auto-provisioning [tenant-id: {}]", deviceId, gatewayId, tenantId);
return deviceManagementService.readDevice(tenantId, deviceId, span).compose(readDeviceResult -> {
if (!readDeviceResult.isOk()) {
span.log("reading device after conflict failed");
LOG.warn("reading device after conflict failed for device [{}] of gateway [{}] of tenant [{}]: status: {}", deviceId, gatewayId, tenantId, readDeviceResult.getStatus());
return Future.failedFuture(StatusCodeMapper.from(readDeviceResult.getStatus(), String.format("reading device after conflict failed (status %d)", readDeviceResult.getStatus())));
}
if (!readDeviceResult.getPayload().getVia().contains(gatewayId)) {
span.log("attempted to auto-provision same device via two different gateways at the same time");
LOG.info("attempted to auto-provision device [{}] via gateway [{}] of tenant [{}] but the registration data's via contains only {}", deviceId, gatewayId, tenantId, readDeviceResult.getPayload().getVia());
return Future.failedFuture(StatusCodeMapper.from(HttpURLConnection.HTTP_FORBIDDEN, "device already auto-provisioned for another gateway"));
}
final Device readDevice = readDeviceResult.getPayload();
// ensure that a notification event gets sent (even if we might send duplicate events)
return sendDelayedAutoProvisioningNotificationIfNeeded(tenantId, tenant, deviceId, gatewayId, readDevice, span).map(readDevice);
});
}
span.log("device created");
LOG.trace("device [{}] for gateway [{}] successfully created by auto-provisioning [tenant-id: {}]", deviceId, gatewayId, tenantId);
return sendAutoProvisioningEvent(tenantId, tenant, deviceId, gatewayId, span).compose(sendEmptyEventOk -> deviceManagementService.readDevice(tenantId, deviceId, span).compose(readDeviceResult -> {
if (!readDeviceResult.isOk()) {
LOG.warn("notification flag of device [{}] for gateway [{}] of tenant [tenant-id: {}] could not be updated", deviceId, gatewayId, tenantId);
return Future.failedFuture(StatusCodeMapper.from(readDeviceResult.getStatus(), String.format("update of notification flag failed (status %d)", readDeviceResult.getStatus())));
}
final Device deviceData = readDeviceResult.getPayload();
return updateAutoProvisioningNotificationSent(tenantId, deviceId, deviceData, readDeviceResult.getResourceVersion(), span).recover(error -> Future.succeededFuture()).map(deviceData);
}));
}).onFailure(thr -> TracingHelper.logError(span, thr)).onComplete(ar -> span.finish());
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class AbstractTenantStore method readTenant.
/**
* Read the content of a tenant.
* <p>
* This will execute the {@code read} statement and return the unprocessed information.
* <p>
* If there are no entries, an empty result will be returned.
* <p>
* If more than one entry is being found, the result will be failed with an
* {@link IllegalStateException} exception.
*
* @param id The key to the tenant entry.
* @param spanContext The span to contribute to.
* @return The future, tracking the outcome of the operation.
*/
public Future<Optional<TenantReadResult>> readTenant(final String id, final SpanContext spanContext) {
final Span span = TracingHelper.buildChildSpan(this.tracer, spanContext, "read tenant", getClass().getSimpleName()).withTag(TracingHelper.TAG_TENANT_ID, id).start();
final var expanded = this.readStatement.expand(map -> {
map.put("tenant_id", id);
});
return SQL.runTransactionally(this.client, this.tracer, span.context(), (connection, context) -> readTenantBy(connection, expanded, context)).onComplete(x -> span.finish());
}
Aggregations