/** * Settles the command message with the <em>accepted</em> outcome. * <p> * This method simply invokes {@link CommandContext#accept(int)} with * 0 credits. */ public void accept() { accept(0); }
/** * Settles the command message with the <em>accepted</em> outcome * and flows credit to the peer. * <p> * This method also finishes the OpenTracing span returned by * {@link #getCurrentSpan()}. * * @param credit The number of credits to flow to the peer. * @throws IllegalArgumentException if credit is negative. */ public void accept(final int credit) { if (credit < 0) { throw new IllegalArgumentException("credit must be >= 0"); } LOG.trace("accepting command message [{}]", getCommand()); ProtonHelper.accepted(delivery, true); currentSpan.log("accepted command for device"); if (credit > 0) { flow(credit); } currentSpan.finish(); }
/** * Settles the command message with the <em>rejected</em> outcome. * <p> * This method simply invokes {@link CommandContext#reject(ErrorCondition, int)} * with 0 credits. * * @param errorCondition The error condition to send in the disposition frame (may be {@code null}). */ public void reject(final ErrorCondition errorCondition) { reject(errorCondition, 0); }
Objects.requireNonNull(commandContext); final Command command = commandContext.getCommand(); final Message msg = command.getCommandMessage(); if (msg.getCorrelationId() == null) { LOG.trace("device accepted command message [command: {}]", command.getName()); logItems.put(Fields.EVENT, "device accepted command"); commandContext.getCurrentSpan().log(logItems); commandContext.accept(1); } else if (Rejected.class.isInstance(remoteState)) { final ErrorCondition error = ((Rejected) remoteState).getError(); LOG.debug("device rejected command message [command: {}, error: {}]", command.getName(), error); logItems.put(Fields.EVENT, "device rejected command"); commandContext.getCurrentSpan().log(logItems); commandContext.reject(error, 1); } else if (Released.class.isInstance(remoteState)) { LOG.debug("device released command message [command: {}]", command.getName()); logItems.put(Fields.EVENT, "device released command"); commandContext.getCurrentSpan().log(logItems); commandContext.release(1); logItems.put(Fields.EVENT, "device did not settle command"); logItems.put("remote state", remoteState.getClass().getSimpleName()); commandContext.getCurrentSpan().log(logItems); commandContext.release(1); commandContext.getCurrentSpan().log(items);
private Future<MessageConsumer> createCommandConsumer(final MqttEndpoint mqttEndpoint, final CommandSubscription sub, final CommandHandler<T> cmdHandler) { // if a device does not specify a keep alive in its CONNECT packet then // the default value of the CommandConnection will be used final long livenessCheckInterval = mqttEndpoint.keepAliveTimeSeconds() * 1000 / 2; return getCommandConnection().createCommandConsumer( sub.getTenant(), sub.getDeviceId(), commandContext -> { Tags.COMPONENT.set(commandContext.getCurrentSpan(), getTypeName()); final Command command = commandContext.getCommand(); if (command.isValid()) { onCommandReceived(mqttEndpoint, sub, commandContext, cmdHandler); } else { // issue credit so that application(s) can send the next command commandContext.reject(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "malformed command message"), 1); } }, remoteClose -> {}, livenessCheckInterval); }
metrics.incrementProcessedPayload(endpointName, tenant, messagePayloadSize(ctx)); if (commandContext != null) { commandContext.getCurrentSpan().log("forwarded command to device in HTTP response body"); commandContext.accept(); metrics.incrementCommandDeliveredToDevice(tenant); endpointName, tenant, deviceId, t); if (commandContext != null) { commandContext.getCurrentSpan().log("failed to forward command to device in HTTP response body"); TracingHelper.logError(commandContext.getCurrentSpan(), t); commandContext.release(); final CommandContext commandContext = ctx.get(CommandContext.KEY_COMMAND_CONTEXT); if (commandContext != null) { commandContext.release();
TracingHelper.TAG_CLIENT_ID.set(commandContext.getCurrentSpan(), endpoint.clientIdentifier()); final Command command = commandContext.getCommand(); items.put(Tags.MESSAGE_BUS_DESTINATION.getKey(), topic); items.put(TracingHelper.TAG_QOS.getKey(), qos.toString()); commandContext.getCurrentSpan().log(items); commandContext.accept(1);
TracingHelper.TAG_CLIENT_ID.set(commandContext.getCurrentSpan(), endpoint.clientIdentifier()); final Command command = commandContext.getCommand(); items.put(TracingHelper.TAG_CLIENT_ID.getKey(), subscription.getClientId()); items.put(TracingHelper.TAG_QOS.getKey(), subscription.getQos().toString()); commandContext.getCurrentSpan().log(items); subscription.getQos(), sentHandler.cause()); TracingHelper.logError(commandContext.getCurrentSpan(), sentHandler.cause()); commandContext.release(1);
private void afterCommandPublished(final CommandSubscription subscription, final CommandContext commandContext) { metrics.incrementCommandDeliveredToDevice(subscription.getTenant()); LOG.debug("Published command to device [tenant-id: {}, device-id: {}, MQTT client-id: {}, QoS: {}]", subscription.getTenant(), subscription.getDeviceId(), subscription.getClientId(), subscription.getQos()); final Map<String, String> items = new HashMap<>(4); items.put(Fields.EVENT, "Published command to device"); items.put(Tags.MESSAGE_BUS_DESTINATION.getKey(), subscription.getTopic()); items.put(TracingHelper.TAG_CLIENT_ID.getKey(), subscription.getClientId()); items.put(TracingHelper.TAG_QOS.getKey(), subscription.getQos().toString()); commandContext.getCurrentSpan().log(items); commandContext.accept(1); }
private long startTimer(final Integer msgId) { LOG.trace("Start a timer for [{}] ms", config.getCommandAckTimeout()); return vertx.setTimer(config.getCommandAckTimeout(), timerId -> { LOG.trace("Timer [{}] expired", timerId); Optional.ofNullable(removeFromWaitingForAcknowledgement(msgId)).ifPresent(value -> { final CommandSubscription subscription = value.two(); final CommandContext commandContext = value.three(); LOG.debug( "Timed out waiting for acknowledgment for command sent to device [tenant-id: {}, device-id: {}, MQTT client-id: {}, QoS: {}]", subscription.getTenant(), subscription.getDeviceId(), subscription.getClientId(), subscription.getQos()); final Map<String, String> items = new HashMap<>(3); items.put(Fields.EVENT, "Timed out waiting for acknowledgment for command sent to device"); items.put(TracingHelper.TAG_CLIENT_ID.getKey(), subscription.getClientId()); items.put(TracingHelper.TAG_QOS.getKey(), subscription.getQos().toString()); commandContext.getCurrentSpan().log(items); commandContext.release(1); }); }); }
/** * Creates a command consumer for a specific device. * * @param tenantId The tenant of the command receiver. * @param deviceId The device of the command receiver. * @param commandConsumer The handler to invoke for each command destined to the device. * @param closeHandler Called when the peer detaches the link. * @return Result of the receiver creation. */ protected final Future<MessageConsumer> createCommandConsumer( final String tenantId, final String deviceId, final Handler<CommandContext> commandConsumer, final Handler<Void> closeHandler) { return commandConnection.createCommandConsumer( tenantId, deviceId, commandContext -> { Tags.COMPONENT.set(commandContext.getCurrentSpan(), getTypeName()); commandConsumer.handle(commandContext); }, closeHandler); }
private void setResponsePayload(final HttpServerResponse response, final CommandContext commandContext, final Span currentSpan) { if (commandContext == null) { response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED); } else { final Command command = commandContext.getCommand(); response.putHeader(Constants.HEADER_COMMAND, command.getName()); currentSpan.setTag(Constants.HEADER_COMMAND, command.getName()); LOG.trace("adding command [name: {}, request-id: {}] to response for device [tenant-id: {}, device-id: {}]", command.getName(), command.getRequestId(), command.getTenant(), command.getDeviceId()); if (!command.isOneWay()) { response.putHeader(Constants.HEADER_COMMAND_REQUEST_ID, command.getRequestId()); currentSpan.setTag(Constants.HEADER_COMMAND_REQUEST_ID, command.getRequestId()); } response.setStatusCode(HttpURLConnection.HTTP_OK); HttpUtils.setResponseBody(response, command.getPayload(), command.getContentType()); } }
/** * Settles the command message with the <em>released</em> outcome. * <p> * This method simply invokes {@link CommandContext#release(int)} with * 0 credits. */ public void release() { release(0); }
/** * Settles the command message with the <em>rejected</em> outcome * and flows credit to the peer. * <p> * This method also finishes the OpenTracing span returned by * {@link #getCurrentSpan()}. * * @param errorCondition The error condition to send in the disposition frame (may be {@code null}). * @param credit The number of credits to flow to the peer. * @throws IllegalArgumentException if credit is negative. */ public void reject(final ErrorCondition errorCondition, final int credit) { final Rejected rejected = new Rejected(); final Map<String, Object> items = new HashMap<>(2); items.put(Fields.EVENT, "rejected command for device"); if (errorCondition != null) { rejected.setError(errorCondition); Optional.ofNullable(errorCondition.getDescription()).ifPresent(s -> items.put(Fields.MESSAGE, s)); } TracingHelper.logError(currentSpan, items); delivery.disposition(rejected, true); if (credit > 0) { flow(credit); } currentSpan.finish(); }
/** * Creates a context for a command. * * @param command The command to be processed. * @param delivery The delivery corresponding to the message. * @param receiver The AMQP link over which the command has been received. * @param currentSpan The OpenTracing span to use for tracking the processing of the command. * @return The context. * @throws NullPointerException if any of the parameters are {@code null}. */ public static CommandContext from( final Command command, final ProtonDelivery delivery, final ProtonReceiver receiver, final Span currentSpan) { Objects.requireNonNull(command); Objects.requireNonNull(delivery); Objects.requireNonNull(receiver); Objects.requireNonNull(currentSpan); return new CommandContext(command, delivery, receiver, currentSpan); }
private Future<MessageConsumer> createCommandConsumer( final ProtonSender sender, final ResourceIdentifier sourceAddress) { return getCommandConnection().createCommandConsumer( sourceAddress.getTenantId(), sourceAddress.getResourceId(), commandContext -> { Tags.COMPONENT.set(commandContext.getCurrentSpan(), getTypeName()); final Command command = commandContext.getCommand(); if (!command.isValid()) { final Exception ex = new ClientErrorException(HttpURLConnection.HTTP_BAD_REQUEST, "malformed command message"); commandContext.reject(AmqpContext.getErrorCondition(ex), 1); } else if (!sender.isOpen()) { commandContext.release(1); } else { onCommandReceived(sender, commandContext); } }, closeHandler -> {}, DEFAULT_COMMAND_CONSUMER_CHECK_INTERVAL_MILLIS); }
private Future<MessageConsumer> createCommandConsumer(final MqttEndpoint mqttEndpoint, final CommandSubscription sub) { // if a device does not specify a keep alive in its CONNECT packet then // the default value of the CommandConnection will be used final long livenessCheckInterval = mqttEndpoint.keepAliveTimeSeconds() * 1000 / 2; return getCommandConnection().createCommandConsumer( sub.getTenant(), sub.getDeviceId(), commandContext -> { Tags.COMPONENT.set(commandContext.getCurrentSpan(), getTypeName()); final Command command = commandContext.getCommand(); if (command.isValid()) { onCommandReceived(mqttEndpoint, sub, commandContext); } else { // issue credit so that application(s) can send the next command commandContext.reject(new ErrorCondition(Constants.AMQP_BAD_REQUEST, "malformed command message"), 1); } }, remoteClose -> {}, livenessCheckInterval); }
metrics.incrementProcessedPayload(endpointName, tenant, messagePayloadSize(ctx)); if (commandContext != null) { commandContext.getCurrentSpan().log("forwarded command to device in HTTP response body"); commandContext.accept(); metrics.incrementCommandDeliveredToDevice(tenant); endpointName, tenant, deviceId, t); if (commandContext != null) { commandContext.getCurrentSpan().log("failed to forward command to device in HTTP response body"); TracingHelper.logError(commandContext.getCurrentSpan(), t); commandContext.release(); final CommandContext commandContext = ctx.get(CommandContext.KEY_COMMAND_CONTEXT); if (commandContext != null) { commandContext.release();
/** * Creates a command consumer for a specific device. * * @param tenantId The tenant of the command receiver. * @param deviceId The device of the command receiver. * @param commandConsumer The handler to invoke for each command destined to the device. * @param closeHandler Called when the peer detaches the link. * @return Result of the receiver creation. */ protected final Future<MessageConsumer> createCommandConsumer( final String tenantId, final String deviceId, final Handler<CommandContext> commandConsumer, final Handler<Void> closeHandler) { return commandConnection.createCommandConsumer( tenantId, deviceId, commandContext -> { Tags.COMPONENT.set(commandContext.getCurrentSpan(), getTypeName()); commandConsumer.handle(commandContext); }, closeHandler); }
private void setResponsePayload(final HttpServerResponse response, final CommandContext commandContext, final Span currentSpan) { if (commandContext == null) { response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED); } else { final Command command = commandContext.getCommand(); response.putHeader(Constants.HEADER_COMMAND, command.getName()); currentSpan.setTag(Constants.HEADER_COMMAND, command.getName()); LOG.trace("adding command [name: {}, request-id: {}] to response for device [tenant-id: {}, device-id: {}]", command.getName(), command.getRequestId(), command.getTenant(), command.getDeviceId()); if (!command.isOneWay()) { response.putHeader(Constants.HEADER_COMMAND_REQUEST_ID, command.getRequestId()); currentSpan.setTag(Constants.HEADER_COMMAND_REQUEST_ID, command.getRequestId()); } response.setStatusCode(HttpURLConnection.HTTP_OK); HttpUtils.setResponseBody(response, command.getPayload(), command.getContentType()); } }