use of io.opentracing.SpanContext in project hono by eclipse.
the class ProtonBasedRequestResponseCommandClient method sendCommand.
/**
* Sends a command to a device and expects a response.
* <p>
* A device needs to be (successfully) registered before a client can upload
* any data for it. The device also needs to be connected to a protocol adapter
* and needs to have indicated its intent to receive commands.
*
* @param tenantId The tenant that the device belongs to.
* @param deviceId The device to send the command to.
* @param command The name of the command.
* @param contentType The type of the data submitted as part of the command or {@code null} if unknown.
* @param data The input data to the command or {@code null} if the command has no input data.
* @param replyId An arbitrary string which gets used for the response link address in the form of
* <em>command_response/${tenantId}/${replyId}</em>. If it is {@code null} then an unique
* identifier generated using {@link UUID#randomUUID()} is used.
* @param properties The headers to include in the command message as AMQP application properties.
* @param timeout The duration after which the send command request times out. If the timeout is {@code null}
* then the default timeout value of {@value DEFAULT_COMMAND_TIMEOUT_IN_MS} ms is used.
* If the timeout duration is set to 0 then the send command request never times out.
* @param context The currently active OpenTracing span context that is used to trace the execution of this
* operation or {@code null} if no span is currently active.
* @return A future indicating the result of the operation.
* <p>
* The future will succeed if a response with status 2xx has been received from the device.
* If the response has no payload, the future will complete with a DownstreamMessage that has a {@code null} payload.
* <p>
* Otherwise, the future will fail with a {@link ServiceInvocationException} containing
* the (error) status code. Status codes are defined at
* <a href="https://www.eclipse.org/hono/docs/api/command-and-control">Command and Control API</a>.
* @throws NullPointerException if any of tenantId, deviceId or command are {@code null}.
* @throws IllegalArgumentException if the timeout duration value is < 0
*/
public Future<DownstreamMessage<AmqpMessageContext>> sendCommand(final String tenantId, final String deviceId, final String command, final String contentType, final Buffer data, final String replyId, final Map<String, Object> properties, final Duration timeout, final SpanContext context) {
Objects.requireNonNull(tenantId);
Objects.requireNonNull(deviceId);
Objects.requireNonNull(command);
final long timeoutInMs = Optional.ofNullable(timeout).map(t -> {
if (t.isNegative()) {
throw new IllegalArgumentException("command timeout duration must be >= 0");
}
return t.toMillis();
}).orElse(DEFAULT_COMMAND_TIMEOUT_IN_MS);
final Span currentSpan = newChildSpan(context, "send command and receive response");
return getOrCreateClient(tenantId, replyId).map(client -> {
client.setRequestTimeout(timeoutInMs);
return client;
}).compose(client -> {
final String messageTargetAddress = AddressHelper.getTargetAddress(CommandConstants.NORTHBOUND_COMMAND_REQUEST_ENDPOINT, tenantId, deviceId, connection.getConfig());
return client.createAndSendRequest(command, messageTargetAddress, properties, data, contentType, this::mapCommandResponse, currentSpan);
}).recover(error -> {
Tags.HTTP_STATUS.set(currentSpan, ServiceInvocationException.extractStatusCode(error));
TracingHelper.logError(currentSpan, error);
return Future.failedFuture(error);
}).compose(result -> {
if (result == null) {
return Future.failedFuture(new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST));
} else {
final DownstreamMessage<AmqpMessageContext> commandResponseMessage = result.getPayload();
setTagsForResult(currentSpan, result);
if (result.isError()) {
final String detailMessage = commandResponseMessage.getPayload() != null && commandResponseMessage.getPayload().length() > 0 ? commandResponseMessage.getPayload().toString(StandardCharsets.UTF_8) : null;
return Future.failedFuture(StatusCodeMapper.from(result.getStatus(), detailMessage));
}
return Future.succeededFuture(commandResponseMessage);
}
}).onComplete(r -> currentSpan.finish());
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class ProtonBasedCommandResponseSender method sendCommandResponse.
@Override
public Future<Void> sendCommandResponse(final TenantObject tenant, final RegistrationAssertion device, final CommandResponse response, final SpanContext context) {
Objects.requireNonNull(tenant);
Objects.requireNonNull(device);
Objects.requireNonNull(response);
final var sender = createSender(response.getTenantId(), response.getReplyToId());
return sender.recover(thr -> Future.failedFuture(StatusCodeMapper.toServerError(thr))).compose(s -> {
final Message msg = createDownstreamMessage(response, tenant, device, response.getAdditionalProperties());
final Span span = newChildSpan(context, "forward Command response");
if (response.getMessagingType() != getMessagingType()) {
span.log(String.format("using messaging type %s instead of type %s used for the original command", getMessagingType(), response.getMessagingType()));
}
return s.sendAndWaitForOutcome(msg, span);
}).onSuccess(delivery -> sender.result().close()).mapEmpty();
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class MongoDbBasedTenantDao method create.
/**
* {@inheritDoc}
*/
@Override
public Future<String> create(final TenantDto tenantConfig, final SpanContext tracingContext) {
Objects.requireNonNull(tenantConfig);
final Span span = tracer.buildSpan("create Tenant").addReference(References.CHILD_OF, tracingContext).withTag(TracingHelper.TAG_TENANT_ID, tenantConfig.getTenantId()).start();
final JsonObject newTenantDtoJson = JsonObject.mapFrom(tenantConfig);
if (LOG.isTraceEnabled()) {
LOG.trace("creating tenant:{}{}", System.lineSeparator(), newTenantDtoJson.encodePrettily());
}
return validateTrustAnchors(tenantConfig, span).compose(ok -> mongoClient.insert(collectionName, newTenantDtoJson)).map(tenantObjectIdResult -> {
LOG.debug("successfully created tenant [tenant-id: {}, version: {}]", tenantConfig.getTenantId(), tenantConfig.getVersion());
span.log("successfully created tenant");
return tenantConfig.getVersion();
}).recover(error -> {
if (MongoDbBasedDao.isDuplicateKeyError(error)) {
TracingHelper.logError(span, "tenant already exists");
return Future.failedFuture(new ClientErrorException(tenantConfig.getTenantId(), HttpURLConnection.HTTP_CONFLICT, "tenant already exists"));
} else {
TracingHelper.logError(span, "error creating tenant", error);
return mapError(error);
}
}).onComplete(r -> span.finish());
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class MongoDbBasedTenantDao method getBySubjectDn.
/**
* {@inheritDoc}
*/
@Override
public Future<TenantDto> getBySubjectDn(final X500Principal subjectDn, final SpanContext tracingContext) {
Objects.requireNonNull(subjectDn);
final String dn = subjectDn.getName(X500Principal.RFC2253);
final Span span = tracer.buildSpan("get Tenant by subject DN").addReference(References.CHILD_OF, tracingContext).withTag(TracingHelper.TAG_SUBJECT_DN, dn).start();
return mongoClient.findWithOptions(collectionName, MongoDbDocumentBuilder.builder().withCa(dn).document(), new FindOptions().setLimit(2)).map(matchingDocuments -> {
if (matchingDocuments.size() == 0) {
LOG.debug("could not find tenant with matching trust anchor [subject DN: {}]", dn);
throw new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "no such tenant");
} else if (matchingDocuments.size() == 1) {
final JsonObject tenantJsonResult = matchingDocuments.get(0);
return TenantDto.forRead(tenantJsonResult.getString(Constants.JSON_FIELD_TENANT_ID), tenantJsonResult.getJsonObject(TenantDto.FIELD_TENANT).mapTo(Tenant.class), tenantJsonResult.getInstant(TenantDto.FIELD_CREATED), tenantJsonResult.getInstant(TenantDto.FIELD_UPDATED_ON), tenantJsonResult.getString(TenantDto.FIELD_VERSION));
} else {
LOG.debug("found multiple tenants with matching trust anchor [subject DN: {}]", dn);
throw new ClientErrorException(HttpURLConnection.HTTP_NOT_FOUND, "found multiple tenants with matching trust anchor");
}
}).onFailure(t -> TracingHelper.logError(span, "error retrieving tenant", t)).recover(this::mapError).onComplete(r -> span.finish());
}
use of io.opentracing.SpanContext in project hono by eclipse.
the class MongoDbBasedTenantDao method update.
/**
* {@inheritDoc}
*/
@Override
public Future<String> update(final TenantDto newTenantConfig, final Optional<String> resourceVersion, final SpanContext tracingContext) {
Objects.requireNonNull(newTenantConfig);
Objects.requireNonNull(resourceVersion);
final Span span = tracer.buildSpan("update Tenant").addReference(References.CHILD_OF, tracingContext).withTag(TracingHelper.TAG_TENANT_ID, newTenantConfig.getTenantId()).start();
resourceVersion.ifPresent(v -> TracingHelper.TAG_RESOURCE_VERSION.set(span, v));
final JsonObject updateTenantQuery = MongoDbDocumentBuilder.builder().withVersion(resourceVersion).withTenantId(newTenantConfig.getTenantId()).document();
return validateTrustAnchors(newTenantConfig, span).compose(ok -> mongoClient.findOneAndReplaceWithOptions(collectionName, updateTenantQuery, JsonObject.mapFrom(newTenantConfig), new FindOptions(), new UpdateOptions().setReturningNewDocument(true))).compose(updateResult -> {
if (updateResult == null) {
return MongoDbBasedDao.checkForVersionMismatchAndFail(newTenantConfig.getTenantId(), resourceVersion, getById(newTenantConfig.getTenantId(), false, span));
} else {
LOG.debug("successfully updated tenant [tenant-id: {}]", newTenantConfig.getTenantId());
span.log("successfully updated tenant");
return Future.succeededFuture(updateResult.getString(TenantDto.FIELD_VERSION));
}
}).recover(error -> {
if (MongoDbBasedDao.isDuplicateKeyError(error)) {
LOG.debug("failed to update tenant [{}], tenant alias already in use", newTenantConfig.getTenantId(), error);
final var exception = new ClientErrorException(newTenantConfig.getTenantId(), HttpURLConnection.HTTP_CONFLICT, "tenant alias already in use");
TracingHelper.logError(span, exception);
return Future.failedFuture(exception);
} else {
TracingHelper.logError(span, "error updating tenant", error);
return mapError(error);
}
}).onComplete(r -> span.finish());
}
Aggregations