From 0dc03b36494a1971d8d339a9944248bbaf498d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20J=C3=A4ckle?= Date: Wed, 29 Jan 2025 11:59:05 +0100 Subject: [PATCH 1/2] fix tracing propagation in general * remove many manual "traceparent" header removals which mitigated the root problem --- .../service/messaging/BasePublisherActor.java | 7 +------ .../messaging/amqp/AmqpConsumerActor.java | 7 +------ .../messaging/kafka/KafkaMessageTransformer.java | 7 +------ .../rabbitmq/RabbitMQConsumerActor.java | 7 +------ .../dispatching/ThingsAggregatorProxyActor.java | 7 +------ .../directives/RequestTracingDirective.java | 6 +----- .../ByRoundTripSignalEnrichmentFacade.java | 4 +--- .../DittoCachingSignalEnrichmentFacade.java | 6 +----- ...actJsonifiableWithDittoHeadersSerializer.java | 13 ++----------- .../AbstractPersistenceActor.java | 7 +------ .../AbstractPersistenceSupervisor.java | 6 +----- .../span/KamonHttpContextPropagation.java | 2 +- .../enforcement/AbstractEnforcerActor.java | 7 +------ .../service/starter/actors/SearchActor.java | 16 +++------------- .../service/updater/actors/ThingUpdater.java | 5 +---- 15 files changed, 18 insertions(+), 89 deletions(-) diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java index 14b917951f..8aa5776779 100644 --- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java +++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/BasePublisherActor.java @@ -482,12 +482,7 @@ private SendingOrDropped publishToGenericTarget(final ExpressionResolver resolve .tag(SpanTagKey.CONNECTION_TARGET.getTagForValue(publishTarget.toString())) .start(); final var mappedMessageWithTraceContext = - mappedMessage.withHeaders(startedSpan.propagateContext( - DittoHeaders.newBuilder(mappedMessage.getHeaders()) - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - .asCaseSensitiveMap() - )); + mappedMessage.withHeaders(startedSpan.propagateContext(mappedMessage.getHeaders())); final CompletionStage responsesFuture = publishMessage(outboundSource, autoAckTarget, diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/amqp/AmqpConsumerActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/amqp/AmqpConsumerActor.java index 80c0a9f2fb..bc6d6104f0 100644 --- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/amqp/AmqpConsumerActor.java +++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/amqp/AmqpConsumerActor.java @@ -363,12 +363,7 @@ private void handleJmsMessage(final JmsMessage message) throws JMSException { .correlationId(correlationId) .connectionId(connectionId) .start(); - headers = startedSpan.propagateContext(DittoHeaders.of(headers) - .toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - .asCaseSensitiveMap() - ); + headers = startedSpan.propagateContext(headers); final ExternalMessageBuilder builder = ExternalMessageFactory.newExternalMessageBuilder(headers); final ExternalMessage externalMessage = extractPayloadFromMessage(message, builder) .withAuthorizationContext(source.getAuthorizationContext()) diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/kafka/KafkaMessageTransformer.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/kafka/KafkaMessageTransformer.java index a64b215c0c..e5d45b3651 100644 --- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/kafka/KafkaMessageTransformer.java +++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/kafka/KafkaMessageTransformer.java @@ -112,12 +112,7 @@ public TransformationResult transform(final ConsumerRecord c ).correlationId(correlationId) .connectionId(connectionId) .start(); - messageHeaders = startedSpan.propagateContext(DittoHeaders.of(messageHeaders) - .toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - .asCaseSensitiveMap() - ); + messageHeaders = startedSpan.propagateContext(messageHeaders); try { final String key = consumerRecord.key(); diff --git a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/rabbitmq/RabbitMQConsumerActor.java b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/rabbitmq/RabbitMQConsumerActor.java index 5da7bd294c..63a3cd317b 100644 --- a/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/rabbitmq/RabbitMQConsumerActor.java +++ b/connectivity/service/src/main/java/org/eclipse/ditto/connectivity/service/messaging/rabbitmq/RabbitMQConsumerActor.java @@ -151,12 +151,7 @@ private void handleDelivery(final Delivery delivery) { .connectionId(connectionId) .correlationId(correlationId) .start(); - headers = startedSpan.propagateContext(DittoHeaders.of(headers) - .toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - .asCaseSensitiveMap() - ); + headers = startedSpan.propagateContext(headers); final ExternalMessageBuilder externalMessageBuilder = ExternalMessageFactory.newExternalMessageBuilder(headers); diff --git a/edge/service/src/main/java/org/eclipse/ditto/edge/service/dispatching/ThingsAggregatorProxyActor.java b/edge/service/src/main/java/org/eclipse/ditto/edge/service/dispatching/ThingsAggregatorProxyActor.java index da2a4897f7..e04ca8c216 100644 --- a/edge/service/src/main/java/org/eclipse/ditto/edge/service/dispatching/ThingsAggregatorProxyActor.java +++ b/edge/service/src/main/java/org/eclipse/ditto/edge/service/dispatching/ThingsAggregatorProxyActor.java @@ -41,7 +41,6 @@ import org.eclipse.ditto.base.model.entity.id.WithEntityId; import org.eclipse.ditto.base.model.exceptions.DittoInternalErrorException; import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException; -import org.eclipse.ditto.base.model.headers.DittoHeaderDefinition; import org.eclipse.ditto.base.model.headers.DittoHeaders; import org.eclipse.ditto.base.model.json.Jsonifiable; import org.eclipse.ditto.base.model.signals.commands.Command; @@ -147,11 +146,7 @@ private void askTargetActor(final Command command, final List thingI .tag("size", Integer.toString(thingIds.size())) .start(); final Command tracedCommand = command.setDittoHeaders( - DittoHeaders.of(startedSpan.propagateContext( - dittoHeaders.toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - )) + DittoHeaders.of(startedSpan.propagateContext(dittoHeaders)) ); final DistributedPubSubMediator.Publish pubSubMsg = diff --git a/gateway/service/src/main/java/org/eclipse/ditto/gateway/service/endpoints/directives/RequestTracingDirective.java b/gateway/service/src/main/java/org/eclipse/ditto/gateway/service/endpoints/directives/RequestTracingDirective.java index 166a132fef..7d43bd1df5 100644 --- a/gateway/service/src/main/java/org/eclipse/ditto/gateway/service/endpoints/directives/RequestTracingDirective.java +++ b/gateway/service/src/main/java/org/eclipse/ditto/gateway/service/endpoints/directives/RequestTracingDirective.java @@ -172,7 +172,7 @@ private static HttpRequest adjustSpanContextHeadersOfRequest( ) { final Set headerNames = new HashSet<>(); final Map httpHeaders = StreamSupport.stream(originalRequest.getHeaders().spliterator(), false) - .map(httpHeader -> { + .peek(httpHeader -> { if (!headerNames.add(httpHeader.name())) { throw GatewayDuplicateHeaderException.newBuilder(httpHeader.name()) .dittoHeaders(DittoHeaders.newBuilder() @@ -180,11 +180,7 @@ private static HttpRequest adjustSpanContextHeadersOfRequest( .build() ).build(); } - return httpHeader; }) - .filter(httpHeader -> - !DittoHeaderDefinition.W3C_TRACEPARENT.getKey().equals(httpHeader.name()) - ) .collect(Collectors.toMap(HttpHeader::name, HttpHeader::value, (dv1, dv2) -> { throw GatewayDuplicateHeaderException.newBuilder() .dittoHeaders(DittoHeaders.newBuilder() diff --git a/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/ByRoundTripSignalEnrichmentFacade.java b/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/ByRoundTripSignalEnrichmentFacade.java index 65bac9c234..7323645567 100644 --- a/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/ByRoundTripSignalEnrichmentFacade.java +++ b/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/ByRoundTripSignalEnrichmentFacade.java @@ -91,9 +91,7 @@ public CompletionStage retrievePartialThing(final ThingId thingId, final RetrieveThing command = RetrieveThing.getBuilder(thingId, DittoHeaders.of(startedSpan.propagateContext( - dittoHeadersBuilder - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() + dittoHeadersBuilder.build() ))) .withSelectedFields(jsonFieldSelector) .build(); diff --git a/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/DittoCachingSignalEnrichmentFacade.java b/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/DittoCachingSignalEnrichmentFacade.java index fc37e2586d..1d91184f11 100644 --- a/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/DittoCachingSignalEnrichmentFacade.java +++ b/internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/DittoCachingSignalEnrichmentFacade.java @@ -144,11 +144,7 @@ private static DittoHeaders buildDittoHeadersNotAddedToCacheKey(final List createJsonifiableFrom( beforeDeserializeInstant ); final var result = - deserializeJson(payload, manifest, DittoHeaders.of(startedSpan.propagateContext( - dittoHeaders.toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - ))); + deserializeJson(payload, manifest, DittoHeaders.of(startedSpan.propagateContext(dittoHeaders))); try { return result; } finally { diff --git a/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceActor.java b/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceActor.java index 3053b4156b..832224ba72 100755 --- a/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceActor.java +++ b/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceActor.java @@ -730,12 +730,7 @@ private > void handleByStrategy(final T command, @Nullable .start(); final var tracedCommand = - command.setDittoHeaders(DittoHeaders.of(startedSpan.propagateContext( - command.getDittoHeaders() - .toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - ))); + command.setDittoHeaders(DittoHeaders.of(startedSpan.propagateContext(command.getDittoHeaders()))); accessCounter++; Result result; diff --git a/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceSupervisor.java b/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceSupervisor.java index e7c1b917aa..1e6c9daf4b 100644 --- a/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceSupervisor.java +++ b/internal/utils/persistent-actors/src/main/java/org/eclipse/ditto/internal/utils/persistentactors/AbstractPersistenceSupervisor.java @@ -908,11 +908,7 @@ protected CompletionStage enforceSignalAndForwardToTargetActor(final S s .correlationId(signal.getDittoHeaders().getCorrelationId().orElse(null)) .start(); final var tracedSignal = signal.setDittoHeaders( - DittoHeaders.of(startedSpan.propagateContext( - signal.getDittoHeaders().toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - )) + DittoHeaders.of(startedSpan.propagateContext(signal.getDittoHeaders())) ); final StartedTimer rootTimer = createTimer(tracedSignal); final StartedTimer enforcementTimer = rootTimer.startNewSegment(ENFORCEMENT_TIMER_SEGMENT_ENFORCEMENT); diff --git a/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/KamonHttpContextPropagation.java b/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/KamonHttpContextPropagation.java index aa68cf1c96..bdca7c44ba 100644 --- a/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/KamonHttpContextPropagation.java +++ b/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/KamonHttpContextPropagation.java @@ -99,7 +99,7 @@ public Context getContextFromHeaders(final Map headers) { public Map propagateContextToHeaders(final Context context, final Map headers) { checkNotNull(context, "context"); final var result = getMutableCopyOfMap(checkNotNull(headers, "headers")); - propagation.write(context, result::putIfAbsent); + propagation.write(context, result::put); // always put, not only if absent in order to overwrite existing values return result; } diff --git a/policies/enforcement/src/main/java/org/eclipse/ditto/policies/enforcement/AbstractEnforcerActor.java b/policies/enforcement/src/main/java/org/eclipse/ditto/policies/enforcement/AbstractEnforcerActor.java index 323a4bc603..299951dd9d 100644 --- a/policies/enforcement/src/main/java/org/eclipse/ditto/policies/enforcement/AbstractEnforcerActor.java +++ b/policies/enforcement/src/main/java/org/eclipse/ditto/policies/enforcement/AbstractEnforcerActor.java @@ -28,7 +28,6 @@ import org.eclipse.ditto.base.model.entity.id.EntityId; import org.eclipse.ditto.base.model.exceptions.DittoInternalErrorException; import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException; -import org.eclipse.ditto.base.model.headers.DittoHeaderDefinition; import org.eclipse.ditto.base.model.headers.DittoHeaders; import org.eclipse.ditto.base.model.headers.WithDittoHeaders; import org.eclipse.ditto.base.model.signals.Signal; @@ -138,11 +137,7 @@ private void doEnforceSignal(final S signal, final ActorRef sender) { .start(); final Optional formerTraceParent = dittoHeaders.getTraceParent(); final var tracedSignal = signal.setDittoHeaders( - DittoHeaders.of(startedSpan.propagateContext( - dittoHeaders.toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - )) + DittoHeaders.of(startedSpan.propagateContext(dittoHeaders)) ); final ActorRef self = getSelf(); diff --git a/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/starter/actors/SearchActor.java b/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/starter/actors/SearchActor.java index 982a3312b7..9bbfe82e76 100644 --- a/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/starter/actors/SearchActor.java +++ b/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/starter/actors/SearchActor.java @@ -45,7 +45,6 @@ import org.eclipse.ditto.base.model.exceptions.DittoInternalErrorException; import org.eclipse.ditto.base.model.exceptions.DittoJsonException; import org.eclipse.ditto.base.model.exceptions.DittoRuntimeException; -import org.eclipse.ditto.base.model.headers.DittoHeaderDefinition; import org.eclipse.ditto.base.model.headers.DittoHeaders; import org.eclipse.ditto.base.model.headers.WithDittoHeaders; import org.eclipse.ditto.base.model.signals.Signal; @@ -304,10 +303,7 @@ private > CompletionStage executeCount(final T coun @SuppressWarnings("unchecked") final T tracedCountCommand = (T) countCommand.setDittoHeaders( - DittoHeaders.of(spanWithTimer.startedSpan.propagateContext(dittoHeaders.toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - ))); + DittoHeaders.of(spanWithTimer.startedSpan.propagateContext(dittoHeaders))); final Source countThingsResponseSource = createQuerySource(queryParseFunction, tracedCountCommand) @@ -352,10 +348,7 @@ private CompletionStage performStream(final StreamThings streamThings, f final var namespaces = streamThings.getNamespaces().orElse(null); final StreamThings tracedStreamThings = streamThings.setDittoHeaders( - DittoHeaders.of(spanWithTimer.startedSpan.propagateContext(streamThings.getDittoHeaders().toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - ))); + DittoHeaders.of(spanWithTimer.startedSpan.propagateContext(streamThings.getDittoHeaders()))); final Source, NotUsed> thingIdSourceRefSource = ThingsSearchCursor.extractCursor(tracedStreamThings).flatMapConcat(cursor -> { @@ -472,10 +465,7 @@ private CompletionStage performQuery(final QueryThings queryThings, fina final var namespaces = queryThings.getNamespaces().orElse(null); final QueryThings tracedQueryThings = queryThings.setDittoHeaders( - DittoHeaders.of(spanWithTimer.startedSpan.propagateContext(queryThings.getDittoHeaders().toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - ))); + DittoHeaders.of(spanWithTimer.startedSpan.propagateContext(queryThings.getDittoHeaders()))); final Source queryThingsResponseSource = ThingsSearchCursor.extractCursor(tracedQueryThings, getSystem()).flatMapConcat(cursor -> { diff --git a/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/updater/actors/ThingUpdater.java b/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/updater/actors/ThingUpdater.java index c4fda51450..85010ab302 100644 --- a/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/updater/actors/ThingUpdater.java +++ b/thingsearch/service/src/main/java/org/eclipse/ditto/thingsearch/service/updater/actors/ThingUpdater.java @@ -548,10 +548,7 @@ private Optional computeEventMetadata(final ThingEvent thingEvent, final StartedSpan startedSpan = DittoTracing.newStartedSpanByTimer(thingEvent.getDittoHeaders(), startedTimer); ConsistencyLag.startS1InUpdater(startedTimer); final var tracedEvent = thingEvent.setDittoHeaders(DittoHeaders.of(startedSpan.propagateContext( - thingEvent.getDittoHeaders().toBuilder() - .removeHeader(DittoHeaderDefinition.W3C_TRACEPARENT.getKey()) - .build() - ))); + thingEvent.getDittoHeaders()))); final var metadata = exportMetadataWithSender(shouldAcknowledge, tracedEvent, getAckRecipient( tracedEvent.getDittoHeaders()), startedTimer, data) .withUpdateReason(UpdateReason.THING_UPDATE); From 028904fa836653e202390329f0947637b9c23488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20J=C3=A4ckle?= Date: Wed, 29 Jan 2025 16:01:18 +0100 Subject: [PATCH 2/2] fix tracestate propagation --- .../utils/tracing/span/StartedKamonSpan.java | 14 +++++++++++--- .../model/ImmutableThingFromScratchBuilder.java | 4 ++-- .../eclipse/ditto/things/model/ThingBuilder.java | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/StartedKamonSpan.java b/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/StartedKamonSpan.java index 09d71cac2d..051b091bcc 100644 --- a/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/StartedKamonSpan.java +++ b/internal/utils/tracing/src/main/java/org/eclipse/ditto/internal/utils/tracing/span/StartedKamonSpan.java @@ -18,8 +18,10 @@ import java.time.Instant; import java.util.Map; +import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; +import org.eclipse.ditto.base.model.headers.DittoHeaderDefinition; import org.eclipse.ditto.internal.utils.metrics.instruments.tag.Tag; import org.eclipse.ditto.internal.utils.metrics.instruments.tag.TagSet; @@ -113,11 +115,17 @@ public SpanOperationName getOperationName() { @Override public Map propagateContext(final Map headers) { - return httpContextPropagation.propagateContextToHeaders(wrapSpanInContext(), headers); + return httpContextPropagation.propagateContextToHeaders(wrapSpanInContext( + headers.get(DittoHeaderDefinition.W3C_TRACESTATE.getKey()) + ), headers); } - private Context wrapSpanInContext() { - return Context.of(Span.Key(), span); + private Context wrapSpanInContext(@Nullable final String traceStateHeader) { + if (traceStateHeader != null) { + return Context.of(Span.Key(), span, Context.key("tracestate", ""), traceStateHeader); + } else { + return Context.of(Span.Key(), span); + } } @Override diff --git a/things/model/src/main/java/org/eclipse/ditto/things/model/ImmutableThingFromScratchBuilder.java b/things/model/src/main/java/org/eclipse/ditto/things/model/ImmutableThingFromScratchBuilder.java index 23d8ebdeb0..a967a74b11 100755 --- a/things/model/src/main/java/org/eclipse/ditto/things/model/ImmutableThingFromScratchBuilder.java +++ b/things/model/src/main/java/org/eclipse/ditto/things/model/ImmutableThingFromScratchBuilder.java @@ -20,10 +20,10 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; +import org.eclipse.ditto.base.model.entity.metadata.Metadata; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; -import org.eclipse.ditto.base.model.entity.metadata.Metadata; import org.eclipse.ditto.policies.model.PolicyId; /** @@ -169,7 +169,7 @@ public FromScratch setFeature(final String featureId) { @Override public FromScratch setFeature(final String featureId, @Nullable final FeatureDefinition featureDefinition, - final FeatureProperties featureProperties) { + @Nullable final FeatureProperties featureProperties) { return setFeature(ThingsModelFactory.newFeature(featureId, featureDefinition, featureProperties)); } diff --git a/things/model/src/main/java/org/eclipse/ditto/things/model/ThingBuilder.java b/things/model/src/main/java/org/eclipse/ditto/things/model/ThingBuilder.java index 8d29c9337a..5bb1e0f679 100755 --- a/things/model/src/main/java/org/eclipse/ditto/things/model/ThingBuilder.java +++ b/things/model/src/main/java/org/eclipse/ditto/things/model/ThingBuilder.java @@ -196,7 +196,7 @@ interface FromScratch { * @throws NullPointerException if {@code featureId} is {@code null}. */ FromScratch setFeature(String featureId, FeatureDefinition featureDefinition, - FeatureProperties featureProperties); + @Nullable FeatureProperties featureProperties); /** * Sets a Feature with the given ID and properties to this builder. A previously set Feature with the