diff --git a/java/pom.xml b/java/pom.xml index 5f2aad32..86016d9d 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -330,7 +330,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M1 + 3.0.0-M4 true @@ -345,7 +345,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.0.0-M1 + 3.0.0-M4 integration-test @@ -445,6 +445,8 @@ org.eclipse.ditto.client.messaging.internal org.eclipse.ditto.client.options.internal org.eclipse.ditto.client.twin.internal + + org.eclipse.ditto.client.configuration.ClientCredentialsAuthenticationConfiguration$ClientCredentialsAuthenticationConfigurationBuilder @@ -825,6 +827,11 @@ ditto-signals-base ${ditto.version} + + org.eclipse.ditto + ditto-signals-acks-things + ${ditto.version} + org.eclipse.ditto ditto-signals-commands-base diff --git a/java/src/main/assembly/assembly.xml b/java/src/main/assembly/assembly.xml index 24d98d85..d7381985 100755 --- a/java/src/main/assembly/assembly.xml +++ b/java/src/main/assembly/assembly.xml @@ -40,6 +40,8 @@ org.eclipse.ditto:ditto-model-things org.eclipse.ditto:ditto-model-thingsearch org.eclipse.ditto:ditto-signals-base + org.eclipse.ditto:ditto-signals-acks-base + org.eclipse.ditto:ditto-signals-acks-things org.eclipse.ditto:ditto-signals-commands-base org.eclipse.ditto:ditto-signals-commands-thingsearch org.eclipse.ditto:ditto-signals-commands-things diff --git a/java/src/main/java/org/eclipse/ditto/client/changes/AcknowledgementRequestHandle.java b/java/src/main/java/org/eclipse/ditto/client/changes/AcknowledgementRequestHandle.java new file mode 100644 index 00000000..07e87804 --- /dev/null +++ b/java/src/main/java/org/eclipse/ditto/client/changes/AcknowledgementRequestHandle.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.changes; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; +import org.eclipse.ditto.model.base.common.HttpStatusCode; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.headers.WithDittoHeaders; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; + +/** + * Abstraction for handling {@code AcknowledgementRequest}s issued by the Ditto backend together with e.g. + * {@code Event}s. This handle provides means to {@code acknowledge} such acknowledgement requests with a custom + * {@code HttpStatusCode}, an optional {@code payload} or - alternatively - with a self built {@code Acknowledgement}. + * + * @since 1.1.0 + */ +@Immutable +public interface AcknowledgementRequestHandle extends WithDittoHeaders { + + /** + * Returns the {@code AcknowledgementLabel} this handle was created for. + * + * @return the acknowledgement label to handle. + */ + AcknowledgementLabel getAcknowledgementLabel(); + + /** + * Returns the entity ID containing the entity type this handle was created for. + * + * @return the entity id to handle. + */ + EntityIdWithType getEntityId(); + + /** + * Builds and sends an {@code Acknowledgement} to the Ditto backend based on the information this handle instance + * already has, combined with the passed {@code statusCode} and no {@code payload}. + * + * @param statusCode the http status code to apply for the acknowledgement to send: use a range between 200 and 300 + * in order to declare a successful acknowledgement and a status code above 400 to declare a not successful one. + */ + void acknowledge(HttpStatusCode statusCode); + + /** + * Builds and sends an {@code Acknowledgement} to the Ditto backend based on the information this handle instance + * already has, combined with the passed {@code statusCode} and the passed {@code payload}. + * + * @param statusCode the http status code to apply for the acknowledgement to send: use a range between 200 and 300 + * in order to declare a successful acknowledgement and a status code above 400 to declare a not successful one. + * @param payload the payload as {@code JsonValue} to include in the sent acknowledgement. + */ + void acknowledge(HttpStatusCode statusCode, @Nullable JsonValue payload); + + /** + * Sends the passed {@code acknowledgement} to the Ditto backend. + * + * @param acknowledgement the already built {@code Acknowledgement} to send to the Ditto backend. + */ + void acknowledge(Acknowledgement acknowledgement); + +} diff --git a/java/src/main/java/org/eclipse/ditto/client/changes/Change.java b/java/src/main/java/org/eclipse/ditto/client/changes/Change.java index 637733ff..7d338bd1 100755 --- a/java/src/main/java/org/eclipse/ditto/client/changes/Change.java +++ b/java/src/main/java/org/eclipse/ditto/client/changes/Change.java @@ -13,13 +13,18 @@ package org.eclipse.ditto.client.changes; import java.time.Instant; +import java.util.Collection; import java.util.Optional; +import java.util.function.Consumer; import javax.annotation.Nullable; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; +import org.eclipse.ditto.model.base.entity.type.WithEntityType; +import org.eclipse.ditto.model.base.headers.WithDittoHeaders; import org.eclipse.ditto.signals.base.WithId; /** @@ -27,7 +32,7 @@ * * @since 1.0.0 */ -public interface Change extends WithId { +public interface Change extends WithId, WithEntityType, WithDittoHeaders { /** * Returns the {@link ChangeAction} which caused this change. @@ -88,8 +93,8 @@ default boolean isFull() { /** * Returns the extra information which enriches the actual value of this change. * - * @since 1.1.0 * @return the extra data or an empty Optional. + * @since 1.1.0 */ Optional getExtra(); @@ -97,10 +102,46 @@ default boolean isFull() { * Sets the given extra information which enriches the actual value of the change. * Previously set extra is replaced. * - * @since 1.1.0 * @param extra the extra data information or {@code null}. * @return a new instance of this change with the added {@code extra} data. + * @since 1.1.0 */ Change withExtra(@Nullable JsonObject extra); + /** + * Sets the given {@code path} and {@code value} into the change. + * + * @param path the relative path of the changed JSON field. + * @param value the optional value of the changed JSON field. + * @return a new instance of this change with the added data. + * @since 1.1.0 + */ + Change withPathAndValue(JsonPointer path, @Nullable JsonValue value); + + /** + * Handles {@code AcknowledgementRequest}s issued by the Ditto backend for a received event translated into this + * change by invoking the passed {@code acknowledgementHandles} consumer with client side + * {@code AcknowledgementHandle}s. + * + * @param acknowledgementHandles the consumer to invoke with a collection of {@code AcknowledgementHandle}s used to + * send back {@code Acknowledgements}. + * @since 1.1.0 + */ + void handleAcknowledgementRequests(Consumer> acknowledgementHandles); + + /** + * Handles an {@code AcknowledgementRequest} identified by the passed {@code acknowledgementLabel} issued by the + * Ditto backend for a received event translated into this change by invoking the passed + * {@code acknowledgementHandle} consumer with a client side {@code AcknowledgementHandle} - if the passed + * acknowledgementLabel was present in the requested acknowledgements. + * + * @param acknowledgementLabel the {@code AcknowledgementLabel} which should be handled - if present - by the passed + * {@code acknowledgementHandle}. + * @param acknowledgementHandle the consumer to invoke with a {@code AcknowledgementHandle} used to + * send back an {@code Acknowledgement}. + * @since 1.1.0 + */ + void handleAcknowledgementRequest(AcknowledgementLabel acknowledgementLabel, + Consumer acknowledgementHandle); + } diff --git a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableAcknowledgementRequestHandle.java b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableAcknowledgementRequestHandle.java new file mode 100644 index 00000000..9bc13228 --- /dev/null +++ b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableAcknowledgementRequestHandle.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.changes.internal; + +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; + +import java.util.Objects; +import java.util.function.Consumer; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +import org.eclipse.ditto.client.changes.AcknowledgementRequestHandle; +import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; +import org.eclipse.ditto.model.base.common.HttpStatusCode; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.headers.DittoHeaders; +import org.eclipse.ditto.model.base.headers.DittoHeadersBuilder; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; + +/** + * An immutable implementation of {@link AcknowledgementRequestHandle}. + * + * @since 1.1.0 + */ +@Immutable +final class ImmutableAcknowledgementRequestHandle implements AcknowledgementRequestHandle { + + private final AcknowledgementLabel acknowledgementLabel; + private final EntityIdWithType entityId; + private final DittoHeaders dittoHeaders; + private final Consumer acknowledgementPublisher; + + /** + * Creates a new ImmutableAcknowledgementRequestHandle instance. + * + * @param acknowledgementLabel the acknowledgement label to handle. + * @param entityId the entity id to handle. + * @param dittoHeaders the ditto headers which were contained in the acknowledgement request to handle. + * @param acknowledgementPublisher the consumer for publishing built acknowledgements to the Ditto backend. + */ + ImmutableAcknowledgementRequestHandle(final AcknowledgementLabel acknowledgementLabel, + final EntityIdWithType entityId, + final DittoHeaders dittoHeaders, + final Consumer acknowledgementPublisher) { + + this.acknowledgementLabel = checkNotNull(acknowledgementLabel, "acknowledgementLabel"); + this.entityId = checkNotNull(entityId, "entityId"); + this.dittoHeaders = checkNotNull(dittoHeaders, "dittoHeaders"); + this.acknowledgementPublisher = checkNotNull(acknowledgementPublisher, "acknowledgementPublisher"); + } + + @Override + public AcknowledgementLabel getAcknowledgementLabel() { + return acknowledgementLabel; + } + + @Override + public EntityIdWithType getEntityId() { + return entityId; + } + + @Override + public DittoHeaders getDittoHeaders() { + return dittoHeaders; + } + + @Override + public AcknowledgementRequestHandle setDittoHeaders(final DittoHeaders dittoHeaders) { + return new ImmutableAcknowledgementRequestHandle(acknowledgementLabel, entityId, dittoHeaders, + acknowledgementPublisher); + } + + @Override + public void acknowledge(final HttpStatusCode statusCode) { + acknowledge(statusCode, null); + } + + @Override + public void acknowledge(final HttpStatusCode statusCode, @Nullable final JsonValue payload) { + // only retain the bare minimum of received DittoHeaders by default: + final DittoHeadersBuilder dittoHeadersBuilder = DittoHeaders.newBuilder(); + dittoHeaders.getCorrelationId().ifPresent(dittoHeadersBuilder::correlationId); + final DittoHeaders minimizedDittoHeaders = dittoHeadersBuilder.build(); + acknowledge(Acknowledgement.of(acknowledgementLabel, entityId, statusCode, minimizedDittoHeaders, payload)); + } + + @Override + public void acknowledge(final Acknowledgement acknowledgement) { + acknowledgementPublisher.accept(acknowledgement); + } + + @Override + public boolean equals(@Nullable final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ImmutableAcknowledgementRequestHandle that = (ImmutableAcknowledgementRequestHandle) o; + return Objects.equals(acknowledgementLabel, that.acknowledgementLabel) && + Objects.equals(entityId, that.entityId) && + Objects.equals(dittoHeaders, that.dittoHeaders); + } + + @Override + public int hashCode() { + return Objects.hash(acknowledgementLabel, entityId, dittoHeaders); + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [" + + "acknowledgementLabel=" + acknowledgementLabel + + ", entityId=" + entityId + + ", dittoHeaders=" + dittoHeaders + + "]"; + } +} diff --git a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableChange.java b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableChange.java index de6352c3..d16d044b 100644 --- a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableChange.java +++ b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableChange.java @@ -12,21 +12,31 @@ */ package org.eclipse.ditto.client.changes.internal; -import static org.eclipse.ditto.model.base.common.ConditionChecker.argumentNotNull; +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; import java.time.Instant; +import java.util.Collection; +import java.util.LinkedHashSet; import java.util.Objects; import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import org.eclipse.ditto.client.changes.AcknowledgementRequestHandle; import org.eclipse.ditto.client.changes.Change; import org.eclipse.ditto.client.changes.ChangeAction; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; import org.eclipse.ditto.model.base.entity.id.EntityId; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.entity.type.EntityType; +import org.eclipse.ditto.model.base.headers.DittoHeaders; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; /** * An immutable holder for an {@link org.eclipse.ditto.model.base.entity.id.EntityId} and a {@link ChangeAction}. @@ -37,40 +47,49 @@ @Immutable public final class ImmutableChange implements Change { - private final EntityId entityId; + private final EntityIdWithType entityId; private final ChangeAction action; private final JsonPointer path; @Nullable private final JsonValue value; private final long revision; @Nullable private final Instant timestamp; @Nullable private final JsonObject extra; + private final DittoHeaders dittoHeaders; + private final Consumer acknowledgementPublisher; /** * Constructs a new {@code ImmutableChange} object. * - * @param entityId ID of the changed entity. + * @param entityId ID (with EntityType) of the changed entity. * @param changeAction the operation which caused the change. * @param path the JsonPointer of the changed json field. * @param value the value of the changed json field. * @param revision the revision (change counter) of the change. * @param timestamp the timestamp of the change. * @param extra the extra data to be included in the change. + * @param dittoHeaders the DittoHeaders of the event which lead to the change. + * @param acknowledgementPublisher the consumer for publishing built acknowledgements to the Ditto backend. + * @throws NullPointerException if any required argument is {@code null}. */ - public ImmutableChange(final EntityId entityId, + public ImmutableChange(final EntityIdWithType entityId, final ChangeAction changeAction, final JsonPointer path, @Nullable final JsonValue value, final long revision, @Nullable final Instant timestamp, - @Nullable final JsonObject extra) { + @Nullable final JsonObject extra, + final DittoHeaders dittoHeaders, + final Consumer acknowledgementPublisher) { - this.entityId = argumentNotNull(entityId, "entityId"); - this.action = argumentNotNull(changeAction, "changeAction"); - this.path = argumentNotNull(path, "path"); + this.entityId = checkNotNull(entityId, "entityId"); + this.action = checkNotNull(changeAction, "changeAction"); + this.path = checkNotNull(path, "path"); this.value = value; this.revision = revision; this.timestamp = timestamp; this.extra = extra; + this.dittoHeaders = checkNotNull(dittoHeaders, "dittoHeaders"); + this.acknowledgementPublisher = checkNotNull(acknowledgementPublisher, "acknowledgementPublisher"); } @Override @@ -78,6 +97,11 @@ public EntityId getEntityId() { return entityId; } + @Override + public EntityType getEntityType() { + return entityId.getEntityType(); + } + @Override public ChangeAction getAction() { return action; @@ -110,11 +134,54 @@ public Optional getExtra() { @Override public Change withExtra(@Nullable final JsonObject extra) { - return new ImmutableChange(entityId, action, path, value, revision, timestamp, extra); + return new ImmutableChange(entityId, action, path, value, revision, timestamp, extra, dittoHeaders, + acknowledgementPublisher); + } + + @Override + public DittoHeaders getDittoHeaders() { + return dittoHeaders; + } + + @Override + public Change setDittoHeaders(final DittoHeaders dittoHeaders) { + return new ImmutableChange(entityId, action, path, value, revision, timestamp, extra, dittoHeaders, + acknowledgementPublisher); + } + + @Override + public void handleAcknowledgementRequests(final Consumer> acknowledgementHandles) { + + checkNotNull(acknowledgementHandles, "handleConsumers"); + acknowledgementHandles.accept( + getDittoHeaders().getAcknowledgementRequests().stream() + .map(request -> new ImmutableAcknowledgementRequestHandle(request.getLabel(), entityId, dittoHeaders, + acknowledgementPublisher)) + .collect(Collectors.toCollection(LinkedHashSet::new)) + ); + } + + @Override + public Change withPathAndValue(final JsonPointer path, @Nullable final JsonValue value) { + return new ImmutableChange(entityId, action, path, value, revision, timestamp, extra, dittoHeaders, + acknowledgementPublisher); + } + + @Override + public void handleAcknowledgementRequest(final AcknowledgementLabel acknowledgementLabel, + final Consumer acknowledgementHandle) { + + checkNotNull(acknowledgementLabel, "acknowledgementLabel"); + checkNotNull(acknowledgementHandle, "handleConsumer"); + getDittoHeaders().getAcknowledgementRequests().stream() + .filter(req -> req.getLabel().equals(acknowledgementLabel)) + .map(request -> new ImmutableAcknowledgementRequestHandle(request.getLabel(), entityId, dittoHeaders, + acknowledgementPublisher)) + .forEach(acknowledgementHandle); } @Override - public boolean equals(final Object o) { + public boolean equals(@Nullable final Object o) { if (this == o) { return true; } @@ -127,13 +194,14 @@ public boolean equals(final Object o) { Objects.equals(path, that.path) && Objects.equals(value, that.value) && revision == that.revision && - Objects.equals(timestamp, that.timestamp)&& - Objects.equals(extra, that.extra); + Objects.equals(timestamp, that.timestamp) && + Objects.equals(extra, that.extra) && + Objects.equals(dittoHeaders, that.dittoHeaders); } @Override public int hashCode() { - return Objects.hash(entityId, action, path, value, revision, timestamp, extra); + return Objects.hash(entityId, action, path, value, revision, timestamp, extra, dittoHeaders); } @Override @@ -146,6 +214,7 @@ public String toString() { ", revision=" + revision + ", timestamp=" + timestamp + ", extra=" + extra + + ", dittoHeaders=" + dittoHeaders + "]"; } diff --git a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeatureChange.java b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeatureChange.java index d0f2344b..0a278386 100644 --- a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeatureChange.java +++ b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeatureChange.java @@ -12,21 +12,31 @@ */ package org.eclipse.ditto.client.changes.internal; +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; + import java.time.Instant; +import java.util.Collection; import java.util.Objects; import java.util.Optional; +import java.util.function.Consumer; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import org.eclipse.ditto.client.changes.AcknowledgementRequestHandle; import org.eclipse.ditto.client.changes.Change; import org.eclipse.ditto.client.changes.ChangeAction; import org.eclipse.ditto.client.changes.FeatureChange; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; import org.eclipse.ditto.model.base.entity.id.EntityId; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.entity.type.EntityType; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.things.Feature; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; /** * An immutable implementation of {@link org.eclipse.ditto.client.changes.FeatureChange}. @@ -49,18 +59,35 @@ public final class ImmutableFeatureChange implements FeatureChange { * @param revision the revision (change counter) of the change. * @param timestamp the timestamp of the change. * @param extra the extra data to be included in the change. - * @throws IllegalArgumentException if any argument is {@code null}. + * @param dittoHeaders the DittoHeaders of the event which lead to the change. + * @param acknowledgementPublisher the consumer for publishing built acknowledgements to the Ditto backend. + * @throws NullPointerException if any required argument is {@code null}. */ - public ImmutableFeatureChange(final EntityId entityId, + public ImmutableFeatureChange(final EntityIdWithType entityId, final ChangeAction changeAction, @Nullable final Feature feature, final JsonPointer path, final long revision, @Nullable final Instant timestamp, - @Nullable final JsonObject extra) { + @Nullable final JsonObject extra, + final DittoHeaders dittoHeaders, + final Consumer acknowledgementPublisher) { this(new ImmutableChange(entityId, changeAction, path, getJsonValueForFeature(feature), revision, timestamp, - extra), feature); + extra, dittoHeaders, acknowledgementPublisher), feature); + } + + /** + * Constructs a new {@code ImmutableFeatureChange} object. + * + * @param change the change to use for delegation. + * @param feature the additional {@code Feature} this FeatureChange is aware of. + * @throws NullPointerException if {@code change} is {@code null}. + * @since 1.1.0 + */ + public ImmutableFeatureChange(final Change change, @Nullable final Feature feature) { + this.change = checkNotNull(change, "change"); + this.feature = feature; } @Nullable @@ -68,16 +95,16 @@ private static JsonValue getJsonValueForFeature(@Nullable final Feature feature) return null != feature ? feature.toJson(feature.getImplementedSchemaVersion()) : null; } - private ImmutableFeatureChange(final Change delegationTarget, @Nullable final Feature feature) { - change = delegationTarget; - this.feature = feature; - } - @Override public EntityId getEntityId() { return change.getEntityId(); } + @Override + public EntityType getEntityType() { + return change.getEntityType(); + } + @Override public ChangeAction getAction() { return change.getAction(); @@ -119,7 +146,33 @@ public ImmutableFeatureChange withExtra(@Nullable final JsonObject extra) { } @Override - public boolean equals(final Object o) { + public DittoHeaders getDittoHeaders() { + return change.getDittoHeaders(); + } + + @Override + public Change setDittoHeaders(final DittoHeaders dittoHeaders) { + return new ImmutableFeatureChange(change.setDittoHeaders(dittoHeaders), feature); + } + + @Override + public void handleAcknowledgementRequests(final Consumer> acknowledgementHandles) { + change.handleAcknowledgementRequests(acknowledgementHandles); + } + + @Override + public Change withPathAndValue(final JsonPointer path, @Nullable final JsonValue value) { + return new ImmutableFeatureChange(change.withPathAndValue(path, value), feature); + } + + @Override + public void handleAcknowledgementRequest(final AcknowledgementLabel acknowledgementLabel, + final Consumer acknowledgementHandle) { + change.handleAcknowledgementRequest(acknowledgementLabel, acknowledgementHandle); + } + + @Override + public boolean equals(@Nullable final Object o) { if (this == o) { return true; } diff --git a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeaturesChange.java b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeaturesChange.java index 16542379..dc1d6863 100644 --- a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeaturesChange.java +++ b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableFeaturesChange.java @@ -12,21 +12,31 @@ */ package org.eclipse.ditto.client.changes.internal; +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; + import java.time.Instant; +import java.util.Collection; import java.util.Objects; import java.util.Optional; +import java.util.function.Consumer; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import org.eclipse.ditto.client.changes.AcknowledgementRequestHandle; import org.eclipse.ditto.client.changes.Change; import org.eclipse.ditto.client.changes.ChangeAction; import org.eclipse.ditto.client.changes.FeaturesChange; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; import org.eclipse.ditto.model.base.entity.id.EntityId; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.entity.type.EntityType; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.things.Features; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; /** * An immutable implementation of {@link org.eclipse.ditto.client.changes.FeaturesChange}. @@ -49,18 +59,35 @@ public final class ImmutableFeaturesChange implements FeaturesChange { * @param revision the revision (change counter) of the change. * @param timestamp the timestamp of the change. * @param extra the extra data to be included in the change. - * @throws IllegalArgumentException if any argument is {@code null}. + * @param dittoHeaders the DittoHeaders of the event which lead to the change. + * @param acknowledgementPublisher the consumer for publishing built acknowledgements to the Ditto backend. + * @throws NullPointerException if any required argument is {@code null}. */ - public ImmutableFeaturesChange(final EntityId entityId, + public ImmutableFeaturesChange(final EntityIdWithType entityId, final ChangeAction changeAction, @Nullable final Features features, final JsonPointer path, final long revision, @Nullable final Instant timestamp, - @Nullable final JsonObject extra) { + @Nullable final JsonObject extra, + final DittoHeaders dittoHeaders, + final Consumer acknowledgementPublisher) { this(new ImmutableChange(entityId, changeAction, path, getJsonValueForFeatures(features), revision, timestamp, - extra), features); + extra, dittoHeaders, acknowledgementPublisher), features); + } + + /** + * Constructs a new {@code ImmutableFeaturesChange} object. + * + * @param change the change to use for delegation. + * @param features the additional {@code Features} this FeaturesChange is aware of. + * @throws NullPointerException if {@code change} is {@code null}. + * @since 1.1.0 + */ + public ImmutableFeaturesChange(final Change change, @Nullable final Features features) { + this.change = checkNotNull(change, "change"); + this.features = features; } @Nullable @@ -68,16 +95,16 @@ private static JsonValue getJsonValueForFeatures(@Nullable final Features featur return null != features ? features.toJson(features.getImplementedSchemaVersion()) : null; } - private ImmutableFeaturesChange(final Change delegationTarget, @Nullable final Features features) { - change = delegationTarget; - this.features = features; - } - @Override public EntityId getEntityId() { return change.getEntityId(); } + @Override + public EntityType getEntityType() { + return change.getEntityType(); + } + @Override public ChangeAction getAction() { return change.getAction(); @@ -119,7 +146,33 @@ public Change withExtra(@Nullable final JsonObject extra) { } @Override - public boolean equals(final Object o) { + public DittoHeaders getDittoHeaders() { + return change.getDittoHeaders(); + } + + @Override + public Change setDittoHeaders(final DittoHeaders dittoHeaders) { + return new ImmutableFeaturesChange(change.setDittoHeaders(dittoHeaders), features); + } + + @Override + public Change withPathAndValue(final JsonPointer path, @Nullable final JsonValue value) { + return new ImmutableFeaturesChange(change.withPathAndValue(path, value), features); + } + + @Override + public void handleAcknowledgementRequests(final Consumer> acknowledgementHandles) { + change.handleAcknowledgementRequests(acknowledgementHandles); + } + + @Override + public void handleAcknowledgementRequest(final AcknowledgementLabel acknowledgementLabel, + final Consumer acknowledgementHandle) { + change.handleAcknowledgementRequest(acknowledgementLabel, acknowledgementHandle); + } + + @Override + public boolean equals(@Nullable final Object o) { if (this == o) { return true; } diff --git a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChange.java b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChange.java index 3e4558d7..bacfb01e 100644 --- a/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChange.java +++ b/java/src/main/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChange.java @@ -15,12 +15,15 @@ import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; import java.time.Instant; +import java.util.Collection; import java.util.Objects; import java.util.Optional; +import java.util.function.Consumer; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import org.eclipse.ditto.client.changes.AcknowledgementRequestHandle; import org.eclipse.ditto.client.changes.Change; import org.eclipse.ditto.client.changes.ChangeAction; import org.eclipse.ditto.client.changes.ThingChange; @@ -28,9 +31,14 @@ import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; import org.eclipse.ditto.model.base.entity.id.EntityId; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.entity.type.EntityType; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.things.Thing; import org.eclipse.ditto.model.things.ThingId; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; /** * Immutable implementation for {@link org.eclipse.ditto.client.changes.ThingChange}. @@ -54,18 +62,23 @@ public final class ImmutableThingChange implements ThingChange { * @param revision the revision (change counter) of the change. * @param timestamp the timestamp of the change. * @param extra the extra data to be included in the change. + * @param dittoHeaders the DittoHeaders of the event which lead to the change. + * @param acknowledgementPublisher the consumer for publishing built acknowledgements to the Ditto backend. * @throws NullPointerException if any required argument is {@code null}. */ - public ImmutableThingChange(final EntityId entityId, + public ImmutableThingChange(final EntityIdWithType entityId, final ChangeAction changeAction, @Nullable final Thing thing, final JsonPointer path, final long revision, @Nullable final Instant timestamp, - @Nullable final JsonObject extra) { + @Nullable final JsonObject extra, + final DittoHeaders dittoHeaders, + final Consumer acknowledgementPublisher) { this(new ImmutableChange(checkNotNull(entityId, "Thing ID"), checkNotNull(changeAction, "change action"), path, - getJsonValueForThing(thing), revision, timestamp, extra), thing); + getJsonValueForThing(thing), revision, timestamp, extra, dittoHeaders, acknowledgementPublisher), + thing); } @Nullable @@ -82,6 +95,8 @@ private static JsonValue getJsonValueForThing(@Nullable final Thing thing) { * deleted. * @param revision the revision (change counter) of the change. * @param timestamp the timestamp of the change. + * @param dittoHeaders the DittoHeaders of the event which lead to the change. + * @param acknowledgementPublisher the consumer for publishing built acknowledgements to the Ditto backend. * @param extra the extra data to be included in the change. * @throws NullPointerException if any required argument is {@code null}. */ @@ -90,13 +105,24 @@ public ImmutableThingChange(final ThingId thingId, @Nullable final Thing thing, final long revision, @Nullable final Instant timestamp, - @Nullable final JsonObject extra) { + @Nullable final JsonObject extra, + final DittoHeaders dittoHeaders, + final Consumer acknowledgementPublisher) { - this(thingId, changeAction, thing, JsonFactory.emptyPointer(), revision, timestamp, extra); + this(thingId, changeAction, thing, JsonFactory.emptyPointer(), revision, timestamp, extra, dittoHeaders, + acknowledgementPublisher); } - private ImmutableThingChange(final Change delegationTarget, @Nullable final Thing thing) { - change = delegationTarget; + /** + * Constructs a new {@code ImmutableThingChange} object. + * + * @param change the change to use for delegation. + * @param thing the additional {@code Thing} this ThingChange is aware of. + * @throws NullPointerException if {@code change} is {@code null}. + * @since 1.1.0 + */ + public ImmutableThingChange(final Change change, @Nullable final Thing thing) { + this.change = checkNotNull(change, "change"); this.thing = thing; } @@ -105,6 +131,11 @@ public EntityId getEntityId() { return change.getEntityId(); } + @Override + public EntityType getEntityType() { + return change.getEntityType(); + } + @Override public ChangeAction getAction() { return change.getAction(); @@ -145,13 +176,40 @@ public ImmutableThingChange withExtra(@Nullable final JsonObject extra) { return new ImmutableThingChange(change.withExtra(extra), thing); } + @Override + public DittoHeaders getDittoHeaders() { + return change.getDittoHeaders(); + } + + @Override + public Change setDittoHeaders(final DittoHeaders dittoHeaders) { + return new ImmutableThingChange(change.setDittoHeaders(dittoHeaders), thing); + } + + @Override + public Change withPathAndValue(final JsonPointer path, @Nullable final JsonValue value) { + return new ImmutableThingChange(change.withPathAndValue(path, value), thing); + } + + @Override + public void handleAcknowledgementRequests( + final Consumer> acknowledgementHandles) { + change.handleAcknowledgementRequests(acknowledgementHandles); + } + + @Override + public void handleAcknowledgementRequest(final AcknowledgementLabel acknowledgementLabel, + final Consumer acknowledgementHandle) { + change.handleAcknowledgementRequest(acknowledgementLabel, acknowledgementHandle); + } + @Override public int hashCode() { return Objects.hash(change, thing); } @Override - public boolean equals(final Object obj) { + public boolean equals(@Nullable final Object obj) { if (this == obj) { return true; } diff --git a/java/src/main/java/org/eclipse/ditto/client/configuration/AbstractAuthenticationConfiguration.java b/java/src/main/java/org/eclipse/ditto/client/configuration/AbstractAuthenticationConfiguration.java index e73e9e03..cbcac2e7 100644 --- a/java/src/main/java/org/eclipse/ditto/client/configuration/AbstractAuthenticationConfiguration.java +++ b/java/src/main/java/org/eclipse/ditto/client/configuration/AbstractAuthenticationConfiguration.java @@ -24,6 +24,8 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; +import org.eclipse.ditto.model.base.headers.DittoHeaderDefinition; + /** * Abstract implementation for common aspects of * {@link org.eclipse.ditto.client.configuration.AuthenticationConfiguration}. @@ -42,7 +44,14 @@ abstract class AbstractAuthenticationConfiguration implements AuthenticationConf checkNotNull(identifier, "identifier"); checkNotNull(additionalHeaders, "additionalHeaders"); - sessionId = identifier + ":" + UUID.randomUUID().toString(); + final String connectionCorrelationId; + if (additionalHeaders.containsKey(DittoHeaderDefinition.CORRELATION_ID.getKey())) { + connectionCorrelationId = additionalHeaders.get(DittoHeaderDefinition.CORRELATION_ID.getKey()); + } else { + connectionCorrelationId = UUID.randomUUID().toString(); + additionalHeaders.put(DittoHeaderDefinition.CORRELATION_ID.getKey(), connectionCorrelationId); + } + sessionId = identifier + ":" + connectionCorrelationId; this.additionalHeaders = Collections.unmodifiableMap(new HashMap<>(additionalHeaders)); this.proxyConfiguration = proxyConfiguration; } diff --git a/java/src/main/java/org/eclipse/ditto/client/configuration/AuthenticationConfiguration.java b/java/src/main/java/org/eclipse/ditto/client/configuration/AuthenticationConfiguration.java index 47efcfdb..51a8501f 100755 --- a/java/src/main/java/org/eclipse/ditto/client/configuration/AuthenticationConfiguration.java +++ b/java/src/main/java/org/eclipse/ditto/client/configuration/AuthenticationConfiguration.java @@ -15,6 +15,8 @@ import java.util.Map; import java.util.Optional; +import javax.annotation.Nullable; + /** * Interface for configuration provider specifying the necessary information for authenticating a client at Eclipse * Ditto. @@ -64,7 +66,7 @@ interface Builder { * @param proxyConfiguration the proxy configuration to set. * @return this builder. */ - Builder proxyConfiguration(ProxyConfiguration proxyConfiguration); + Builder proxyConfiguration(@Nullable ProxyConfiguration proxyConfiguration); /** * Build the authentication configuration. diff --git a/java/src/main/java/org/eclipse/ditto/client/configuration/ClientCredentialsAuthenticationConfiguration.java b/java/src/main/java/org/eclipse/ditto/client/configuration/ClientCredentialsAuthenticationConfiguration.java index da220d0a..1ec36218 100644 --- a/java/src/main/java/org/eclipse/ditto/client/configuration/ClientCredentialsAuthenticationConfiguration.java +++ b/java/src/main/java/org/eclipse/ditto/client/configuration/ClientCredentialsAuthenticationConfiguration.java @@ -42,16 +42,15 @@ public final class ClientCredentialsAuthenticationConfiguration extends Abstract private final List scopes; private final Duration expiryGracePeriod; - private ClientCredentialsAuthenticationConfiguration(final String tokenEndpoint, final String clientId, - final String clientSecret, final Collection scopes, final Duration expiryGracePeriod, - final Map additionalHeaders, - @Nullable final ProxyConfiguration proxyConfiguration) { - super(clientId, additionalHeaders, proxyConfiguration); - this.tokenEndpoint = checkNotNull(tokenEndpoint, "tokenEndpoint"); - this.clientId = checkNotNull(clientId, "clientId"); - this.clientSecret = checkNotNull(clientSecret, "clientSecret"); - this.scopes = Collections.unmodifiableList(new ArrayList<>(scopes)); - this.expiryGracePeriod = checkNotNull(expiryGracePeriod, "expiryGracePeriod"); + public ClientCredentialsAuthenticationConfiguration( + final ClientCredentialsAuthenticationConfigurationBuilder builder) { + + super(builder.clientId, builder.additionalHeaders, builder.proxyConfiguration); + tokenEndpoint = checkNotNull(builder.tokenEndpoint, "tokenEndpoint"); + clientId = checkNotNull(builder.clientId, "clientId"); + clientSecret = checkNotNull(builder.clientSecret, "clientSecret"); + scopes = Collections.unmodifiableList(new ArrayList<>(builder.scopes)); + expiryGracePeriod = checkNotNull(builder.expiryGracePeriod, "expiryGracePeriod"); } /** @@ -143,19 +142,25 @@ public String toString() { } @NotThreadSafe - public static class ClientCredentialsAuthenticationConfigurationBuilder + public static final class ClientCredentialsAuthenticationConfigurationBuilder implements AuthenticationConfiguration.Builder { private static final Duration DEFAULT_EXPIRY_GRACE_PERIOD = Duration.ofSeconds(5); - private final Map additionalHeaders = new HashMap<>(); - private String tokenEndpoint; private String clientId; private String clientSecret; private Collection scopes; - private Duration expiryGracePeriod = DEFAULT_EXPIRY_GRACE_PERIOD; - private ProxyConfiguration proxyConfiguration; + private Duration expiryGracePeriod; + @Nullable private ProxyConfiguration proxyConfiguration; + private final Map additionalHeaders; + + private ClientCredentialsAuthenticationConfigurationBuilder() { + scopes = Collections.emptyList(); + expiryGracePeriod = DEFAULT_EXPIRY_GRACE_PERIOD; + proxyConfiguration = null; + additionalHeaders = new HashMap<>(); + } /** * Sets the endpoint to retrieve tokens. @@ -164,18 +169,18 @@ public static class ClientCredentialsAuthenticationConfigurationBuilder * @return this builder. */ public ClientCredentialsAuthenticationConfigurationBuilder tokenEndpoint(final String tokenEndpoint) { - this.tokenEndpoint = tokenEndpoint; + this.tokenEndpoint = checkNotNull(tokenEndpoint, "tokenEndpoint"); return this; } /** - * Sets the client id to authenticate. + * Sets the client ID to authenticate. * - * @param clientId the client id. + * @param clientId the client ID. * @return this builder. */ public ClientCredentialsAuthenticationConfigurationBuilder clientId(final String clientId) { - this.clientId = clientId; + this.clientId = checkNotNull(clientId, "clientId"); return this; } @@ -187,7 +192,7 @@ public ClientCredentialsAuthenticationConfigurationBuilder clientId(final String * @return this builder. */ public ClientCredentialsAuthenticationConfigurationBuilder clientSecret(final String clientSecret) { - this.clientSecret = clientSecret; + this.clientSecret = checkNotNull(clientSecret, "clientSecret"); return this; } @@ -198,7 +203,7 @@ public ClientCredentialsAuthenticationConfigurationBuilder clientSecret(final St * @return this builder. */ public ClientCredentialsAuthenticationConfigurationBuilder scopes(final Collection scopes) { - this.scopes = scopes; + this.scopes = new ArrayList<>(checkNotNull(scopes, "scopes")); return this; } @@ -209,28 +214,29 @@ public ClientCredentialsAuthenticationConfigurationBuilder scopes(final Collecti * @return this builder. */ public ClientCredentialsAuthenticationConfigurationBuilder expiryGracePeriod(final Duration expiryGracePeriod) { - this.expiryGracePeriod = expiryGracePeriod; + this.expiryGracePeriod = checkNotNull(expiryGracePeriod, "expiryGracePeriod"); return this; } @Override public ClientCredentialsAuthenticationConfigurationBuilder withAdditionalHeader(final String key, final String value) { + additionalHeaders.put(checkNotNull(key, "key"), value); return this; } @Override public ClientCredentialsAuthenticationConfigurationBuilder proxyConfiguration( - final ProxyConfiguration proxyConfiguration) { - this.proxyConfiguration = checkNotNull(proxyConfiguration, "proxyConfiguration"); + @Nullable final ProxyConfiguration proxyConfiguration) { + + this.proxyConfiguration = proxyConfiguration; return this; } @Override public ClientCredentialsAuthenticationConfiguration build() { - return new ClientCredentialsAuthenticationConfiguration(tokenEndpoint, clientId, clientSecret, scopes, - expiryGracePeriod, additionalHeaders, proxyConfiguration); + return new ClientCredentialsAuthenticationConfiguration(this); } } diff --git a/java/src/main/java/org/eclipse/ditto/client/configuration/MessagingConfiguration.java b/java/src/main/java/org/eclipse/ditto/client/configuration/MessagingConfiguration.java index d21d1ac7..a3cffa9d 100755 --- a/java/src/main/java/org/eclipse/ditto/client/configuration/MessagingConfiguration.java +++ b/java/src/main/java/org/eclipse/ditto/client/configuration/MessagingConfiguration.java @@ -16,6 +16,8 @@ import java.time.Duration; import java.util.Optional; +import javax.annotation.Nullable; + import org.eclipse.ditto.model.base.json.JsonSchemaVersion; /** @@ -113,7 +115,7 @@ interface Builder { * @param proxyConfiguration the proxy configuration to set. * @return this builder. */ - Builder proxyConfiguration(ProxyConfiguration proxyConfiguration); + Builder proxyConfiguration(@Nullable ProxyConfiguration proxyConfiguration); /** * Sets the {@code trustStoreConfiguration}. diff --git a/java/src/main/java/org/eclipse/ditto/client/configuration/ProxyConfiguration.java b/java/src/main/java/org/eclipse/ditto/client/configuration/ProxyConfiguration.java index 6c15cee0..388a8aa0 100755 --- a/java/src/main/java/org/eclipse/ditto/client/configuration/ProxyConfiguration.java +++ b/java/src/main/java/org/eclipse/ditto/client/configuration/ProxyConfiguration.java @@ -12,7 +12,7 @@ */ package org.eclipse.ditto.client.configuration; -import static java.util.Objects.requireNonNull; +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; import java.util.Optional; @@ -30,12 +30,11 @@ public final class ProxyConfiguration { @Nullable private final String username; @Nullable private final String password; - private ProxyConfiguration(final String host, final int port, @Nullable final String username, - @Nullable final String password) { - this.host = host; - this.port = port; - this.username = username; - this.password = password; + public ProxyConfiguration(final Builder builder) { + host = checkNotNull(builder.host, "host"); + port = checkNotNull(builder.port, "port"); + username = builder.username; + password = builder.password; } /** @@ -112,7 +111,7 @@ public interface ProxyOptionalSettable extends ProxyConfigurationBuildable { * @param username the username for proxy authentication. * @return a builder object to set the password. */ - ProxyPasswordSettable proxyUsername(String username); + ProxyPasswordSettable proxyUsername(@Nullable String username); } @@ -125,7 +124,7 @@ public interface ProxyPasswordSettable extends ProxyConfigurationBuildable { * @param password the password for proxy authentication. * @return a builder object for optional proxy settings. */ - ProxyOptionalSettable proxyPassword(String password); + ProxyOptionalSettable proxyPassword(@Nullable String password); } public interface ProxyConfigurationBuildable { @@ -136,8 +135,8 @@ public interface ProxyConfigurationBuildable { ProxyConfiguration build(); } - private static final class Builder implements ProxyConfigurationBuilder, ProxyHostSettable, - ProxyPortSettable, ProxyOptionalSettable, ProxyPasswordSettable, ProxyConfigurationBuildable { + private static final class Builder implements ProxyConfigurationBuilder, ProxyHostSettable, ProxyPortSettable, + ProxyOptionalSettable, ProxyPasswordSettable, ProxyConfigurationBuildable { private String host; private int port; @@ -145,16 +144,15 @@ private static final class Builder implements ProxyConfigurationBuilder, ProxyHo private String password; private Builder() { - } - - @Override - public ProxyConfiguration build() { - return new ProxyConfiguration(host, port, username, password); + host = null; + port = 0; + username = null; + password = null; } @Override public ProxyPortSettable proxyHost(final String host) { - this.host = requireNonNull(host, "Proxy host must not be null."); + this.host = checkNotNull(host, "Proxy host must not be null."); return this; } @@ -168,16 +166,22 @@ public ProxyOptionalSettable proxyPort(final int port) { } @Override - public ProxyPasswordSettable proxyUsername(final String username) { - this.username = requireNonNull(username, "Proxy username must not be null."); + public ProxyPasswordSettable proxyUsername(@Nullable final String username) { + this.username = username; return this; } @Override - public ProxyOptionalSettable proxyPassword(final String password) { - this.password = requireNonNull(password, "Proxy password must not be null."); + public ProxyOptionalSettable proxyPassword(@Nullable final String password) { + this.password = password; return this; } + + @Override + public ProxyConfiguration build() { + return new ProxyConfiguration(this); + } + } } diff --git a/java/src/main/java/org/eclipse/ditto/client/configuration/WebSocketMessagingConfiguration.java b/java/src/main/java/org/eclipse/ditto/client/configuration/WebSocketMessagingConfiguration.java index 9f180ac8..2f186b4c 100755 --- a/java/src/main/java/org/eclipse/ditto/client/configuration/WebSocketMessagingConfiguration.java +++ b/java/src/main/java/org/eclipse/ditto/client/configuration/WebSocketMessagingConfiguration.java @@ -42,16 +42,15 @@ public final class WebSocketMessagingConfiguration implements MessagingConfigura @Nullable private final ProxyConfiguration proxyConfiguration; @Nullable private final TrustStoreConfiguration trustStoreConfiguration; - private WebSocketMessagingConfiguration(final Duration timeout, final JsonSchemaVersion jsonSchemaVersion, - final URI endpointUri, - final boolean reconnectEnabled, @Nullable final ProxyConfiguration proxyConfiguration, - @Nullable final TrustStoreConfiguration trustStoreConfiguration) { - this.timeout = timeout; - this.jsonSchemaVersion = jsonSchemaVersion; + public WebSocketMessagingConfiguration(final WebSocketMessagingConfigurationBuilder builder, + final URI endpointUri) { + + jsonSchemaVersion = builder.jsonSchemaVersion; + reconnectEnabled = builder.reconnectEnabled; + proxyConfiguration = builder.proxyConfiguration; + trustStoreConfiguration = builder.trustStoreConfiguration; + this.timeout = builder.timeout; this.endpointUri = endpointUri; - this.reconnectEnabled = reconnectEnabled; - this.proxyConfiguration = proxyConfiguration; - this.trustStoreConfiguration = trustStoreConfiguration; } public static MessagingConfiguration.Builder newBuilder() { @@ -94,13 +93,19 @@ private static final class WebSocketMessagingConfigurationBuilder implements Mes private static final String WS_PATH = "/ws/"; private static final String WS_PATH_REGEX = "/ws/([12])/?"; + private JsonSchemaVersion jsonSchemaVersion; private Duration timeout = Duration.ofSeconds(60L); - private JsonSchemaVersion jsonSchemaVersion = JsonSchemaVersion.LATEST; private URI endpointUri; - private boolean reconnectEnabled = true; - private ProxyConfiguration proxyConfiguration; + private boolean reconnectEnabled; + @Nullable private ProxyConfiguration proxyConfiguration; private TrustStoreConfiguration trustStoreConfiguration; + private WebSocketMessagingConfigurationBuilder() { + jsonSchemaVersion = JsonSchemaVersion.LATEST; + reconnectEnabled = true; + proxyConfiguration = null; + } + @Override public Builder timeout(final Duration timeout) { this.timeout = timeout; @@ -122,7 +127,7 @@ public MessagingConfiguration.Builder endpoint(final String endpoint) { return MessageFormat.format(msgTemplate, uriScheme, ALLOWED_URI_SCHEME); }); - this.endpointUri = uri; + endpointUri = uri; return this; } @@ -133,8 +138,9 @@ public MessagingConfiguration.Builder reconnectEnabled(final boolean reconnectEn } @Override - public MessagingConfiguration.Builder proxyConfiguration(final ProxyConfiguration proxyConfiguration) { - this.proxyConfiguration = checkNotNull(proxyConfiguration, "proxyConfiguration"); + public MessagingConfiguration.Builder proxyConfiguration( + @Nullable final ProxyConfiguration proxyConfiguration) { + this.proxyConfiguration = proxyConfiguration; return this; } @@ -148,8 +154,7 @@ public MessagingConfiguration.Builder trustStoreConfiguration( @Override public MessagingConfiguration build() { final URI wsEndpointUri = appendWsPathIfNecessary(this.endpointUri, jsonSchemaVersion); - return new WebSocketMessagingConfiguration(timeout, jsonSchemaVersion, wsEndpointUri, reconnectEnabled, - proxyConfiguration, trustStoreConfiguration); + return new WebSocketMessagingConfiguration(this, wsEndpointUri); } private static URI appendWsPathIfNecessary(final URI baseUri, final JsonSchemaVersion schemaVersion) { diff --git a/java/src/main/java/org/eclipse/ditto/client/internal/AbstractHandle.java b/java/src/main/java/org/eclipse/ditto/client/internal/AbstractHandle.java index 56c572bb..14c07439 100644 --- a/java/src/main/java/org/eclipse/ditto/client/internal/AbstractHandle.java +++ b/java/src/main/java/org/eclipse/ditto/client/internal/AbstractHandle.java @@ -17,26 +17,43 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletionStage; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; +import javax.annotation.Nonnull; + import org.eclipse.ditto.client.internal.bus.Classification; +import org.eclipse.ditto.client.management.AcknowledgementsFailedException; import org.eclipse.ditto.client.messaging.MessagingProvider; +import org.eclipse.ditto.json.JsonField; +import org.eclipse.ditto.json.JsonObject; +import org.eclipse.ditto.json.JsonPointer; +import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.DittoAcknowledgementLabel; +import org.eclipse.ditto.model.base.common.HttpStatusCode; import org.eclipse.ditto.model.base.headers.DittoHeaderDefinition; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.base.headers.WithDittoHeaders; +import org.eclipse.ditto.model.base.json.JsonSchemaVersion; +import org.eclipse.ditto.model.things.ThingId; import org.eclipse.ditto.protocoladapter.Adaptable; import org.eclipse.ditto.protocoladapter.DittoProtocolAdapter; import org.eclipse.ditto.protocoladapter.HeaderTranslator; import org.eclipse.ditto.protocoladapter.ProtocolAdapter; import org.eclipse.ditto.protocoladapter.ProtocolFactory; import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; +import org.eclipse.ditto.signals.acks.base.Acknowledgements; import org.eclipse.ditto.signals.base.Signal; +import org.eclipse.ditto.signals.commands.base.CommandResponse; import org.eclipse.ditto.signals.commands.base.ErrorResponse; import org.eclipse.ditto.signals.commands.policies.PolicyCommand; import org.eclipse.ditto.signals.commands.policies.PolicyCommandResponse; import org.eclipse.ditto.signals.commands.things.ThingCommand; -import org.eclipse.ditto.signals.commands.things.ThingCommandResponse; +import org.eclipse.ditto.signals.commands.things.modify.ThingModifyCommandResponse; /** * Super class of API handles including common methods for request-response handling. @@ -119,9 +136,7 @@ protected , S extends PolicyCommandResponse, R> Co final Class expectedResponse, final Function onSuccess) { return sendSignalAndExpectResponse(command, expectedResponse, onSuccess, ErrorResponse.class, - errorResponse -> { - throw errorResponse.getDittoRuntimeException(); - }); + ErrorResponse::getDittoRuntimeException); } /** @@ -137,15 +152,13 @@ protected , S extends PolicyCommandResponse, R> Co * @return future of the result if the expected response arrives or a failed future on error. * Type is {@code CompletionStage} to signify that the future will complete or fail without caller intervention. */ - protected , S extends ThingCommandResponse, R> CompletionStage askThingCommand( + protected , S extends CommandResponse, R> CompletionStage askThingCommand( final T command, final Class expectedResponse, final Function onSuccess) { final ThingCommand commandWithChannel = setChannel(command, channel); return sendSignalAndExpectResponse(commandWithChannel, expectedResponse, onSuccess, ErrorResponse.class, - errorResponse -> { - throw errorResponse.getDittoRuntimeException(); - }); + ErrorResponse::getDittoRuntimeException); } /** @@ -161,11 +174,11 @@ protected , S extends ThingCommandResponse, R> Comp * @param type of the result. * @return future of the result. */ - protected CompletionStage sendSignalAndExpectResponse(final Signal signal, + protected CompletionStage sendSignalAndExpectResponse(final Signal signal, final Class expectedResponseClass, final Function onSuccess, final Class expectedErrorResponseClass, - final Function onError) { + final Function onError) { final CompletionStage responseFuture = messagingProvider.getAdaptableBus() .subscribeOnceForAdaptable(Classification.forCorrelationId(signal), getTimeout()); @@ -173,10 +186,15 @@ protected CompletionStage sendSignalAndExpectResponse(final Signal messagingProvider.emit(signalToJsonString(signal)); return responseFuture.thenApply(responseAdaptable -> { final Signal response = signalFromAdaptable(responseAdaptable); - if (expectedResponseClass.isInstance(response)) { + if (expectedErrorResponseClass.isInstance(response)) { + // extracted runtime exception will be wrapped in CompletionException. + throw onError.apply(expectedErrorResponseClass.cast(response)); + } else if (response instanceof Acknowledgements) { + final CommandResponse commandResponse = + extractCommandResponseFromAcknowledgements(signal, (Acknowledgements) response); + return onSuccess.apply(expectedResponseClass.cast(commandResponse)); + } else if (expectedResponseClass.isInstance(response)) { return onSuccess.apply(expectedResponseClass.cast(response)); - } else if (expectedErrorResponseClass.isInstance(response)) { - return onError.apply(expectedErrorResponseClass.cast(response)); } else { throw new ClassCastException("Expect " + expectedResponseClass.getSimpleName() + ", got: " + response); } @@ -233,13 +251,86 @@ protected static Signal adjustHeadersForLiveSignal(final Signal signal) { return adjustHeadersForLive((Signal) signal); } + static CommandResponse extractCommandResponseFromAcknowledgements(final Signal signal, + final Acknowledgements acknowledgements) { + if (areFailedAcknowledgements(acknowledgements.getStatusCode())) { + throw AcknowledgementsFailedException.of(acknowledgements); + } else { + return acknowledgements.stream() + .filter(ack -> ack.getLabel().equals(DittoAcknowledgementLabel.TWIN_PERSISTED)) + .findFirst() + .map(ack -> createThingModifyCommandResponseFromAcknowledgement(signal, ack)) + .orElseThrow(() -> new IllegalStateException("Didn't receive an Acknowledgement for label '" + + DittoAcknowledgementLabel.TWIN_PERSISTED + "'. Please make sure to always request the '" + + DittoAcknowledgementLabel.TWIN_PERSISTED + "' Acknowledgement if you need to process the " + + "response in the client.")); + } + } + + private static boolean areFailedAcknowledgements(final HttpStatusCode statusCode) { + return statusCode.isClientError() || statusCode.isInternalError(); + } + + private static ThingModifyCommandResponse> + createThingModifyCommandResponseFromAcknowledgement( + final Signal signal, + final Acknowledgement ack) { + return new ThingModifyCommandResponse>() { + @Override + public JsonPointer getResourcePath() { + return signal.getResourcePath(); + } + + @Override + public String getType() { + return signal.getType().replace(".commands", ".responses"); + } + + @Nonnull + @Override + public String getManifest() { + return getType(); + } + + @Override + public ThingId getThingEntityId() { + return (ThingId) ack.getEntityId(); + } + + @Override + public DittoHeaders getDittoHeaders() { + return ack.getDittoHeaders(); + } + + @Override + public Optional getEntity(final JsonSchemaVersion schemaVersion) { + return ack.getEntity(schemaVersion); + } + + @Override + public ThingModifyCommandResponse setDittoHeaders(final DittoHeaders dittoHeaders) { + return this; + } + + @Override + public HttpStatusCode getStatusCode() { + return ack.getStatusCode(); + } + + @Override + public JsonObject toJson(final JsonSchemaVersion schemaVersion, final Predicate predicate) { + return JsonObject.empty(); + } + }; + } + private static > T adjustHeadersForLive(final T signal) { return signal.setDittoHeaders( signal.getDittoHeaders() .toBuilder() .channel(TopicPath.Channel.LIVE.getName()) .removeHeader(DittoHeaderDefinition.READ_SUBJECTS.getKey()) - .removeHeader(DittoHeaderDefinition.AUTHORIZATION_SUBJECTS.getKey()) + .removeHeader(DittoHeaderDefinition.AUTHORIZATION_CONTEXT.getKey()) .removeHeader(DittoHeaderDefinition.RESPONSE_REQUIRED.getKey()) .build() ); diff --git a/java/src/main/java/org/eclipse/ditto/client/internal/CommonManagementImpl.java b/java/src/main/java/org/eclipse/ditto/client/internal/CommonManagementImpl.java index a31d9c8d..44c6fa61 100755 --- a/java/src/main/java/org/eclipse/ditto/client/internal/CommonManagementImpl.java +++ b/java/src/main/java/org/eclipse/ditto/client/internal/CommonManagementImpl.java @@ -35,7 +35,6 @@ import org.eclipse.ditto.client.changes.FeatureChange; import org.eclipse.ditto.client.changes.FeaturesChange; import org.eclipse.ditto.client.changes.ThingChange; -import org.eclipse.ditto.client.changes.internal.ImmutableChange; import org.eclipse.ditto.client.changes.internal.ImmutableFeatureChange; import org.eclipse.ditto.client.changes.internal.ImmutableFeaturesChange; import org.eclipse.ditto.client.changes.internal.ImmutableThingChange; @@ -50,7 +49,6 @@ import org.eclipse.ditto.client.options.Option; import org.eclipse.ditto.client.options.OptionName; import org.eclipse.ditto.client.options.internal.OptionsEvaluator; -import org.eclipse.ditto.json.JsonFactory; import org.eclipse.ditto.json.JsonFieldSelector; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; @@ -67,12 +65,11 @@ import org.eclipse.ditto.protocoladapter.Adaptable; import org.eclipse.ditto.protocoladapter.TopicPath; import org.eclipse.ditto.signals.base.Signal; +import org.eclipse.ditto.signals.base.WithOptionalEntity; +import org.eclipse.ditto.signals.commands.base.CommandResponse; import org.eclipse.ditto.signals.commands.things.modify.CreateThing; -import org.eclipse.ditto.signals.commands.things.modify.CreateThingResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteThing; -import org.eclipse.ditto.signals.commands.things.modify.DeleteThingResponse; -import org.eclipse.ditto.signals.commands.things.modify.ModifyThingResponse; -import org.eclipse.ditto.signals.commands.things.modify.ThingModifyCommandResponse; +import org.eclipse.ditto.signals.commands.things.modify.ModifyThing; import org.eclipse.ditto.signals.commands.things.query.RetrieveThings; import org.eclipse.ditto.signals.commands.things.query.RetrieveThingsResponse; import org.slf4j.Logger; @@ -363,8 +360,11 @@ private CompletableFuture processCreate(final Thing thing, @Nullable fina assertThatThingHasId(thing); final CreateThing command = outgoingMessageFactory.createThing(thing, initialPolicy, options); - return askThingCommand(command, CreateThingResponse.class, response -> - response.getThingCreated().orElseGet(() -> ThingsModelFactory.newThing(JsonFactory.nullObject()))) + + return this.askThingCommand(command, + // response could be CreateThingResponse or ModifyThingResponse or Acknowledgements. + CommandResponse.class, + this::transformModifyResponse) .toCompletableFuture(); } @@ -427,12 +427,12 @@ private CompletableFuture> processPut(final Thing thing, @Nullab final Option... options) { argumentNotNull(thing); assertThatThingHasId(thing); - return askThingCommand(outgoingMessageFactory.putThing(thing, initialPolicy, options), - // response could be either CreateThingResponse or ModifyThingResponse. - ThingModifyCommandResponse.class, - response -> response.getEntity(response.getImplementedSchemaVersion()) - .map(JsonValue::asObject) - .map(ThingsModelFactory::newThing) + + final ModifyThing command = outgoingMessageFactory.putThing(thing, initialPolicy, options); + return askThingCommand(command, + // response could be CreateThingResponse or ModifyThingResponse or Acknowledgements. + CommandResponse.class, + response -> Optional.ofNullable(transformModifyResponse(response)) ).toCompletableFuture(); } @@ -441,7 +441,7 @@ public CompletableFuture update(final Thing thing, final Option... opti argumentNotNull(thing); assertThatThingHasId(thing); - return askThingCommand(outgoingMessageFactory.updateThing(thing, options), ModifyThingResponse.class, + return askThingCommand(outgoingMessageFactory.updateThing(thing, options), CommandResponse.class, this::toVoid).toCompletableFuture(); } @@ -458,7 +458,7 @@ public CompletableFuture delete(final ThingId thingId, final Option... argumentNotNull(thingId); final DeleteThing command = outgoingMessageFactory.deleteThing(thingId, options); - return askThingCommand(command, DeleteThingResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -509,9 +509,7 @@ public void registerForAttributesChanges(final String registrationId, final Cons argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/'{thingId}'/attributes"), - Change.class, handler, - (change, value, path, params) -> new ImmutableChange(change.getEntityId(), change.getAction(), path, - value, change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + Change.class, handler, (change, value, path, params) -> change.withPathAndValue(path, value) ); } @@ -523,9 +521,7 @@ public void registerForAttributeChanges(final String registrationId, final JsonP argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/'{thingId}'/attributes{0}", attrPath), Change.class, - handler, - (change, value, path, params) -> new ImmutableChange(change.getEntityId(), change.getAction(), path, - value, change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + handler, (change, value, path, params) -> change.withPathAndValue(path, value) ); } @@ -534,14 +530,12 @@ public void registerForFeatureChanges(final String registrationId, final Consume argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/'{thingId}'/features/'{featureId}'"), - FeatureChange.class, handler, - (change, value, path, params) -> { + FeatureChange.class, handler, (change, value, path, params) -> { final Feature feature = value != null ? ThingsModelFactory.newFeatureBuilder(value.asObject()) .useId(params.get("{featureId}")) .build() : null; - return new ImmutableFeatureChange(change.getEntityId(), change.getAction(), feature, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableFeatureChange(change.withPathAndValue(path, value), feature); }); } @@ -556,8 +550,7 @@ public void registerForFeatureChanges(final String registrationId, final String FeatureChange.class, handler, (change, value, path, params) -> { final Feature feature = value != null ? ThingsModelFactory.newFeatureBuilder(value.asObject()).useId(featureId).build() : null; - return new ImmutableFeatureChange(change.getEntityId(), change.getAction(), feature, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableFeatureChange(change.withPathAndValue(path, value), feature); }); } @@ -568,8 +561,7 @@ public void registerForFeaturesChanges(final String registrationId, final Consum SelectorUtil.formatJsonPointer(LOGGER, "/things/'{thingId}'/features"), FeaturesChange.class, handler, (change, value, path, params) -> { final Features features = value != null ? ThingsModelFactory.newFeatures(value.asObject()) : null; - return new ImmutableFeaturesChange(change.getEntityId(), change.getAction(), features, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableFeaturesChange(change.withPathAndValue(path, value), features); }); } @@ -581,10 +573,7 @@ public void registerForFeaturePropertyChanges(final String registrationId, final argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/'{thingId}'/features/{0}/properties", featureId), - Change.class, - handler, - (change, value, path, params) -> new ImmutableChange(change.getEntityId(), change.getAction(), path, - value, change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + Change.class, handler, (change, value, path, params) -> change.withPathAndValue(path, value) ); } @@ -600,9 +589,7 @@ public void registerForFeaturePropertyChanges(final String registrationId, SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/'{thingId}'/features/{0}/properties{1}", featureId, propertyPath), - Change.class, handler, - (change, value, path, params) -> new ImmutableChange(change.getEntityId(), change.getAction(), path, - value, change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + Change.class, handler, (change, value, path, params) -> change.withPathAndValue(path, value) ); } @@ -613,8 +600,7 @@ public void registerForThingChanges(final String registrationId, final Consumer< SelectorUtil.formatJsonPointer(LOGGER, "/things/'{thingId}'"), ThingChange.class, handler, (change, value, path, params) -> { final Thing thing = null != value ? ThingsModelFactory.newThing(value.asObject()) : null; - return new ImmutableThingChange(change.getEntityId(), change.getAction(), thing, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableThingChange(change.withPathAndValue(path, value), thing); }); } @@ -734,6 +720,19 @@ private CompletableFuture> sendRetrieveThingsMessage(final RetrieveT .toCompletableFuture(); } + @Nullable + private Thing transformModifyResponse(final CommandResponse response) { + if (response instanceof WithOptionalEntity) { + return ((WithOptionalEntity) response).getEntity(response.getImplementedSchemaVersion()) + .filter(JsonValue::isObject) + .map(JsonValue::asObject) + .map(ThingsModelFactory::newThing) + .orElse(null); + } else { + return null; + } + } + @FunctionalInterface protected interface NotifyMessage { diff --git a/java/src/main/java/org/eclipse/ditto/client/internal/DefaultDittoClient.java b/java/src/main/java/org/eclipse/ditto/client/internal/DefaultDittoClient.java index ceb1e725..43f35497 100644 --- a/java/src/main/java/org/eclipse/ditto/client/internal/DefaultDittoClient.java +++ b/java/src/main/java/org/eclipse/ditto/client/internal/DefaultDittoClient.java @@ -14,6 +14,7 @@ import java.text.MessageFormat; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import org.eclipse.ditto.client.DittoClient; import org.eclipse.ditto.client.changes.ChangeAction; @@ -34,12 +35,19 @@ import org.eclipse.ditto.client.twin.Twin; import org.eclipse.ditto.client.twin.internal.TwinImpl; import org.eclipse.ditto.json.JsonPointer; +import org.eclipse.ditto.model.base.headers.DittoHeaderDefinition; +import org.eclipse.ditto.model.base.headers.DittoHeadersBuilder; import org.eclipse.ditto.model.base.json.JsonSchemaVersion; import org.eclipse.ditto.model.messages.Message; import org.eclipse.ditto.model.messages.MessageDirection; import org.eclipse.ditto.model.things.ThingId; import org.eclipse.ditto.protocoladapter.Adaptable; +import org.eclipse.ditto.protocoladapter.DittoProtocolAdapter; +import org.eclipse.ditto.protocoladapter.HeaderTranslator; +import org.eclipse.ditto.protocoladapter.ProtocolAdapter; import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; +import org.eclipse.ditto.signals.base.Signal; import org.eclipse.ditto.signals.events.things.AclEntryCreated; import org.eclipse.ditto.signals.events.things.AclEntryDeleted; import org.eclipse.ditto.signals.events.things.AclEntryModified; @@ -226,7 +234,8 @@ private static OutgoingMessageFactory getOutgoingMessageFactoryForPolicies( private static void init(final PointerBus bus, final MessagingProvider messagingProvider) { registerKeyBasedDistributorForIncomingEvents(bus); - registerKeyBasedHandlersForIncomingEvents(bus); + registerKeyBasedHandlersForIncomingEvents(bus, messagingProvider, + DittoProtocolAdapter.of(HeaderTranslator.empty())); messagingProvider.initialize(); } @@ -255,26 +264,54 @@ private static void registerKeyBasedDistributorForIncomingEvents(final PointerBu }); } - private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus bus) { + private static > Consumer emitSignal(final MessagingProvider messagingProvider, + final ProtocolAdapter protocolAdapter, + final DittoHeaderDefinition... headersToRemove) { + + return signal -> { + final DittoHeadersBuilder headersBuilder = signal.getDittoHeaders().toBuilder(); + for (final DittoHeaderDefinition definition : headersToRemove) { + headersBuilder.removeHeader(definition.getKey()); + } + final Signal signalToEmit = signal.setDittoHeaders(headersBuilder.build()); + messagingProvider.emitAdaptable(protocolAdapter.toAdaptable(signalToEmit)); + }; + } + + private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus bus, + final MessagingProvider messagingProvider, + final ProtocolAdapter protocolAdapter) { + + final Consumer emitAcknowledgement = emitSignal( + messagingProvider, + protocolAdapter, + DittoHeaderDefinition.READ_SUBJECTS, + DittoHeaderDefinition.AUTHORIZATION_CONTEXT, + DittoHeaderDefinition.RESPONSE_REQUIRED + ); + /* * Thing */ SelectorUtil.addHandlerForThingEvent(LOGGER, bus, ThingCreated.TYPE, ThingCreated.class, e -> MessageFormat.format(THING_PATTERN, e.getThingEntityId()), (e, extra) -> new ImmutableThingChange(e.getThingEntityId(), ChangeAction.CREATED, e.getThing(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, ThingModified.TYPE, ThingModified.class, e -> MessageFormat.format(THING_PATTERN, e.getThingEntityId()), (e, extra) -> new ImmutableThingChange(e.getThingEntityId(), ChangeAction.UPDATED, e.getThing(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, ThingDeleted.TYPE, ThingDeleted.class, e -> MessageFormat.format(THING_PATTERN, e.getThingEntityId()), (e, extra) -> new ImmutableThingChange(e.getThingEntityId(), ChangeAction.DELETED, null, - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); /* @@ -286,7 +323,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.UPDATED, JsonPointer.empty(), e.getAccessControlList().toJson(e.getImplementedSchemaVersion()), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, AclEntryCreated.TYPE, AclEntryCreated.class, @@ -295,7 +333,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.CREATED, JsonPointer.empty(), e.getAclEntry().toJson(e.getImplementedSchemaVersion()), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, AclEntryModified.TYPE, AclEntryModified.class, @@ -304,7 +343,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.UPDATED, JsonPointer.empty(), e.getAclEntry().toJson(e.getImplementedSchemaVersion()), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, AclEntryDeleted.TYPE, AclEntryDeleted.class, @@ -313,7 +353,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.DELETED, JsonPointer.empty(), null, - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); /* @@ -324,7 +365,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.CREATED, JsonPointer.empty(), e.getCreatedAttributes(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, AttributesModified.TYPE, AttributesModified.class, @@ -332,7 +374,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.UPDATED, JsonPointer.empty(), e.getModifiedAttributes(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, AttributesDeleted.TYPE, AttributesDeleted.class, @@ -340,7 +383,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.DELETED, JsonPointer.empty(), null, - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); /* @@ -352,7 +396,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.CREATED, e.getAttributePointer(), e.getAttributeValue(), - e.getRevision(), e.getTimestamp().orElse(null), extra)); + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement)); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, AttributeModified.TYPE, AttributeModified.class, e -> MessageFormat.format(ATTRIBUTE_PATTERN, e.getThingEntityId(), @@ -360,7 +405,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.UPDATED, e.getAttributePointer(), e.getAttributeValue(), - e.getRevision(), e.getTimestamp().orElse(null), extra)); + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement)); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, AttributeDeleted.TYPE, AttributeDeleted.class, e -> MessageFormat.format(ATTRIBUTE_PATTERN, e.getThingEntityId(), @@ -368,7 +414,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.DELETED, e.getAttributePointer(), null, - e.getRevision(), e.getTimestamp().orElse(null), extra)); + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement)); /* * Features @@ -378,7 +425,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableFeaturesChange(e.getThingEntityId(), ChangeAction.CREATED, e.getFeatures(), JsonPointer.empty(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeaturesModified.TYPE, FeaturesModified.class, @@ -386,7 +434,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableFeaturesChange(e.getThingEntityId(), ChangeAction.UPDATED, e.getFeatures(), JsonPointer.empty(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeaturesDeleted.TYPE, FeaturesDeleted.class, @@ -394,7 +443,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableFeaturesChange(e.getThingEntityId(), ChangeAction.DELETED, null, JsonPointer.empty(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); /* @@ -405,7 +455,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableFeatureChange(e.getThingEntityId(), ChangeAction.CREATED, e.getFeature(), JsonPointer.empty(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeatureModified.TYPE, FeatureModified.class, @@ -413,7 +464,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableFeatureChange(e.getThingEntityId(), ChangeAction.UPDATED, e.getFeature(), JsonPointer.empty(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeatureDeleted.TYPE, FeatureDeleted.class, @@ -421,7 +473,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableFeatureChange(e.getThingEntityId(), ChangeAction.DELETED, null, JsonPointer.empty(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); /* @@ -434,7 +487,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.CREATED, JsonPointer.empty(), e.getProperties().toJson(e.getImplementedSchemaVersion()), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeaturePropertiesModified.TYPE, @@ -444,7 +498,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.UPDATED, JsonPointer.empty(), e.getProperties().toJson(e.getImplementedSchemaVersion()), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeaturePropertiesDeleted.TYPE, @@ -454,7 +509,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.DELETED, JsonPointer.empty(), null, - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); /* @@ -467,7 +523,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.CREATED, e.getPropertyPointer(), e.getPropertyValue(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeaturePropertyModified.TYPE, @@ -477,7 +534,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.UPDATED, e.getPropertyPointer(), e.getPropertyValue(), - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); SelectorUtil.addHandlerForThingEvent(LOGGER, bus, FeaturePropertyDeleted.TYPE, @@ -487,7 +545,8 @@ private static void registerKeyBasedHandlersForIncomingEvents(final PointerBus b (e, extra) -> new ImmutableChange(e.getThingEntityId(), ChangeAction.DELETED, e.getPropertyPointer(), null, - e.getRevision(), e.getTimestamp().orElse(null), extra) + e.getRevision(), e.getTimestamp().orElse(null), extra, e.getDittoHeaders(), + emitAcknowledgement) ); } diff --git a/java/src/main/java/org/eclipse/ditto/client/internal/OutgoingMessageFactory.java b/java/src/main/java/org/eclipse/ditto/client/internal/OutgoingMessageFactory.java index d727b17d..3ce5bcaf 100644 --- a/java/src/main/java/org/eclipse/ditto/client/internal/OutgoingMessageFactory.java +++ b/java/src/main/java/org/eclipse/ditto/client/internal/OutgoingMessageFactory.java @@ -498,10 +498,13 @@ public Message sendMessage(final MessageSerializerRegistry registry, fina } private DittoHeaders buildDittoHeaders(final boolean allowExists, final Option... options) { + final OptionsEvaluator.Global global = OptionsEvaluator.forGlobalOptions(options); final OptionsEvaluator.Modify modify = OptionsEvaluator.forModifyOptions(options); - final DittoHeadersBuilder headersBuilder = DittoHeaders.newBuilder() - .correlationId(UUID.randomUUID().toString()) + final DittoHeaders additionalHeaders = global.getDittoHeaders().orElse(DittoHeaders.empty()); + final DittoHeadersBuilder headersBuilder = DittoHeaders.newBuilder(additionalHeaders) + .correlationId(additionalHeaders.getCorrelationId() + .orElseGet(() -> UUID.randomUUID().toString())) .schemaVersion(jsonSchemaVersion) .responseRequired(modify.isResponseRequired().orElse(true)); modify.exists().ifPresent(exists -> { diff --git a/java/src/main/java/org/eclipse/ditto/client/live/internal/PendingMessageImpl.java b/java/src/main/java/org/eclipse/ditto/client/live/internal/PendingMessageImpl.java index 26dcb0a1..973bd094 100644 --- a/java/src/main/java/org/eclipse/ditto/client/live/internal/PendingMessageImpl.java +++ b/java/src/main/java/org/eclipse/ditto/client/live/internal/PendingMessageImpl.java @@ -107,37 +107,41 @@ private static void typeCheckAndConsume(final MessageResponseConsumer respons final BiConsumer uncheckedResponseConsumer = responseConsumer.getResponseConsumer(); final Class responseType = responseConsumer.getResponseType(); - // throw ClassCastException if response has incorrect type - final Message responseMessage; - if (response instanceof MessageCommand) { - responseMessage = ((MessageCommand) response).getMessage(); - } else if (response instanceof MessageCommandResponse) { - responseMessage = ((MessageCommandResponse) response).getMessage(); - } else if (response instanceof ErrorResponse) { - uncheckedResponseConsumer.accept(null, ((ErrorResponse) response).getDittoRuntimeException()); - return; - } else { - uncheckedResponseConsumer.accept(null, classCastException(responseType, response)); - return; - } + try { + // throw ClassCastException if response has incorrect type + final Message responseMessage; + if (response instanceof MessageCommand) { + responseMessage = ((MessageCommand) response).getMessage(); + } else if (response instanceof MessageCommandResponse) { + responseMessage = ((MessageCommandResponse) response).getMessage(); + } else if (response instanceof ErrorResponse) { + uncheckedResponseConsumer.accept(null, ((ErrorResponse) response).getDittoRuntimeException()); + return; + } else { + uncheckedResponseConsumer.accept(null, classCastException(responseType, response)); + return; + } - if (responseConsumer.getResponseType().isAssignableFrom(ByteBuffer.class)) { - uncheckedResponseConsumer.accept(asByteBufferMessage(responseMessage), null); - } else { - final Optional payloadOptional = responseMessage.getPayload(); - if (payloadOptional.isPresent()) { - final Object payload = payloadOptional.get(); - if (responseConsumer.getResponseType().isInstance(payload)) { - uncheckedResponseConsumer.accept(payload, null); + if (responseConsumer.getResponseType().isAssignableFrom(ByteBuffer.class)) { + uncheckedResponseConsumer.accept(asByteBufferMessage(responseMessage), null); + } else { + final Optional payloadOptional = responseMessage.getPayload(); + if (payloadOptional.isPresent()) { + final Object payload = payloadOptional.get(); + if (responseConsumer.getResponseType().isInstance(payload)) { + uncheckedResponseConsumer.accept(setMessagePayload(responseMessage, payload), null); + } else { + // response has unexpected type + uncheckedResponseConsumer.accept(setMessagePayload(responseMessage, null), + classCastException(responseType, payload)); + } } else { - // response has unexpected type - uncheckedResponseConsumer.accept(setMessagePayload(responseMessage, null), - classCastException(responseType, payload)); + // response has no payload; regard it as any message type + uncheckedResponseConsumer.accept(responseMessage, null); } - } else { - // response has no payload; regard it as any message type - uncheckedResponseConsumer.accept(responseMessage, null); } + } catch (final RuntimeException e) { + uncheckedResponseConsumer.accept(null, e); } } diff --git a/java/src/main/java/org/eclipse/ditto/client/live/messages/MessageSender.java b/java/src/main/java/org/eclipse/ditto/client/live/messages/MessageSender.java index 27d03a67..409829b6 100755 --- a/java/src/main/java/org/eclipse/ditto/client/live/messages/MessageSender.java +++ b/java/src/main/java/org/eclipse/ditto/client/live/messages/MessageSender.java @@ -19,6 +19,7 @@ import java.util.function.Consumer; import org.eclipse.ditto.model.base.common.HttpStatusCode; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.messages.Message; import org.eclipse.ditto.model.things.ThingId; @@ -152,6 +153,16 @@ interface SetPayloadOrSend extends MessageSendable { */ SetPayloadOrSend statusCode(HttpStatusCode statusCode); + /** + * Sets additional headers to send in the message. + * + * @param additionalHeaders the headers. + * @return fluent api builder that provides the functionality to set optionally fields of the message + * or send the message. + * @since 1.1.0 + */ + SetPayloadOrSend headers(DittoHeaders additionalHeaders); + /** * Sets the payload of the message. NOTE: The maximum payload size is restricted to 10MB. * diff --git a/java/src/main/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSender.java b/java/src/main/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSender.java index 324a5d51..16f303ac 100755 --- a/java/src/main/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSender.java +++ b/java/src/main/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSender.java @@ -24,6 +24,7 @@ import org.eclipse.ditto.client.live.messages.MessageSender; import org.eclipse.ditto.model.base.common.HttpStatusCode; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.messages.Message; import org.eclipse.ditto.model.messages.MessageBuilder; import org.eclipse.ditto.model.messages.MessageDirection; @@ -56,6 +57,7 @@ public final class ImmutableMessageSender implements MessageSender { private String messageCorrelationId; private String messageContentType; private HttpStatusCode messageStatusCode; + private DittoHeaders messageAdditionalHeaders; private Consumer> sendConsumer; private ImmutableMessageSender(final boolean isResponse) { @@ -69,6 +71,7 @@ private ImmutableMessageSender(final boolean isResponse) { messageTimestamp = null; messageCorrelationId = null; messageStatusCode = null; + messageAdditionalHeaders = null; } /** @@ -128,12 +131,19 @@ private void buildAndSendMessage(final T payload) { private void buildAndSendMessage(final T payload, final MessageResponseConsumer responseConsumer) { final MessageHeadersBuilder messageHeadersBuilder = - MessageHeaders.newBuilder(messageDirection, messageThingId, messageSubject) - .contentType(messageContentType) - .featureId(messageFeatureId) - .timeout(messageTimeout) - .timestamp(messageTimestamp) - .correlationId(messageCorrelationId); + MessageHeaders.newBuilder(messageDirection, messageThingId, messageSubject); + + if (null != messageAdditionalHeaders) { + // put additionalHeaders first, so that custom "contentType", "timeout", etc. still overwrites the values: + messageHeadersBuilder.putHeaders(messageAdditionalHeaders); + } + + messageHeadersBuilder + .contentType(messageContentType) + .featureId(messageFeatureId) + .timeout(messageTimeout) + .timestamp(messageTimestamp) + .correlationId(messageCorrelationId); if (responseConsumer == null) { messageHeadersBuilder.responseRequired(false); @@ -222,6 +232,12 @@ public SetPayloadOrSend statusCode(final HttpStatusCode statusCode) { return this; } + @Override + public SetPayloadOrSend headers(final DittoHeaders additionalHeaders) { + messageAdditionalHeaders = additionalHeaders; + return this; + } + @Override public SetContentType payload(final T payload) { return new SetContentTypeImpl(payload); diff --git a/java/src/main/java/org/eclipse/ditto/client/management/AcknowledgementsFailedException.java b/java/src/main/java/org/eclipse/ditto/client/management/AcknowledgementsFailedException.java new file mode 100755 index 00000000..1d1e12af --- /dev/null +++ b/java/src/main/java/org/eclipse/ditto/client/management/AcknowledgementsFailedException.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.management; + +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; + +import javax.annotation.concurrent.Immutable; + +import org.eclipse.ditto.signals.acks.base.Acknowledgements; + +/** + * This exception is thrown in the Ditto client if the requested acknowledgements failed. + * + * @since 1.1.0 + */ +@Immutable +public class AcknowledgementsFailedException extends RuntimeException { + + private static final long serialVersionUID = -4578923424099138760L; + + private static final String MESSAGE_TEMPLATE = "Requested acknowledgements failed with status code %d."; + + private final Acknowledgements acknowledgements; + + private AcknowledgementsFailedException(final Acknowledgements acknowledgements) { + super(String.format(MESSAGE_TEMPLATE, checkNotNull(acknowledgements, "acknowledgements").getStatusCodeValue())); + this.acknowledgements = acknowledgements; + } + + /** + * Create an {@code AcknowledgementsFailedException}. + * + * @param acknowledgements The failed acknowledgements. + * @return The exception. + */ + public static AcknowledgementsFailedException of(final Acknowledgements acknowledgements) { + return new AcknowledgementsFailedException(acknowledgements); + } + + /** + * Get the failed acknowledgements that caused this exception. + * + * @return the failed acknowledgements. + */ + public Acknowledgements getAcknowledgements() { + return acknowledgements; + } + +} diff --git a/java/src/main/java/org/eclipse/ditto/client/management/internal/FeatureHandleImpl.java b/java/src/main/java/org/eclipse/ditto/client/management/internal/FeatureHandleImpl.java index 916f98bb..ec64ae86 100755 --- a/java/src/main/java/org/eclipse/ditto/client/management/internal/FeatureHandleImpl.java +++ b/java/src/main/java/org/eclipse/ditto/client/management/internal/FeatureHandleImpl.java @@ -19,7 +19,6 @@ import java.util.function.Consumer; import org.eclipse.ditto.client.changes.Change; -import org.eclipse.ditto.client.changes.internal.ImmutableChange; import org.eclipse.ditto.client.internal.AbstractHandle; import org.eclipse.ditto.client.internal.HandlerRegistry; import org.eclipse.ditto.client.internal.OutgoingMessageFactory; @@ -37,20 +36,14 @@ import org.eclipse.ditto.model.things.FeatureDefinition; import org.eclipse.ditto.model.things.ThingId; import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.commands.base.CommandResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeature; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatureDefinition; -import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatureDefinitionResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatureProperties; -import org.eclipse.ditto.signals.commands.things.modify.DeleteFeaturePropertiesResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatureProperty; -import org.eclipse.ditto.signals.commands.things.modify.DeleteFeaturePropertyResponse; -import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatureResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyFeatureDefinition; -import org.eclipse.ditto.signals.commands.things.modify.ModifyFeatureDefinitionResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyFeatureProperties; -import org.eclipse.ditto.signals.commands.things.modify.ModifyFeaturePropertiesResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyFeatureProperty; -import org.eclipse.ditto.signals.commands.things.modify.ModifyFeaturePropertyResponse; import org.eclipse.ditto.signals.commands.things.query.RetrieveFeature; import org.eclipse.ditto.signals.commands.things.query.RetrieveFeatureResponse; import org.slf4j.Logger; @@ -128,7 +121,7 @@ public String getFeatureId() { @Override public CompletableFuture delete(final Option... options) { final DeleteFeature command = outgoingMessageFactory.deleteFeature(thingId, featureId, options); - return askThingCommand(command, DeleteFeatureResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -151,14 +144,14 @@ public CompletableFuture setDefinition(final FeatureDefinition featureDefi final Option... options) { final ModifyFeatureDefinition command = outgoingMessageFactory.setFeatureDefinition(thingId, featureId, featureDefinition, options); - return askThingCommand(command, ModifyFeatureDefinitionResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override public CompletableFuture deleteDefinition(final Option... options) { final DeleteFeatureDefinition command = outgoingMessageFactory.deleteFeatureDefinition(thingId, featureId, options); - return askThingCommand(command, DeleteFeatureDefinitionResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -198,28 +191,28 @@ public CompletableFuture putProperty(final JsonPointer path, final JsonVal final ModifyFeatureProperty command = outgoingMessageFactory.setFeatureProperty(thingId, featureId, path, value, options); - return askThingCommand(command, ModifyFeaturePropertyResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override public CompletableFuture setProperties(final JsonObject value, final Option... options) { final ModifyFeatureProperties command = outgoingMessageFactory.setFeatureProperties(thingId, featureId, value, options); - return askThingCommand(command, ModifyFeaturePropertiesResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override public CompletableFuture deleteProperty(final JsonPointer path, final Option... options) { final DeleteFeatureProperty command = outgoingMessageFactory.deleteFeatureProperty(thingId, featureId, path, options); - return askThingCommand(command, DeleteFeaturePropertyResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override public CompletableFuture deleteProperties(final Option... options) { final DeleteFeatureProperties command = outgoingMessageFactory.deleteFeatureProperties(thingId, featureId, options); - return askThingCommand(command, DeleteFeaturePropertiesResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -227,9 +220,7 @@ public void registerForPropertyChanges(final String registrationId, final Consum argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/{0}/features/{1}/properties", thingId, featureId), - Change.class, handler, (change, value, path, params) -> - new ImmutableChange(change.getEntityId(), change.getAction(), path, value, change.getRevision(), - change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + Change.class, handler, (change, value, path, params) -> change.withPathAndValue(path, value) ); } @@ -240,9 +231,8 @@ public void registerForPropertyChanges(final String registrationId, final JsonPo argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/{0}/features/{1}/properties{2}", thingId, featureId, - propertyPath), Change.class, handler, - (change, value, path, params) -> new ImmutableChange(change.getEntityId(), change.getAction(), path, - value, change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + propertyPath), Change.class, handler, (change, value, path, params) -> + change.withPathAndValue(path, value) ); } diff --git a/java/src/main/java/org/eclipse/ditto/client/management/internal/ThingHandleImpl.java b/java/src/main/java/org/eclipse/ditto/client/management/internal/ThingHandleImpl.java index 18460134..53085858 100755 --- a/java/src/main/java/org/eclipse/ditto/client/management/internal/ThingHandleImpl.java +++ b/java/src/main/java/org/eclipse/ditto/client/management/internal/ThingHandleImpl.java @@ -22,7 +22,6 @@ import org.eclipse.ditto.client.changes.FeatureChange; import org.eclipse.ditto.client.changes.FeaturesChange; import org.eclipse.ditto.client.changes.ThingChange; -import org.eclipse.ditto.client.changes.internal.ImmutableChange; import org.eclipse.ditto.client.changes.internal.ImmutableFeatureChange; import org.eclipse.ditto.client.changes.internal.ImmutableFeaturesChange; import org.eclipse.ditto.client.changes.internal.ImmutableThingChange; @@ -46,26 +45,17 @@ import org.eclipse.ditto.model.things.ThingId; import org.eclipse.ditto.model.things.ThingsModelFactory; import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.commands.base.CommandResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteAttribute; -import org.eclipse.ditto.signals.commands.things.modify.DeleteAttributeResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteAttributes; -import org.eclipse.ditto.signals.commands.things.modify.DeleteAttributesResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeature; -import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatureResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatures; -import org.eclipse.ditto.signals.commands.things.modify.DeleteFeaturesResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteThing; -import org.eclipse.ditto.signals.commands.things.modify.DeleteThingResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyAttribute; -import org.eclipse.ditto.signals.commands.things.modify.ModifyAttributeResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyAttributes; -import org.eclipse.ditto.signals.commands.things.modify.ModifyAttributesResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyFeature; -import org.eclipse.ditto.signals.commands.things.modify.ModifyFeatureResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyFeatures; -import org.eclipse.ditto.signals.commands.things.modify.ModifyFeaturesResponse; import org.eclipse.ditto.signals.commands.things.modify.ModifyPolicyId; -import org.eclipse.ditto.signals.commands.things.modify.ModifyPolicyIdResponse; import org.eclipse.ditto.signals.commands.things.query.RetrieveThing; import org.eclipse.ditto.signals.commands.things.query.RetrieveThingResponse; import org.slf4j.Logger; @@ -153,7 +143,7 @@ public F forFeature(final String featureId) { @Override public CompletableFuture delete(final Option[] options) { final DeleteThing command = outgoingMessageFactory.deleteThing(thingId, options); - return askThingCommand(command, DeleteThingResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -187,7 +177,7 @@ public CompletableFuture putAttribute(final JsonPointer path, final JsonVa "If you want to update the whole attributes object, please use the setAttributes(JsonObject) method."); final ModifyAttribute command = outgoingMessageFactory.setAttribute(thingId, path, value, options); - return askThingCommand(command, ModifyAttributeResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -197,7 +187,7 @@ public CompletableFuture setAttributes(final JsonObject attributes, final () -> "The root attributes entry can only be a JSON" + " object or JSON NULL literal!"); final ModifyAttributes command = outgoingMessageFactory.setAttributes(thingId, attributes, options); - return askThingCommand(command, ModifyAttributesResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -205,7 +195,7 @@ public CompletableFuture setFeatures(final Features features, final Option argumentNotNull(features); final ModifyFeatures command = outgoingMessageFactory.setFeatures(thingId, features, options); - return askThingCommand(command, ModifyFeaturesResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -213,7 +203,7 @@ public CompletableFuture setPolicyId(final PolicyId policyId, final Option argumentNotNull(policyId); final ModifyPolicyId command = outgoingMessageFactory.setPolicyId(thingId, policyId, options); - return askThingCommand(command, ModifyPolicyIdResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -221,7 +211,7 @@ public CompletableFuture putFeature(final Feature feature, final Option argumentNotNull(feature); final ModifyFeature command = outgoingMessageFactory.setFeature(thingId, feature, options); - return askThingCommand(command, ModifyFeatureResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -229,16 +219,15 @@ public CompletableFuture deleteFeature(final String featureId, final Optio argumentNotNull(featureId); final DeleteFeature command = outgoingMessageFactory.deleteFeature(thingId, featureId, options); - return askThingCommand(command, DeleteFeatureResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override public CompletableFuture deleteFeatures(final Option... options) { final DeleteFeatures command = outgoingMessageFactory.deleteFeatures(thingId, options); - return askThingCommand(command, DeleteFeaturesResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } - @Override public CompletableFuture putAttribute(final JsonPointer path, final String value, final Option... options) { @@ -251,13 +240,13 @@ public CompletableFuture deleteAttribute(final JsonPointer path, final Opt checkArgument(path, p -> !p.isEmpty(), () -> "The root attributes object cannot be deleted!"); final DeleteAttribute command = outgoingMessageFactory.deleteAttribute(thingId, path, options); - return askThingCommand(command, DeleteAttributeResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override public CompletableFuture deleteAttributes(final Option... options) { final DeleteAttributes command = outgoingMessageFactory.deleteAttributes(thingId, options); - return askThingCommand(command, DeleteAttributesResponse.class, this::toVoid).toCompletableFuture(); + return askThingCommand(command, CommandResponse.class, this::toVoid).toCompletableFuture(); } @Override @@ -270,9 +259,7 @@ public void registerForAttributesChanges(final String registrationId, final Cons argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/{0}/attributes", thingId), - Change.class, handler, (change, value, path, params) -> - new ImmutableChange(change.getEntityId(), change.getAction(), path, value, change.getRevision(), - change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + Change.class, handler, (change, value, path, params) -> change.withPathAndValue(path, value) ); } @@ -283,9 +270,7 @@ public void registerForAttributeChanges(final String registrationId, final JsonP argumentNotNull(handler); SelectorUtil.registerForChanges(handlerRegistry, registrationId, SelectorUtil.formatJsonPointer(LOGGER, "/things/{0}/attributes{1}", thingId, attrPath), - Change.class, handler, (change, value, path, params) -> - new ImmutableChange(change.getEntityId(), change.getAction(), path, value, change.getRevision(), - change.getTimestamp().orElse(null), change.getExtra().orElse(null)) + Change.class, handler, (change, value, path, params) -> change.withPathAndValue(path, value) ); } @@ -299,8 +284,7 @@ public void registerForFeatureChanges(final String registrationId, final Consume ThingsModelFactory.newFeatureBuilder(value.asObject()) .useId(params.get("{featureId}")) .build() : null; - return new ImmutableFeatureChange(change.getEntityId(), change.getAction(), feature, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableFeatureChange(change.withPathAndValue(path, value), feature); }); } @@ -314,8 +298,7 @@ public void registerForFeatureChanges(final String registrationId, final String FeatureChange.class, handler, (change, value, path, params) -> { final Feature feature = value != null ? ThingsModelFactory.newFeatureBuilder(value.asObject()).useId(featureId).build() : null; - return new ImmutableFeatureChange(change.getEntityId(), change.getAction(), feature, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableFeatureChange(change.withPathAndValue(path, value), feature); }); } @@ -326,8 +309,7 @@ public void registerForFeaturesChanges(final String registrationId, final Consum SelectorUtil.formatJsonPointer(LOGGER, "/things/{0}/features", thingId), FeaturesChange.class, handler, (change, value, path, params) -> { final Features features = value != null ? ThingsModelFactory.newFeatures(value.asObject()) : null; - return new ImmutableFeaturesChange(change.getEntityId(), change.getAction(), features, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableFeaturesChange(change.withPathAndValue(path, value), features); }); } @@ -339,8 +321,7 @@ public void registerForThingChanges(final String registrationId, final Consumer< (change, value, path, params) -> { final Thing thing = value != null ? ThingsModelFactory.newThingBuilder(value.asObject()).build() : null; - return new ImmutableThingChange(change.getEntityId(), change.getAction(), thing, path, - change.getRevision(), change.getTimestamp().orElse(null), change.getExtra().orElse(null)); + return new ImmutableThingChange(change.withPathAndValue(path, value), thing); }); } diff --git a/java/src/main/java/org/eclipse/ditto/client/messaging/MessagingProvider.java b/java/src/main/java/org/eclipse/ditto/client/messaging/MessagingProvider.java index 8fabb710..2e1d72b8 100644 --- a/java/src/main/java/org/eclipse/ditto/client/messaging/MessagingProvider.java +++ b/java/src/main/java/org/eclipse/ditto/client/messaging/MessagingProvider.java @@ -27,6 +27,7 @@ import org.eclipse.ditto.protocoladapter.Adaptable; import org.eclipse.ditto.protocoladapter.ProtocolFactory; import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.base.Signal; import org.eclipse.ditto.signals.commands.base.Command; import org.eclipse.ditto.signals.commands.base.CommandResponse; import org.eclipse.ditto.signals.events.base.Event; @@ -141,6 +142,7 @@ default CompletableFuture sendAdaptable(Adaptable adaptable) { } /** + * Send message using the underlying connection. * Throw {@code UnsupportedOperationException}. * Protocol-relevant concerns are moved away from messaging providers into API handles. * diff --git a/java/src/main/java/org/eclipse/ditto/client/messaging/internal/WebSocketMessagingProvider.java b/java/src/main/java/org/eclipse/ditto/client/messaging/internal/WebSocketMessagingProvider.java index 82f1da74..c3483613 100644 --- a/java/src/main/java/org/eclipse/ditto/client/messaging/internal/WebSocketMessagingProvider.java +++ b/java/src/main/java/org/eclipse/ditto/client/messaging/internal/WebSocketMessagingProvider.java @@ -457,7 +457,7 @@ public void onBinaryMessage(final WebSocket websocket, final byte[] binary) { @Override public void onTextMessage(final WebSocket websocket, final String text) { callbackExecutor.execute(() -> { - LOGGER.trace("Client <{}>: Received WebSocket string message <{}>", sessionId, text); + LOGGER.debug("Client <{}>: Received WebSocket string message <{}>", sessionId, text); handleIncomingMessage(text); }); } diff --git a/java/src/main/java/org/eclipse/ditto/client/options/OptionName.java b/java/src/main/java/org/eclipse/ditto/client/options/OptionName.java index bb61ff97..d88c105a 100755 --- a/java/src/main/java/org/eclipse/ditto/client/options/OptionName.java +++ b/java/src/main/java/org/eclipse/ditto/client/options/OptionName.java @@ -35,7 +35,11 @@ default boolean test(final Object o) { * @since 1.0.0 */ enum Global implements OptionName { - // currently empty + /** + * Name of the option for defining the DittoHeaders to send along with a command/message to the Ditto backend. + * @since 1.1.0 + */ + DITTO_HEADERS } /** diff --git a/java/src/main/java/org/eclipse/ditto/client/options/Options.java b/java/src/main/java/org/eclipse/ditto/client/options/Options.java index 93d4fb82..f558e264 100755 --- a/java/src/main/java/org/eclipse/ditto/client/options/Options.java +++ b/java/src/main/java/org/eclipse/ditto/client/options/Options.java @@ -15,9 +15,10 @@ import java.util.Arrays; import org.eclipse.ditto.client.management.CommonManagement; +import org.eclipse.ditto.json.JsonFieldSelector; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.policies.PolicyId; import org.eclipse.ditto.model.things.ThingId; -import org.eclipse.ditto.json.JsonFieldSelector; /** * This utility class allows to create {@link Option}s with custom values the Ditto Client is aware of. @@ -30,6 +31,21 @@ private Options() { throw new AssertionError(); } + /** + * Creates an option for specifying additional/custom DittoHeaders to send along together with any command/message + * accepting options. + *

+ * DittoHeader passed in here will be overwritten by more specific {@link Options}, when specified. + *

+ * + * @param dittoHeaders the additional DittoHeaders to send along with an operation. + * @return the new option. + * @since 1.1.0 + */ + public static Option headers(final DittoHeaders dittoHeaders) { + return DefaultOption.newInstance(OptionName.Global.DITTO_HEADERS, dittoHeaders); + } + /** * The Modify class provides static factory methods for creating Options which are related to modifying operations. * diff --git a/java/src/main/java/org/eclipse/ditto/client/options/internal/DittoHeadersOptionVisitor.java b/java/src/main/java/org/eclipse/ditto/client/options/internal/DittoHeadersOptionVisitor.java new file mode 100644 index 00000000..09133db9 --- /dev/null +++ b/java/src/main/java/org/eclipse/ditto/client/options/internal/DittoHeadersOptionVisitor.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.options.internal; + +import javax.annotation.concurrent.ThreadSafe; + +import org.eclipse.ditto.client.options.Option; +import org.eclipse.ditto.client.options.OptionName; +import org.eclipse.ditto.model.base.headers.DittoHeaders; + +/** + * This visitor fetches and provides the value as {@code DittoHeaders} for the option with name + * {@link OptionName.Global#DITTO_HEADERS} from the user provided options. + * + * @since 1.1.0 + */ +@ThreadSafe +final class DittoHeadersOptionVisitor extends AbstractOptionVisitor { + + /** + * Constructs a new {@code DittoHeadersOptionVisitor} object. + */ + DittoHeadersOptionVisitor() { + super(OptionName.Global.DITTO_HEADERS); + } + + @Override + protected DittoHeaders getValueFromOption(final Option option) { + return option.getValueAs(DittoHeaders.class); + } + +} diff --git a/java/src/main/java/org/eclipse/ditto/client/options/internal/OptionsEvaluator.java b/java/src/main/java/org/eclipse/ditto/client/options/internal/OptionsEvaluator.java index eb382132..16a520fb 100644 --- a/java/src/main/java/org/eclipse/ditto/client/options/internal/OptionsEvaluator.java +++ b/java/src/main/java/org/eclipse/ditto/client/options/internal/OptionsEvaluator.java @@ -18,6 +18,7 @@ import org.eclipse.ditto.client.options.Option; import org.eclipse.ditto.json.JsonFieldSelector; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.policies.PolicyId; import org.eclipse.ditto.model.things.ThingId; @@ -99,6 +100,15 @@ private Global() { super(); } + /** + * Returns the DittoHeaders to send along for commands/messages to the backend. + * + * @return the DittoHeaders to send along for commands/messages to the backend. + * @since 1.1.0 + */ + public Optional getDittoHeaders() { + return getValue(new DittoHeadersOptionVisitor()); + } } /** diff --git a/java/src/test/java/org/eclipse/ditto/client/ChangeUpwardsDownwardsPropagationTest.java b/java/src/test/java/org/eclipse/ditto/client/ChangeUpwardsDownwardsPropagationTest.java index be72efa6..3b461a7a 100755 --- a/java/src/test/java/org/eclipse/ditto/client/ChangeUpwardsDownwardsPropagationTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/ChangeUpwardsDownwardsPropagationTest.java @@ -267,7 +267,6 @@ public void testMultipleChangeHandlersAreInvokedOnSingleChange() throws Exceptio assertEquals(0, latch.getCount()); } - @Test public void testUpwardsRegisterForThingChangeWhenThingIsDeleted() throws Exception { // start consuming changes: diff --git a/java/src/test/java/org/eclipse/ditto/client/DittoClientAttributesTest.java b/java/src/test/java/org/eclipse/ditto/client/DittoClientAttributesTest.java index c05b33d4..6c4bc57b 100644 --- a/java/src/test/java/org/eclipse/ditto/client/DittoClientAttributesTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/DittoClientAttributesTest.java @@ -14,22 +14,28 @@ import static org.eclipse.ditto.client.TestConstants.Thing.THING_ID; import static org.eclipse.ditto.client.assertions.ClientAssertions.assertThat; +import static org.eclipse.ditto.model.base.acks.AcknowledgementRequest.parseAcknowledgementRequest; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; +import org.assertj.core.api.Assumptions; import org.eclipse.ditto.client.internal.AbstractDittoClientThingsTest; import org.eclipse.ditto.client.options.Options; import org.eclipse.ditto.json.JsonFactory; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.common.HttpStatusCode; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.messages.Message; import org.eclipse.ditto.model.messages.MessageDirection; import org.eclipse.ditto.model.messages.MessageHeaders; import org.eclipse.ditto.model.messages.MessagesModelFactory; import org.eclipse.ditto.model.things.ThingsModelFactory; +import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; import org.eclipse.ditto.signals.base.Signal; import org.eclipse.ditto.signals.commands.things.ThingErrorResponse; import org.eclipse.ditto.signals.commands.things.exceptions.ThingNotAccessibleException; @@ -239,4 +245,39 @@ public void testChangeAttributeWithEmptyPointerThrowsException() { .forId(THING_ID) .putAttribute(JsonFactory.emptyPointer(), "it should fail"); } + + @Test + public void testEventAcknowledgement() { + // Acknowledgements are not implemented for live signals yet + Assumptions.assumeThat(channel).isEqualTo(TopicPath.Channel.TWIN); + + getManagement().startConsumption(); + getManagement().registerForAttributesChanges("Attributes", change -> + change.handleAcknowledgementRequests(handles -> + handles.forEach(handle -> handle.acknowledge( + HttpStatusCode.forInt(Integer.parseInt(handle.getAcknowledgementLabel().toString())) + .orElse(HttpStatusCode.EXPECTATION_FAILED) + )) + ) + ); + // expect subscription messages + assertThat(expectMsgClass(String.class)).startsWith("START-SEND-"); + + reply(AttributeCreated.of(THING_ID, JsonPointer.of("hello"), JsonValue.of("World"), 5L, + DittoHeaders.newBuilder() + .channel(channel.name()) + .acknowledgementRequest( + parseAcknowledgementRequest("200"), + parseAcknowledgementRequest("403"), + parseAcknowledgementRequest("500") + ) + .build()) + ); + Assertions.assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()) + .isEqualTo(HttpStatusCode.OK); + Assertions.assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()) + .isEqualTo(HttpStatusCode.FORBIDDEN); + Assertions.assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()) + .isEqualTo(HttpStatusCode.INTERNAL_SERVER_ERROR); + } } diff --git a/java/src/test/java/org/eclipse/ditto/client/DittoClientFeaturesTest.java b/java/src/test/java/org/eclipse/ditto/client/DittoClientFeaturesTest.java index 07199d58..f3612ac4 100644 --- a/java/src/test/java/org/eclipse/ditto/client/DittoClientFeaturesTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/DittoClientFeaturesTest.java @@ -14,19 +14,32 @@ import static org.eclipse.ditto.client.TestConstants.Thing.THING_ID; import static org.eclipse.ditto.client.assertions.ClientAssertions.assertThat; +import static org.eclipse.ditto.model.base.acks.AcknowledgementRequest.parseAcknowledgementRequest; +import java.util.Arrays; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; +import org.assertj.core.api.Assumptions; import org.eclipse.ditto.client.internal.AbstractDittoClientThingsTest; +import org.eclipse.ditto.client.management.AcknowledgementsFailedException; import org.eclipse.ditto.client.options.Options; import org.eclipse.ditto.json.JsonFactory; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; +import org.eclipse.ditto.json.JsonValue; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; +import org.eclipse.ditto.model.base.acks.AcknowledgementRequest; +import org.eclipse.ditto.model.base.common.HttpStatusCode; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.things.Feature; import org.eclipse.ditto.model.things.FeatureDefinition; import org.eclipse.ditto.model.things.ThingsModelFactory; +import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; +import org.eclipse.ditto.signals.acks.base.Acknowledgements; import org.eclipse.ditto.signals.base.Signal; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeature; import org.eclipse.ditto.signals.commands.things.modify.DeleteFeatureDefinition; @@ -50,6 +63,7 @@ import org.eclipse.ditto.signals.commands.things.modify.ModifyFeaturesResponse; import org.eclipse.ditto.signals.commands.things.query.RetrieveFeature; import org.eclipse.ditto.signals.commands.things.query.RetrieveFeatureResponse; +import org.eclipse.ditto.signals.events.things.FeaturePropertyModified; import org.junit.Test; /** @@ -177,4 +191,102 @@ public void testDeleteFeatureProperties() { expectMsgClass(DeleteFeatureProperties.class).getDittoHeaders())); } + @Test + public void testDeleteFeatureWith2Acknowledgements() { + // skip this test for LIVE - 'twin-persisted' is obligatory + Assumptions.assumeThat(channel).isEqualTo(TopicPath.Channel.TWIN); + + final AcknowledgementLabel label1 = AcknowledgementLabel.of("custom-ack-1"); + final AcknowledgementLabel label2 = AcknowledgementLabel.of("twin-persisted"); + assertEventualCompletion(getManagement().forId(THING_ID).forFeature(FEATURE_ID) + .delete(Options.headers(DittoHeaders.newBuilder() + .acknowledgementRequest(AcknowledgementRequest.of(label1), AcknowledgementRequest.of(label2)) + .build()) + )); + + final DittoHeaders sentDittoHeaders = expectMsgClass(DeleteFeature.class).getDittoHeaders(); + reply(Acknowledgements.of( + Arrays.asList( + Acknowledgement.of(label1, THING_ID, HttpStatusCode.OK, DittoHeaders.empty()), + Acknowledgement.of(label2, THING_ID, HttpStatusCode.ACCEPTED, DittoHeaders.empty()) + ), + sentDittoHeaders + )); + + assertThat(sentDittoHeaders.getAcknowledgementRequests()) + .containsExactly(AcknowledgementRequest.of(label1), AcknowledgementRequest.of(label2)); + } + + @Test + public void testDeleteFeaturePropertiesWithFailedAcknowledgements() { + // skip this test for LIVE - 'twin-persisted' is obligatory + Assumptions.assumeThat(channel).isEqualTo(TopicPath.Channel.TWIN); + + final AcknowledgementLabel label1 = AcknowledgementLabel.of("custom-ack-1"); + final AcknowledgementLabel label2 = AcknowledgementLabel.of("twin-persisted"); + final Acknowledgements expectedAcknowledgements = Acknowledgements.of( + Arrays.asList( + Acknowledgement.of(label1, THING_ID, HttpStatusCode.FORBIDDEN, DittoHeaders.empty()), + Acknowledgement.of(label2, THING_ID, HttpStatusCode.ACCEPTED, DittoHeaders.empty()) + ), + DittoHeaders.empty() + ); + assertEventualCompletion(getManagement().forFeature(THING_ID, FEATURE_ID) + .deleteProperties(Options.headers(DittoHeaders.newBuilder() + .acknowledgementRequest( + AcknowledgementRequest.of(label1), + AcknowledgementRequest.of(label2)) + .build())) + .exceptionally(error -> { + assertThat(error).isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(AcknowledgementsFailedException.class); + final AcknowledgementsFailedException cause = (AcknowledgementsFailedException) error.getCause(); + Assertions.assertThat(cause.getAcknowledgements().setDittoHeaders(DittoHeaders.empty())) + .isEqualTo(expectedAcknowledgements); + return null; + }) + ); + + final DittoHeaders sentDittoHeaders = expectMsgClass(DeleteFeatureProperties.class).getDittoHeaders(); + reply(expectedAcknowledgements.setDittoHeaders(sentDittoHeaders)); + + assertThat(sentDittoHeaders.getAcknowledgementRequests()) + .containsExactly(AcknowledgementRequest.of(label1), AcknowledgementRequest.of(label2)); + } + + @Test + public void testEventAcknowledgement() { + // Acknowledgements are not implemented for live signals yet + Assumptions.assumeThat(channel).isEqualTo(TopicPath.Channel.TWIN); + + getManagement().startConsumption(); + getManagement().registerForFeatureChanges("Features", change -> + change.handleAcknowledgementRequests(handles -> + handles.forEach(handle -> handle.acknowledge( + HttpStatusCode.forInt(Integer.parseInt(handle.getAcknowledgementLabel().toString())) + .orElse(HttpStatusCode.EXPECTATION_FAILED) + )) + ) + ); + // expect subscription messages + assertThat(expectMsgClass(String.class)).startsWith("START-SEND-"); + + reply(FeaturePropertyModified.of(THING_ID, FEATURE_ID, JsonPointer.of("hello"), JsonValue.of("World"), 5L, + DittoHeaders.newBuilder() + .channel(channel.name()) + .acknowledgementRequest( + parseAcknowledgementRequest("409"), + parseAcknowledgementRequest("201"), + parseAcknowledgementRequest("403") + ) + .build()) + ); + Assertions.assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()) + .isEqualTo(HttpStatusCode.CONFLICT); + Assertions.assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()) + .isEqualTo(HttpStatusCode.CREATED); + Assertions.assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()) + .isEqualTo(HttpStatusCode.FORBIDDEN); + } + } diff --git a/java/src/test/java/org/eclipse/ditto/client/DittoClientPoliciesTest.java b/java/src/test/java/org/eclipse/ditto/client/DittoClientPoliciesTest.java index 58f49ea6..cd1efad8 100644 --- a/java/src/test/java/org/eclipse/ditto/client/DittoClientPoliciesTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/DittoClientPoliciesTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.function.Function; import org.assertj.core.api.Assertions; @@ -35,6 +36,8 @@ import org.eclipse.ditto.model.policies.Policy; import org.eclipse.ditto.signals.commands.policies.PolicyCommand; import org.eclipse.ditto.signals.commands.policies.PolicyCommandResponse; +import org.eclipse.ditto.signals.commands.policies.PolicyErrorResponse; +import org.eclipse.ditto.signals.commands.policies.exceptions.PolicyNotAccessibleException; import org.eclipse.ditto.signals.commands.policies.modify.CreatePolicy; import org.eclipse.ditto.signals.commands.policies.modify.CreatePolicyResponse; import org.eclipse.ditto.signals.commands.policies.modify.DeletePolicy; @@ -181,6 +184,22 @@ public void testRetrievePolicy() throws Exception { Assertions.assertThat(retrievePolicyResponse).isCompletedWithValue(POLICY); } + @Test + public void testRetrievePolicyFails() { + assertEventualCompletion(client.policies().retrieve(POLICY_ID).handle((response, error) -> { + assertThat(error) + .describedAs("Expect failure with %s, got response=%s, error=%s", + PolicyNotAccessibleException.class.getSimpleName(), response, error) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(PolicyNotAccessibleException.class); + return null; + })); + final RetrievePolicy retrievePolicy = expectMsgClass(RetrievePolicy.class); + reply(PolicyErrorResponse.of(PolicyNotAccessibleException.newBuilder(POLICY_ID) + .dittoHeaders(retrievePolicy.getDittoHeaders()) + .build())); + } + @Test(expected = JsonMissingFieldException.class) public void testCreatePolicyWithMissingId() { client.policies().create(JsonFactory.newObject()); diff --git a/java/src/test/java/org/eclipse/ditto/client/DittoClientThingTest.java b/java/src/test/java/org/eclipse/ditto/client/DittoClientThingTest.java index 15f892fc..3d1584c8 100644 --- a/java/src/test/java/org/eclipse/ditto/client/DittoClientThingTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/DittoClientThingTest.java @@ -20,20 +20,30 @@ import static org.eclipse.ditto.client.TestConstants.Thing.THING_ID_COPY_POLICY; import static org.eclipse.ditto.client.TestConstants.Thing.THING_WITH_INLINE_POLICY; import static org.eclipse.ditto.client.assertions.ClientAssertions.assertThat; +import static org.eclipse.ditto.model.base.acks.AcknowledgementRequest.parseAcknowledgementRequest; +import java.util.Arrays; +import java.util.concurrent.CompletionException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import org.assertj.core.api.Assertions; +import org.assertj.core.api.Assumptions; import org.eclipse.ditto.client.internal.AbstractDittoClientThingsTest; +import org.eclipse.ditto.client.management.AcknowledgementsFailedException; import org.eclipse.ditto.client.options.Option; import org.eclipse.ditto.client.options.Options; import org.eclipse.ditto.client.registration.DuplicateRegistrationIdException; import org.eclipse.ditto.json.JsonFactory; import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; +import org.eclipse.ditto.model.base.acks.AcknowledgementRequest; import org.eclipse.ditto.model.base.auth.AuthorizationModelFactory; import org.eclipse.ditto.model.base.auth.AuthorizationSubject; +import org.eclipse.ditto.model.base.common.HttpStatusCode; +import org.eclipse.ditto.model.base.exceptions.DittoRuntimeException; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.messages.Message; import org.eclipse.ditto.model.messages.MessageDirection; import org.eclipse.ditto.model.messages.MessageHeaders; @@ -44,6 +54,11 @@ import org.eclipse.ditto.model.things.Thing; import org.eclipse.ditto.model.things.ThingId; import org.eclipse.ditto.model.things.ThingsModelFactory; +import org.eclipse.ditto.protocoladapter.TopicPath; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; +import org.eclipse.ditto.signals.acks.base.Acknowledgements; +import org.eclipse.ditto.signals.commands.things.ThingErrorResponse; +import org.eclipse.ditto.signals.commands.things.exceptions.ThingPreconditionFailedException; import org.eclipse.ditto.signals.commands.things.modify.CreateThing; import org.eclipse.ditto.signals.commands.things.modify.CreateThingResponse; import org.eclipse.ditto.signals.commands.things.modify.DeleteThing; @@ -86,6 +101,71 @@ public void testCreateThing() { expectMsgClass(CreateThing.class).getDittoHeaders())); } + @Test + public void testCreateThingWith2Acknowledgements() { + // skip this test for LIVE - 'twin-persisted' is obligatory + Assumptions.assumeThat(channel).isEqualTo(TopicPath.Channel.TWIN); + + final AcknowledgementLabel label1 = AcknowledgementLabel.of("custom-ack-1"); + final AcknowledgementLabel label2 = AcknowledgementLabel.of("twin-persisted"); + assertEventualCompletion(getManagement() + .create(THING_ID, Options.headers(DittoHeaders.newBuilder() + .acknowledgementRequest( + AcknowledgementRequest.of(label1), + AcknowledgementRequest.of(label2)) + .build())) + ); + + final DittoHeaders sentDittoHeaders = expectMsgClass(CreateThing.class).getDittoHeaders(); + reply(Acknowledgements.of( + Arrays.asList( + Acknowledgement.of(label1, THING_ID, HttpStatusCode.OK, DittoHeaders.empty()), + Acknowledgement.of(label2, THING_ID, HttpStatusCode.ACCEPTED, DittoHeaders.empty()) + ), + sentDittoHeaders + )); + + assertThat(sentDittoHeaders.getAcknowledgementRequests()) + .containsExactly(AcknowledgementRequest.of(label1), AcknowledgementRequest.of(label2)); + } + + @Test + public void testUpdateThingWithFailedAcknowledgements() { + // skip this test for LIVE - 'twin-persisted' is obligatory + Assumptions.assumeThat(channel).isEqualTo(TopicPath.Channel.TWIN); + + final AcknowledgementLabel label1 = AcknowledgementLabel.of("custom-ack-1"); + final AcknowledgementLabel label2 = AcknowledgementLabel.of("twin-persisted"); + final Acknowledgements expectedAcknowledgements = Acknowledgements.of( + Arrays.asList( + Acknowledgement.of(label1, THING_ID, HttpStatusCode.FORBIDDEN, DittoHeaders.empty()), + Acknowledgement.of(label2, THING_ID, HttpStatusCode.ACCEPTED, DittoHeaders.empty()) + ), + DittoHeaders.empty() + ); + assertEventualCompletion(getManagement() + .update(THING, Options.headers(DittoHeaders.newBuilder() + .acknowledgementRequest( + AcknowledgementRequest.of(label1), + AcknowledgementRequest.of(label2)) + .build())) + .exceptionally(error -> { + assertThat(error).isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(AcknowledgementsFailedException.class); + final AcknowledgementsFailedException cause = (AcknowledgementsFailedException) error.getCause(); + assertThat(cause.getAcknowledgements().setDittoHeaders(DittoHeaders.empty())) + .isEqualTo(expectedAcknowledgements); + return null; + }) + ); + + final DittoHeaders sentDittoHeaders = expectMsgClass(ModifyThing.class).getDittoHeaders(); + reply(expectedAcknowledgements.setDittoHeaders(sentDittoHeaders)); + + assertThat(sentDittoHeaders.getAcknowledgementRequests()) + .containsExactly(AcknowledgementRequest.of(label1), AcknowledgementRequest.of(label2)); + } + @Test public void createThingFailsWithExistsOption() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -109,6 +189,23 @@ public void testPutThingWithExistsOptionTrue() { assertOnlyIfMatchHeader(command); } + @Test + public void testPutThingWithUnsatisfiedPrecondition() { + assertEventualCompletion(getManagement().put(THING, Options.Modify.exists(true)) + .handle((response, error) -> { + assertThat(error) + .describedAs("Expect failure with %s, got response=%s, error=%s", + ThingPreconditionFailedException.class.getSimpleName(), response, error) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(ThingPreconditionFailedException.class); + return null; + })); + final ModifyThing command = expectMsgClass(ModifyThing.class); + final DittoRuntimeException error = + ThingPreconditionFailedException.newBuilder("if-match", "\"*\"", "").build(); + reply(ThingErrorResponse.of(error, command.getDittoHeaders())); + } + @Test public void testUpdateThing() { assertEventualCompletion(getManagement().update(THING)); @@ -398,4 +495,44 @@ public void testPutThingWithAllOptionCopyPolicy() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> getManagement().put(THING, copyPolicy, copyPolicyFromThing)); } + + @Test + public void testPutThingWithoutPolicy() { + assertEventualCompletion(getManagement().put(THING) + .thenAccept(result -> assertThat(result).isEmpty()) + ); + + reply(ModifyThingResponse.modified(THING_ID, expectMsgClass(ModifyThing.class).getDittoHeaders())); + } + + @Test + public void testEventAcknowledgement() { + // Acknowledgements are not implemented for live signals yet + Assumptions.assumeThat(channel).isEqualTo(TopicPath.Channel.TWIN); + + getManagement().startConsumption(); + getManagement().registerForThingChanges("Ackermann", change -> + change.handleAcknowledgementRequests(handles -> + handles.forEach(handle -> handle.acknowledge( + HttpStatusCode.forInt(Integer.parseInt(handle.getAcknowledgementLabel().toString())) + .orElse(HttpStatusCode.EXPECTATION_FAILED) + )) + ) + ); + // expect subscription messages + assertThat(expectMsgClass(String.class)).startsWith("START-SEND-"); + + reply(ThingDeleted.of(THING_ID, 1L, DittoHeaders.newBuilder() + .channel(channel.name()) + .acknowledgementRequest( + parseAcknowledgementRequest("100"), + parseAcknowledgementRequest("301"), + parseAcknowledgementRequest("403") + ) + .build()) + ); + assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()).isEqualTo(HttpStatusCode.CONTINUE); + assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()).isEqualTo(HttpStatusCode.MOVED_PERMANENTLY); + assertThat(expectMsgClass(Acknowledgement.class).getStatusCode()).isEqualTo(HttpStatusCode.FORBIDDEN); + } } diff --git a/java/src/test/java/org/eclipse/ditto/client/TestConstants.java b/java/src/test/java/org/eclipse/ditto/client/TestConstants.java index 4ed9bb43..b1cdb2c5 100755 --- a/java/src/test/java/org/eclipse/ditto/client/TestConstants.java +++ b/java/src/test/java/org/eclipse/ditto/client/TestConstants.java @@ -20,6 +20,7 @@ import org.eclipse.ditto.model.base.auth.AuthorizationContext; import org.eclipse.ditto.model.base.auth.AuthorizationModelFactory; import org.eclipse.ditto.model.base.auth.AuthorizationSubject; +import org.eclipse.ditto.model.base.auth.DittoAuthorizationContextType; import org.eclipse.ditto.model.policies.PoliciesModelFactory; import org.eclipse.ditto.model.policies.PolicyId; import org.eclipse.ditto.model.things.AccessControlList; @@ -66,7 +67,8 @@ public static final class Authorization { * An Authorization Context which contains all known Authorization Subjects. */ public static final AuthorizationContext AUTH_CONTEXT = - AuthorizationModelFactory.newAuthContext(AUTH_SUBJECT_OLDMAN, AUTH_SUBJECT_GRIMES); + AuthorizationModelFactory.newAuthContext(DittoAuthorizationContextType.UNSPECIFIED, + AUTH_SUBJECT_OLDMAN, AUTH_SUBJECT_GRIMES); /** * The known ACL entry of John Oldman. diff --git a/java/src/test/java/org/eclipse/ditto/client/assertions/AbstractThingChangeAssert.java b/java/src/test/java/org/eclipse/ditto/client/assertions/AbstractThingChangeAssert.java index 1ec266ab..51315f2f 100644 --- a/java/src/test/java/org/eclipse/ditto/client/assertions/AbstractThingChangeAssert.java +++ b/java/src/test/java/org/eclipse/ditto/client/assertions/AbstractThingChangeAssert.java @@ -20,9 +20,7 @@ import org.eclipse.ditto.client.changes.ChangeAction; import org.eclipse.ditto.model.things.ThingId; -/** - * - */ + public abstract class AbstractThingChangeAssert, T extends Change> extends AbstractAssert { diff --git a/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableAcknowledgementRequestHandleTest.java b/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableAcknowledgementRequestHandleTest.java new file mode 100644 index 00000000..172f978f --- /dev/null +++ b/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableAcknowledgementRequestHandleTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.changes.internal; + +import static org.mutabilitydetector.unittesting.AllowedReason.provided; +import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf; +import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable; + +import java.util.function.Consumer; + +import org.eclipse.ditto.model.base.acks.AcknowledgementLabel; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.headers.DittoHeaders; +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +/** + * Unit tests for {@link ImmutableAcknowledgementRequestHandle}. + */ +public class ImmutableAcknowledgementRequestHandleTest { + + @Test + public void assertImmutability() { + assertInstancesOf(ImmutableAcknowledgementRequestHandle.class, areImmutable(), + provided(AcknowledgementLabel.class, EntityIdWithType.class, DittoHeaders.class, Consumer.class) + .isAlsoImmutable()); + } + + @Test + public void testHashCodeAndEquals() { + EqualsVerifier.forClass(ImmutableAcknowledgementRequestHandle.class) + .withIgnoredFields("acknowledgementPublisher") + .verify(); + } +} \ No newline at end of file diff --git a/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableChangeTest.java b/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableChangeTest.java index 919f962b..ca61020e 100644 --- a/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableChangeTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableChangeTest.java @@ -16,10 +16,13 @@ import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf; import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable; +import java.util.function.Consumer; + import org.eclipse.ditto.json.JsonObject; import org.eclipse.ditto.json.JsonPointer; import org.eclipse.ditto.json.JsonValue; -import org.eclipse.ditto.model.base.entity.id.EntityId; +import org.eclipse.ditto.model.base.entity.id.EntityIdWithType; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.junit.Test; import nl.jqno.equalsverifier.EqualsVerifier; @@ -29,21 +32,18 @@ */ public final class ImmutableChangeTest { - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(ImmutableChange.class, areImmutable(), - provided(JsonValue.class, JsonPointer.class, EntityId.class, JsonObject.class).isAlsoImmutable()); + provided(JsonValue.class, JsonPointer.class, EntityIdWithType.class, JsonObject.class, + DittoHeaders.class, Consumer.class).isAlsoImmutable()); } - /** - * - */ @Test public void testHashCodeAndEquals() { - EqualsVerifier.forClass(ImmutableChange.class).verify(); + EqualsVerifier.forClass(ImmutableChange.class) + .withIgnoredFields("acknowledgementPublisher") + .verify(); } } diff --git a/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChangeTest.java b/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChangeTest.java index 89f435a0..00402549 100644 --- a/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChangeTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/changes/internal/ImmutableThingChangeTest.java @@ -21,12 +21,15 @@ import java.time.Instant; import java.util.Optional; +import java.util.function.Consumer; import org.eclipse.ditto.client.changes.Change; import org.eclipse.ditto.client.changes.ChangeAction; import org.eclipse.ditto.client.changes.ThingChange; import org.eclipse.ditto.json.JsonPointer; +import org.eclipse.ditto.model.base.headers.DittoHeaders; import org.eclipse.ditto.model.things.Thing; +import org.eclipse.ditto.signals.acks.base.Acknowledgement; import org.junit.Test; import org.mockito.Mockito; @@ -42,12 +45,14 @@ public final class ImmutableThingChangeTest { private static final JsonPointer POINTER_MOCK = Mockito.mock(JsonPointer.class); private static final long KNOWN_REVISION = 34L; private static final Instant KNOWN_TIMESTAMP = Instant.now(); + private static final DittoHeaders KNOWN_DITTO_HEADERS = DittoHeaders.newBuilder().correlationId("cor-id").build(); + private static final Consumer ACK_CONSUMER = acknowledgement -> {}; @Test public void constructWithValidThingIdAndType() { final ThingChange thingChange = new ImmutableThingChange(THING_ID, KNOWN_ACTION, THING_MOCK, POINTER_MOCK, KNOWN_REVISION, - KNOWN_TIMESTAMP, null); + KNOWN_TIMESTAMP, null, KNOWN_DITTO_HEADERS, ACK_CONSUMER); assertThat(thingChange).isNotNull(); } @@ -56,7 +61,7 @@ public void constructWithValidThingIdAndType() { public void constructWithNullThingId() { assertThatNullPointerException() .isThrownBy(() -> new ImmutableThingChange(null, KNOWN_ACTION, THING_MOCK, POINTER_MOCK, KNOWN_REVISION, - KNOWN_TIMESTAMP, null)) + KNOWN_TIMESTAMP, null, KNOWN_DITTO_HEADERS, ACK_CONSUMER)) .withMessage("The %s must not be null!", "Thing ID") .withNoCause(); } @@ -66,7 +71,7 @@ public void constructWithChangeAction() { assertThatNullPointerException() .isThrownBy( () -> new ImmutableThingChange(THING_ID, null, THING_MOCK, POINTER_MOCK, KNOWN_REVISION, - KNOWN_TIMESTAMP, null)) + KNOWN_TIMESTAMP, null, KNOWN_DITTO_HEADERS, ACK_CONSUMER)) .withMessage("The %s must not be null!", "change action") .withNoCause(); } @@ -89,7 +94,7 @@ public void testHashCodeAndEquals() { public void getHashCodeForChangeWithNullThing() { final ImmutableThingChange underTest = new ImmutableThingChange(THING_ID, KNOWN_ACTION, null, POINTER_MOCK, KNOWN_REVISION, - KNOWN_TIMESTAMP, null); + KNOWN_TIMESTAMP, null, KNOWN_DITTO_HEADERS, ACK_CONSUMER); final int hashCode = underTest.hashCode(); @@ -100,7 +105,7 @@ public void getHashCodeForChangeWithNullThing() { public void gettersReturnExpected() { final ThingChange underTest = new ImmutableThingChange(THING_ID, KNOWN_ACTION, THING_MOCK, POINTER_MOCK, KNOWN_REVISION, - KNOWN_TIMESTAMP, null); + KNOWN_TIMESTAMP, null, KNOWN_DITTO_HEADERS, ACK_CONSUMER); assertThat((CharSequence) underTest.getEntityId()).isEqualTo(THING_ID); assertThat(underTest.getAction()).isSameAs(KNOWN_ACTION); @@ -111,7 +116,7 @@ public void gettersReturnExpected() { public void createEventWithThing() { final ThingChange underTest = new ImmutableThingChange(THING_ID, KNOWN_ACTION, THING_MOCK, POINTER_MOCK, KNOWN_REVISION, - KNOWN_TIMESTAMP, null); + KNOWN_TIMESTAMP, null, KNOWN_DITTO_HEADERS, ACK_CONSUMER); final Optional thing = underTest.getThing(); diff --git a/java/src/test/java/org/eclipse/ditto/client/internal/AbstractDittoClientTest.java b/java/src/test/java/org/eclipse/ditto/client/internal/AbstractDittoClientTest.java index f4a76684..60262e0d 100755 --- a/java/src/test/java/org/eclipse/ditto/client/internal/AbstractDittoClientTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/internal/AbstractDittoClientTest.java @@ -109,12 +109,16 @@ protected void expectMsg(final String msg) { protected T expectMsgClass(final Class clazz) { final String nextMessage = messaging.expectEmitted(); - final Signal signal = PROTOCOL_ADAPTER.fromAdaptable( - ProtocolFactory.jsonifiableAdaptableFromJson(JsonObject.of(nextMessage))); - if (clazz.isInstance(signal)) { - return clazz.cast(signal); + if (clazz.isAssignableFrom(String.class)) { + return clazz.cast(nextMessage); } else { - throw new AssertionError("Expect " + clazz + ", got " + signal); + final Signal signal = PROTOCOL_ADAPTER.fromAdaptable( + ProtocolFactory.jsonifiableAdaptableFromJson(JsonObject.of(nextMessage))); + if (clazz.isInstance(signal)) { + return clazz.cast(signal); + } else { + throw new AssertionError("Expect " + clazz + ", got " + signal); + } } } diff --git a/java/src/test/java/org/eclipse/ditto/client/internal/HandlerRegistryTest.java b/java/src/test/java/org/eclipse/ditto/client/internal/HandlerRegistryTest.java index 52bca3df..720f5b33 100755 --- a/java/src/test/java/org/eclipse/ditto/client/internal/HandlerRegistryTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/internal/HandlerRegistryTest.java @@ -52,9 +52,7 @@ public class HandlerRegistryTest { private Registration> registrationMock; private HandlerRegistry registry; - /** - * - */ + @Before public void before() { registry = new HandlerRegistry(busMock); @@ -62,17 +60,11 @@ public void before() { when(busMock.on(any(JsonPointerSelector.class), any(Consumer.class))).thenReturn(registrationMock); } - /** - * - */ @Test(expected = NullPointerException.class) public void constructorWithNullBus() { new HandlerRegistry(null); } - /** - * - */ @Test public void registerWithNewRegistrationId() { // test @@ -82,9 +74,6 @@ public void registerWithNewRegistrationId() { verify(busMock).on(selectorMock, consumerMock); } - /** - * - */ @Test public void registerWithAlreadyExistingRegistrationId() { // prepare @@ -103,10 +92,6 @@ public void registerWithAlreadyExistingRegistrationId() { verify(busMock, never()).on(selectorMock, consumerMock); } - - /** - * - */ @Test public void registerTwoConsumersWithDifferentRegistrationId() { // test @@ -117,9 +102,6 @@ public void registerTwoConsumersWithDifferentRegistrationId() { verify(busMock, times(2)).on(selectorMock, consumerMock); } - /** - * - */ @Test public void deregisterWithKnownRegistrationId() { // prepare @@ -133,9 +115,6 @@ public void deregisterWithKnownRegistrationId() { verify(registrationMock).cancel(); } - /** - * - */ @Test public void deregisterWithUnknownRegistrationId() { // test @@ -145,9 +124,6 @@ public void deregisterWithUnknownRegistrationId() { assertFalse(deregistered); } - /** - * - */ @Test public void deregisterAndRegisterAgain() { // prepare diff --git a/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableFeatureEventFactoryTest.java b/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableFeatureEventFactoryTest.java index 52638303..f7c142f6 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableFeatureEventFactoryTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableFeatureEventFactoryTest.java @@ -35,17 +35,12 @@ public final class ImmutableFeatureEventFactoryTest { private ImmutableFeatureEventFactory underTest = null; - /** - * - */ + @Before public void setUp() { underTest = ImmutableFeatureEventFactory.getInstance(SCHEMA_VERSION, THING_ID, FLUX_CAPACITOR_ID); } - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(ImmutableFeatureEventFactory.class, @@ -53,17 +48,11 @@ public void assertImmutability() { provided(ThingEventFactory.class).isAlsoImmutable()); } - /** - * - */ @Test public void testHashCodeAndEquals() { EqualsVerifier.forClass(ImmutableFeatureEventFactory.class).usingGetClass().verify(); } - /** - * - */ @Test public void toStringReturnsExpected() { assertThat(underTest.toString()) diff --git a/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableGlobalEventFactoryTest.java b/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableGlobalEventFactoryTest.java index cdfe0cf3..93cb493e 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableGlobalEventFactoryTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableGlobalEventFactoryTest.java @@ -169,9 +169,6 @@ public void attributeCreatedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void attributeDeletedReturnsExpected() { final AttributeDeleted attributeDeleted = underTest.attributeDeleted(THING_ID, ATTRIBUTE_JSON_POINTER); @@ -188,9 +185,6 @@ public void attributeDeletedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void attributeModifiedReturnsExpected() { final AttributeModified attributeModified = underTest.attributeModified(THING_ID, ATTRIBUTE_JSON_POINTER, @@ -208,9 +202,6 @@ public void attributeModifiedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void attributesCreatedReturnsExpected() { final AttributesCreated attributesCreated = underTest.attributesCreated(THING_ID, ATTRIBUTES); @@ -227,9 +218,6 @@ public void attributesCreatedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void attributesDeletedReturnsExpected() { final AttributesDeleted attributesDeleted = underTest.attributesDeleted(THING_ID); @@ -246,9 +234,6 @@ public void attributesDeletedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void attributesModifiedReturnsExpected() { final AttributesModified attributesModified = underTest.attributesModified(THING_ID, ATTRIBUTES); @@ -265,9 +250,6 @@ public void attributesModifiedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featureCreatedReturnsExpected() { final FeatureCreated featureCreated = underTest.featureCreated(THING_ID, FLUX_CAPACITOR); @@ -284,9 +266,6 @@ public void featureCreatedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featureDeletedReturnsExpected() { final FeatureDeleted featureDeleted = underTest.featureDeleted(THING_ID, FLUX_CAPACITOR_ID); @@ -303,9 +282,6 @@ public void featureDeletedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featureModifiedReturnsExpected() { final FeatureModified featureModified = underTest.featureModified(THING_ID, FLUX_CAPACITOR); @@ -322,9 +298,6 @@ public void featureModifiedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featuresCreatedReturnsExpected() { final FeaturesCreated featuresCreated = underTest.featuresCreated(THING_ID, FEATURES); @@ -341,9 +314,6 @@ public void featuresCreatedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featuresDeletedReturnsExpected() { final FeaturesDeleted featuresDeleted = underTest.featuresDeleted(THING_ID); @@ -360,9 +330,6 @@ public void featuresDeletedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featuresModifiedReturnsExpected() { final FeaturesModified featuresModified = underTest.featuresModified(THING_ID, FEATURES); @@ -379,9 +346,6 @@ public void featuresModifiedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featurePropertiesCreatedReturnsExpected() { final FeaturePropertiesCreated featurePropertiesCreated = @@ -399,9 +363,6 @@ public void featurePropertiesCreatedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featurePropertiesDeletedReturnsExpected() { final FeaturePropertiesDeleted featurePropertiesDeleted = @@ -419,9 +380,6 @@ public void featurePropertiesDeletedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featurePropertiesModifiedReturnsExpected() { final FeaturePropertiesModified featurePropertiesModified = @@ -439,9 +397,6 @@ public void featurePropertiesModifiedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featurePropertyCreatedReturnsExpected() { final FeaturePropertyCreated featurePropertyCreated = @@ -460,9 +415,6 @@ public void featurePropertyCreatedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featurePropertyDeletedReturnsExpected() { final FeaturePropertyDeleted featurePropertyDeleted = @@ -481,9 +433,6 @@ public void featurePropertyDeletedReturnsExpected() { .hasSchemaVersion(underTest.getSchemaVersion()); } - /** - * - */ @Test public void featurePropertyModifiedReturnsExpected() { final FeaturePropertyModified featurePropertyModified = diff --git a/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableThingEventFactoryTest.java b/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableThingEventFactoryTest.java index 1a7596cf..c569a02b 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableThingEventFactoryTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/events/internal/ImmutableThingEventFactoryTest.java @@ -35,17 +35,12 @@ public final class ImmutableThingEventFactoryTest { private ImmutableThingEventFactory underTest = null; - /** - * - */ + @Before public void setUp() { underTest = ImmutableThingEventFactory.getInstance(SCHEMA_VERSION, TestConstants.Thing.THING_ID); } - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(ImmutableThingEventFactory.class, @@ -53,17 +48,11 @@ public void assertImmutability() { provided(GlobalEventFactory.class, ThingId.class).areAlsoImmutable()); } - /** - * - */ @Test public void testHashCodeAndEquals() { EqualsVerifier.forClass(ImmutableThingEventFactory.class).usingGetClass().verify(); } - /** - * - */ @Test public void toStringReturnsExpected() { assertThat(underTest.toString()) diff --git a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerRegistryTest.java b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerRegistryTest.java index 0767f68d..6a05c5c0 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerRegistryTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerRegistryTest.java @@ -46,9 +46,6 @@ public void setupBefore() { sut = new DefaultMessageSerializerRegistry(); } - /** - * - */ @Test(expected = MessageSerializationException.class) public void registerMessageSerializerTwiceResultsInException() { final MessageSerializerKey key = ImmutableMessageSerializerKey.of(TEXT_PLAIN, String.class); @@ -63,9 +60,6 @@ public void registerMessageSerializerTwiceResultsInException() { Assert.fail("Registering a messageSerializer for the twice should have failed!"); } - /** - * - */ @Test public void registerMessageSerializerAndUnregisterToNewRegister() { final MessageSerializerKey key = ImmutableMessageSerializerKey.of(TEXT_PLAIN, String.class); @@ -81,9 +75,6 @@ public void registerMessageSerializerAndUnregisterToNewRegister() { sut.registerMessageSerializer(messageSerializer); } - /** - * - */ @Test public void registerMessageSerializerAndCheckForExistance() { final MessageSerializerKey key = ImmutableMessageSerializerKey.of(TEXT_PLAIN, String.class); @@ -104,9 +95,6 @@ public void registerMessageSerializerAndCheckForExistance() { sut.containsMessageSerializerFor(key.getJavaType(), key.getSubject())); } - /** - * - */ @Test public void findMessageSerializerWithDifferentSubjects() { final MessageSerializerKey @@ -132,9 +120,6 @@ public void findMessageSerializerWithDifferentSubjects() { sut.findSerializerFor(key2).get().getDeserializer()); } - /** - * - */ @Test public void findMessageSerializerWithDifferentSubjectsAndFallback() { final MessageSerializerKey key0 = ImmutableMessageSerializerKey.of(TEXT_PLAIN, String.class); @@ -180,9 +165,6 @@ public void findMessageSerializerWithDifferentSubjectsAndFallback() { sut.findSerializerFor("foo/bar", String.class).get().getSerializer()); } - /** - * - */ @Test public void findMessageSerializerWithTypeInheritance() { final MessageSerializerKey key0 = @@ -211,9 +193,6 @@ public void findMessageSerializerWithTypeInheritance() { sut.findSerializerFor(JsonArray.class).get().getSerializer()); } - /** - * - */ @Test public void findMessageSerializerKey() { sut.registerMessageSerializer(MessageSerializers.textPlainAsString()); @@ -234,9 +213,6 @@ public void findMessageSerializerKey() { sut.findKeyFor(ByteBuffer.class).get().getContentType()); } - /** - * - */ @Test public void findUnkownMessageSerializerKey() { sut.registerMessageSerializer(MessageSerializers.textPlainAsString()); @@ -247,9 +223,6 @@ public void findUnkownMessageSerializerKey() { sut.findKeyFor(JsonValue.class).isPresent()); } - /** - * - */ @Test public void registerMultipleSerialzersForSameContentType() { final MessageSerializerKey key0 = diff --git a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerTest.java b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerTest.java index 1c66545c..a4654c61 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/DefaultMessageSerializerTest.java @@ -21,9 +21,7 @@ */ public class DefaultMessageSerializerTest { - /** - * - */ + @Test public void testHashCodeAndEquals() { EqualsVerifier.forClass(DefaultMessageSerializer.class).verify(); diff --git a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableDeserializingMessageTest.java b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableDeserializingMessageTest.java index 62a54801..eac3db85 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableDeserializingMessageTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableDeserializingMessageTest.java @@ -27,18 +27,13 @@ */ public class ImmutableDeserializingMessageTest { - /** - * - */ + @Test public void assertImmutability() { MutabilityAssert.assertInstancesOf(ImmutableDeserializingMessage.class, areImmutable(), provided( Message.class, MessageSerializerRegistry.class).areAlsoImmutable()); } - /** - * - */ @Test public void testHashCodeAndEquals() { EqualsVerifier.forClass(ImmutableDeserializingMessage.class).verify(); diff --git a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSerializerKeyTest.java b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSerializerKeyTest.java index 88349645..af15e884 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSerializerKeyTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableMessageSerializerKeyTest.java @@ -24,17 +24,12 @@ */ public class ImmutableMessageSerializerKeyTest { - /** - * - */ + @Test public void assertImmutability() { MutabilityAssert.assertInstancesOf(ImmutableMessageSerializerKey.class, areImmutable()); } - /** - * - */ @Test public void testHashCodeAndEquals() { EqualsVerifier.forClass(ImmutableMessageSerializerKey.class).verify(); diff --git a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableRepliableMessageTest.java b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableRepliableMessageTest.java index b3bc80f2..b3b98302 100755 --- a/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableRepliableMessageTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/live/messages/internal/ImmutableRepliableMessageTest.java @@ -28,18 +28,13 @@ */ public class ImmutableRepliableMessageTest { - /** - * - */ + @Test public void assertImmutability() { MutabilityAssert.assertInstancesOf(ImmutableRepliableMessage.class, areImmutable(), provided( Message.class, Consumer.class).areAlsoImmutable()); } - /** - * - */ @Test public void testHashCodeAndEquals() { EqualsVerifier.forClass(ImmutableRepliableMessage.class).verify(); diff --git a/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionNameTest.java b/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionNameTest.java index 3fcaa4f4..7bccfdbf 100755 --- a/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionNameTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionNameTest.java @@ -21,9 +21,6 @@ */ public final class ConsumeOptionNameTest { - /** - * - */ @Test public void testAgainstValidStringValueReturnsTrue() { final String optionNameValue = OptionName.Modify.RESPONSE_REQUIRED.toString(); @@ -31,17 +28,11 @@ public void testAgainstValidStringValueReturnsTrue() { assertThat(OptionName.Modify.RESPONSE_REQUIRED.test(optionNameValue)).isTrue(); } - /** - * - */ @Test public void testAgainstInvalidStringValueReturnsFalse() { assertThat(OptionName.Modify.RESPONSE_REQUIRED.test("Fnord")).isFalse(); } - /** - * - */ @Test public void testAgainstSameOptionNameReturnsTrue() { final OptionName optionName = OptionName.Modify.RESPONSE_REQUIRED; diff --git a/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionsTest.java b/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionsTest.java index c4faf18e..007a9b2b 100755 --- a/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionsTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/ConsumeOptionsTest.java @@ -23,17 +23,11 @@ */ public final class ConsumeOptionsTest { - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(Options.Modify.class, areImmutable()); } - /** - * - */ @Test public void responseTimeoutWithDurationReturnsExpectedOption() { final boolean responseRequired = false; diff --git a/java/src/test/java/org/eclipse/ditto/client/options/DefaultOptionTest.java b/java/src/test/java/org/eclipse/ditto/client/options/DefaultOptionTest.java index 832b18b8..6554b447 100755 --- a/java/src/test/java/org/eclipse/ditto/client/options/DefaultOptionTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/DefaultOptionTest.java @@ -27,26 +27,17 @@ public final class DefaultOptionTest { private static final OptionName DEFAULT_OPTION_NAME = OptionName.Modify.RESPONSE_REQUIRED; - /** - * - */ @Test public void testHashCodeAndEquals() { EqualsVerifier.forClass(DefaultOption.class).verify(); } - /** - * - */ @Test public void tryToCreateNewInstanceWithNullName() { assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> DefaultOption.newInstance(null, 0)) .withMessage("The option name must not be null!"); } - /** - * - */ @Test public void tryToCreateNewInstanceWithNullValue() { assertThatExceptionOfType(NullPointerException.class).isThrownBy( @@ -54,9 +45,6 @@ public void tryToCreateNewInstanceWithNullValue() { .withMessage("The option value must not be null!"); } - /** - * - */ @Test public void getNameReturnsExpected() { final DefaultOption underTest = DefaultOption.newInstance(DEFAULT_OPTION_NAME, 0); @@ -64,9 +52,6 @@ public void getNameReturnsExpected() { assertThat(underTest).hasName(DEFAULT_OPTION_NAME); } - /** - * - */ @Test public void getValueReturnsExpected() { final int value = 1337; @@ -75,9 +60,6 @@ public void getValueReturnsExpected() { Assertions.assertThat(underTest.getValue()).isEqualTo(value); } - /** - * - */ @Test public void tryToGetValueAsNullType() { final int value = 1337; @@ -87,9 +69,6 @@ public void tryToGetValueAsNullType() { .withMessage("The target type to cast the value of this option to must not be null!"); } - /** - * - */ @Test public void tryToGetIntValueAsString() { final int value = 1337; @@ -99,9 +78,6 @@ public void tryToGetIntValueAsString() { assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> underTest.getValueAs(stringClass)); } - /** - * - */ @Test public void getHiddenIntegerValueAsInteger() { final Integer value = 1337; diff --git a/java/src/test/java/org/eclipse/ditto/client/options/OptionsTest.java b/java/src/test/java/org/eclipse/ditto/client/options/OptionsTest.java index ea59a056..0884984a 100755 --- a/java/src/test/java/org/eclipse/ditto/client/options/OptionsTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/OptionsTest.java @@ -22,9 +22,6 @@ */ public final class OptionsTest { - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(Options.class, areImmutable()); diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/ConsumptionOptionsEvaluatorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/ConsumptionOptionsEvaluatorTest.java new file mode 100644 index 00000000..1d45a575 --- /dev/null +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/ConsumptionOptionsEvaluatorTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.options.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mutabilitydetector.unittesting.AllowedReason.provided; +import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf; +import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable; + +import org.eclipse.ditto.client.options.Option; +import org.eclipse.ditto.client.options.Options; +import org.eclipse.ditto.json.JsonFieldSelector; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit test for {@link OptionsEvaluator.Consumption}. + */ +@RunWith(MockitoJUnitRunner.class) +public final class ConsumptionOptionsEvaluatorTest { + + private static final Option EXTRA_FIELDS = Options.Consumption.extraFields( + JsonFieldSelector.newInstance("foo/bar")); + + private OptionsEvaluator.Consumption underTest = null; + + + @Before + public void setUp() { + final Option[] options = new Option[]{EXTRA_FIELDS}; + underTest = OptionsEvaluator.forConsumptionOptions(options); + } + + @Test + public void assertImmutability() { + assertInstancesOf(OptionsEvaluator.Consumption.class, areImmutable(), + provided(OptionsEvaluator.class).isAlsoImmutable()); + } + + @Test + public void tryToCreateInstanceWithNullOptions() { + assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> OptionsEvaluator.forConsumptionOptions(null)) + .withMessage("The options must not be null!"); + } + + @Test + public void createInstanceWithEmptyOptions() { + final OptionsEvaluator.Consumption underTest = OptionsEvaluator.forConsumptionOptions(new Option[0]); + + assertThat(underTest).isNotNull(); + } + + @Test + public void getResponseTimeoutReturnsExpectedIfProvided() { + assertThat(underTest.getExtraFields()).contains(EXTRA_FIELDS.getValue()); + } + + @Test + public void getResponseTimeoutReturnsEmptyOptionalIfNotProvided() { + final Option[] options = new Option[]{}; + underTest = OptionsEvaluator.forConsumptionOptions(options); + + assertThat(underTest.getExtraFields()).isEmpty(); + } + +} diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyFromThingOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyFromThingOptionVisitorTest.java index 64a79ee1..61f7f860 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyFromThingOptionVisitorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyFromThingOptionVisitorTest.java @@ -28,17 +28,12 @@ public final class CopyPolicyFromThingOptionVisitorTest { private CopyPolicyFromThingOptionVisitor underTest = null; - /** - * - */ + @Before public void setUp() { underTest = new CopyPolicyFromThingOptionVisitor(); } - /** - * - */ @Test public void tryToVisitNullOption() { assertThatExceptionOfType(NullPointerException.class) @@ -47,9 +42,6 @@ public void tryToVisitNullOption() { .withMessageContaining("null"); } - /** - * - */ @Test public void getNoValueIfOptionNameIsUnexpected() { final String value = "Booh!"; @@ -65,9 +57,6 @@ public boolean test(final Object o) { assertThat(underTest.getValue()).isEmpty(); } - /** - * - */ @Test public void optionValueTypeDiffersFromExpectedType() { final String value = "Booh!"; @@ -78,9 +67,6 @@ public void optionValueTypeDiffersFromExpectedType() { .withCauseInstanceOf(ClassCastException.class); } - /** - * - */ @Test public void optionValueIsExpected() { final ThingId thingId = ThingId.inDefaultNamespace("thing"); diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyOptionVisitorTest.java index 354aa98d..99f6a252 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyOptionVisitorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/CopyPolicyOptionVisitorTest.java @@ -28,17 +28,12 @@ public final class CopyPolicyOptionVisitorTest { private CopyPolicyOptionVisitor underTest = null; - /** - * - */ + @Before public void setUp() { underTest = new CopyPolicyOptionVisitor(); } - /** - * - */ @Test public void tryToVisitNullOption() { assertThatExceptionOfType(NullPointerException.class) @@ -47,9 +42,6 @@ public void tryToVisitNullOption() { .withMessageContaining("null"); } - /** - * - */ @Test public void getNoValueIfOptionNameIsUnexpected() { final String value = "Booh!"; @@ -65,9 +57,6 @@ public boolean test(final Object o) { assertThat(underTest.getValue()).isEmpty(); } - /** - * - */ @Test public void optionValueTypeDiffersFromExpectedType() { final String value = "Booh!"; @@ -78,9 +67,6 @@ public void optionValueTypeDiffersFromExpectedType() { .withCauseInstanceOf(ClassCastException.class); } - /** - * - */ @Test public void optionValueIsExpected() { final PolicyId policyId = PolicyId.inNamespaceWithRandomName("org.eclipse.ditto"); diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/DittoHeadersOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/DittoHeadersOptionVisitorTest.java new file mode 100644 index 00000000..2f44cea4 --- /dev/null +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/DittoHeadersOptionVisitorTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.options.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.eclipse.ditto.client.options.internal.MockOptionFactory.createOptionMock; + +import org.eclipse.ditto.client.options.OptionName; +import org.eclipse.ditto.model.base.headers.DittoHeaders; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit test for {@link DittoHeadersOptionVisitor}. + */ +public final class DittoHeadersOptionVisitorTest { + + private DittoHeadersOptionVisitor underTest = null; + + + @Before + public void setUp() { + underTest = new DittoHeadersOptionVisitor(); + } + + @Test + public void tryToVisitNullOption() { + assertThatExceptionOfType(NullPointerException.class) + .isThrownBy(() -> underTest.visit(null)) + .withMessageContaining("option to be visited") + .withMessageContaining("null"); + } + + @Test + public void getNoValueIfOptionNameIsUnexpected() { + final String value = "Booh!"; + + final boolean isFinished = underTest.visit(createOptionMock(new OptionName() { + @Override + public boolean test(final Object o) { + return false; + } + }, value)); + + assertThat(isFinished).isFalse(); + assertThat(underTest.getValue()).isEmpty(); + } + + @Test + public void optionValueTypeDiffersFromExpectedType() { + final boolean value = false; + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> underTest.visit(createOptionMock(OptionName.Global.DITTO_HEADERS, value))) + .withMessage(String.format("The option value <%s> is not of expected type!", value)) + .withCauseInstanceOf(ClassCastException.class); + } + + @Test + public void optionValueIsExpected() { + final DittoHeaders dittoHeaders = DittoHeaders.newBuilder().correlationId("123456").build(); + + final boolean isFinished = + underTest.visit(createOptionMock(OptionName.Global.DITTO_HEADERS, dittoHeaders)); + + assertThat(isFinished).isTrue(); + assertThat(underTest.getValue()).contains(dittoHeaders); + } + +} diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/ExistsOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/ExistsOptionVisitorTest.java index 571f0e99..6123831e 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/ExistsOptionVisitorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/ExistsOptionVisitorTest.java @@ -27,17 +27,12 @@ public final class ExistsOptionVisitorTest { private ExistsOptionVisitor underTest = null; - /** - * - */ + @Before public void setUp() { underTest = new ExistsOptionVisitor(); } - /** - * - */ @Test public void tryToVisitNullOption() { assertThatExceptionOfType(NullPointerException.class) @@ -46,9 +41,6 @@ public void tryToVisitNullOption() { .withMessageContaining("null"); } - /** - * - */ @Test public void getNoValueIfOptionNameIsUnexpected() { final String value = "Booh!"; @@ -64,9 +56,6 @@ public boolean test(final Object o) { assertThat(underTest.getValue()).isEmpty(); } - /** - * - */ @Test public void optionValueTypeDiffersFromExpectedType() { final String value = "Booh!"; @@ -77,9 +66,6 @@ public void optionValueTypeDiffersFromExpectedType() { .withCauseInstanceOf(ClassCastException.class); } - /** - * - */ @Test public void optionValueIsExpected() { final boolean responseRequired = false; diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/ExtraFieldsOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/ExtraFieldsOptionVisitorTest.java index 4cf31028..6ee906e0 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/ExtraFieldsOptionVisitorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/ExtraFieldsOptionVisitorTest.java @@ -23,23 +23,17 @@ import org.junit.Test; /** - * Unit test for {@link org.eclipse.ditto.client.options.internal.ExtraFieldsOptionVisitor}. + * Unit test for {@link ExtraFieldsOptionVisitor}. */ public final class ExtraFieldsOptionVisitorTest { private ExtraFieldsOptionVisitor underTest = null; - /** - * - */ @Before public void setUp() { underTest = new ExtraFieldsOptionVisitor(); } - /** - * - */ @Test public void tryToVisitNullOption() { assertThatExceptionOfType(NullPointerException.class) @@ -48,9 +42,6 @@ public void tryToVisitNullOption() { .withMessageContaining("null"); } - /** - * - */ @Test public void getNoValueIfOptionNameIsUnexpected() { final String value = "Booh!"; @@ -66,9 +57,6 @@ public boolean test(final Object o) { assertThat(underTest.getValue()).isEmpty(); } - /** - * - */ @Test public void optionValueTypeDiffersFromExpectedType() { final boolean value = false; @@ -79,9 +67,6 @@ public void optionValueTypeDiffersFromExpectedType() { .withCauseInstanceOf(ClassCastException.class); } - /** - * - */ @Test public void optionValueIsExpected() { final JsonFieldSelector fieldSelector = JsonFactory.newFieldSelector("thingId,attributes,features/foo"); diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/FilterOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/FilterOptionVisitorTest.java index adee5881..a3f3e115 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/FilterOptionVisitorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/FilterOptionVisitorTest.java @@ -27,17 +27,12 @@ public final class FilterOptionVisitorTest { private FilterOptionVisitor underTest = null; - /** - * - */ + @Before public void setUp() { underTest = new FilterOptionVisitor(); } - /** - * - */ @Test public void tryToVisitNullOption() { assertThatExceptionOfType(NullPointerException.class) @@ -46,9 +41,6 @@ public void tryToVisitNullOption() { .withMessageContaining("null"); } - /** - * - */ @Test public void getNoValueIfOptionNameIsUnexpected() { final String value = "Booh!"; @@ -64,9 +56,6 @@ public boolean test(final Object o) { assertThat(underTest.getValue()).isEmpty(); } - /** - * - */ @Test public void optionValueTypeDiffersFromExpectedType() { final boolean value = false; @@ -77,9 +66,6 @@ public void optionValueTypeDiffersFromExpectedType() { .withCauseInstanceOf(ClassCastException.class); } - /** - * - */ @Test public void optionValueIsExpected() { final CharSequence filter = "gt(attributes/foo,42)"; diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/GlobalOptionsEvaluatorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/GlobalOptionsEvaluatorTest.java new file mode 100644 index 00000000..0fb7cee1 --- /dev/null +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/GlobalOptionsEvaluatorTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.ditto.client.options.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mutabilitydetector.unittesting.AllowedReason.provided; +import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf; +import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable; + +import java.util.UUID; + +import org.eclipse.ditto.client.options.Option; +import org.eclipse.ditto.client.options.Options; +import org.eclipse.ditto.model.base.headers.DittoHeaders; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit test for {@link OptionsEvaluator.Global}. + */ +@RunWith(MockitoJUnitRunner.class) +public final class GlobalOptionsEvaluatorTest { + + private static final DittoHeaders KNOWN_DITTO_HEADERS = DittoHeaders.newBuilder() + .correlationId(UUID.randomUUID().toString()) + .build(); + private static final Option DITTO_HEADERS_OPTION = Options.headers(KNOWN_DITTO_HEADERS); + + private OptionsEvaluator.Global underTest = null; + + + @Before + public void setUp() { + final Option[] options = new Option[]{DITTO_HEADERS_OPTION}; + underTest = OptionsEvaluator.forGlobalOptions(options); + } + + @Test + public void assertImmutability() { + assertInstancesOf(OptionsEvaluator.Global.class, areImmutable(), + provided(OptionsEvaluator.class).isAlsoImmutable()); + } + + @Test + public void tryToCreateInstanceWithNullOptions() { + assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> OptionsEvaluator.forGlobalOptions(null)) + .withMessage("The options must not be null!"); + } + + @Test + public void createInstanceWithEmptyOptions() { + final OptionsEvaluator.Global underTest = OptionsEvaluator.forGlobalOptions(new Option[0]); + + assertThat(underTest).isNotNull(); + } + + @Test + public void getResponseTimeoutReturnsExpectedIfProvided() { + assertThat(underTest.getDittoHeaders()).contains(DITTO_HEADERS_OPTION.getValue()); + } + + @Test + public void getResponseTimeoutReturnsEmptyOptionalIfNotProvided() { + final Option[] options = new Option[]{}; + underTest = OptionsEvaluator.forGlobalOptions(options); + + assertThat(underTest.getDittoHeaders()).isEmpty(); + } + +} diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/ConsumeOptionsEvaluatorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/ModifyOptionsEvaluatorTest.java similarity index 93% rename from java/src/test/java/org/eclipse/ditto/client/options/internal/ConsumeOptionsEvaluatorTest.java rename to java/src/test/java/org/eclipse/ditto/client/options/internal/ModifyOptionsEvaluatorTest.java index 1138ea0b..1a68fdf5 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/ConsumeOptionsEvaluatorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/ModifyOptionsEvaluatorTest.java @@ -29,51 +29,37 @@ * Unit test for {@link OptionsEvaluator.Modify}. */ @RunWith(MockitoJUnitRunner.class) -public final class ConsumeOptionsEvaluatorTest { +public final class ModifyOptionsEvaluatorTest { private static final Option RESPONSE_REQUIRED_OPTION = Options.Modify.responseRequired(false); private OptionsEvaluator.Modify underTest = null; - /** - * - */ + @Before public void setUp() { final Option[] options = new Option[]{RESPONSE_REQUIRED_OPTION}; underTest = OptionsEvaluator.forModifyOptions(options); } - /** - * - */ @Test public void assertThatOptionsEvaluatorIsImmutable() { assertInstancesOf(OptionsEvaluator.class, areImmutable(), provided(UserProvidedOptions.class).isAlsoImmutable()); } - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(OptionsEvaluator.Modify.class, areImmutable(), provided(OptionsEvaluator.class).isAlsoImmutable()); } - /** - * - */ @Test public void tryToCreateInstanceWithNullOptions() { assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> OptionsEvaluator.forModifyOptions(null)) .withMessage("The options must not be null!"); } - /** - * - */ @Test public void createInstanceWithEmptyOptions() { final OptionsEvaluator.Modify underTest = OptionsEvaluator.forModifyOptions(new Option[0]); @@ -81,17 +67,11 @@ public void createInstanceWithEmptyOptions() { assertThat(underTest).isNotNull(); } - /** - * - */ @Test public void getResponseTimeoutReturnsExpectedIfProvided() { assertThat(underTest.isResponseRequired()).contains(RESPONSE_REQUIRED_OPTION.getValue()); } - /** - * - */ @Test public void getResponseTimeoutReturnsEmptyOptionalIfNotProvided() { final Option[] options = new Option[]{}; diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/NamespacesOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/NamespacesOptionVisitorTest.java index c60f0d3f..6bbce8a6 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/NamespacesOptionVisitorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/NamespacesOptionVisitorTest.java @@ -29,17 +29,12 @@ public final class NamespacesOptionVisitorTest { private NamespacesOptionVisitor underTest = null; - /** - * - */ + @Before public void setUp() { underTest = new NamespacesOptionVisitor(); } - /** - * - */ @Test public void tryToVisitNullOption() { assertThatExceptionOfType(NullPointerException.class) @@ -48,9 +43,6 @@ public void tryToVisitNullOption() { .withMessageContaining("null"); } - /** - * - */ @Test public void getNoValueIfOptionNameIsUnexpected() { final String value = "Booh!"; @@ -66,9 +58,6 @@ public boolean test(final Object o) { assertThat(underTest.getValue()).isEmpty(); } - /** - * - */ @Test public void optionValueTypeDiffersFromExpectedType() { final String value = "Booh!"; @@ -79,9 +68,6 @@ public void optionValueTypeDiffersFromExpectedType() { .withCauseInstanceOf(ClassCastException.class); } - /** - * - */ @Test public void optionValueIsExpected() { final Iterable namespaces = Arrays.asList("one", "two", "three"); diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/OptionsValidatorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/OptionsValidatorTest.java index daee4627..ae912e70 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/OptionsValidatorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/OptionsValidatorTest.java @@ -29,25 +29,17 @@ public final class OptionsValidatorTest { private OptionsValidator underTest = null; - /** - * - */ + @Before public void setUp() { underTest = new OptionsValidator(); } - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(OptionsValidator.class, areImmutable()); } - /** - * - */ @Test public void allOptionsHaveDifferentNames() { final Option responseRequiredOption = createOptionMock(OptionName.Modify.RESPONSE_REQUIRED, true); @@ -56,9 +48,6 @@ public void allOptionsHaveDifferentNames() { underTest.accept(options); } - /** - * - */ @Test public void twoOptionsHaveSameName() { final Option firstOption = createOptionMock(OptionName.Modify.RESPONSE_REQUIRED, false); diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/ResponseRequiredOptionVisitorTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/ResponseRequiredOptionVisitorTest.java index 64a100ed..421f11b4 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/ResponseRequiredOptionVisitorTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/ResponseRequiredOptionVisitorTest.java @@ -27,17 +27,12 @@ public final class ResponseRequiredOptionVisitorTest { private ResponseRequiredOptionVisitor underTest = null; - /** - * - */ + @Before public void setUp() { underTest = new ResponseRequiredOptionVisitor(); } - /** - * - */ @Test public void tryToVisitNullOption() { assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> underTest.visit(null)) @@ -45,9 +40,6 @@ public void tryToVisitNullOption() { .withMessageContaining("null"); } - /** - * - */ @Test public void getNoValueIfOptionNameIsUnexpected() { final String value = "Booh!"; @@ -63,9 +55,6 @@ public boolean test(final Object o) { assertThat(underTest.getValue()).isEmpty(); } - /** - * - */ @Test public void optionValueTypeDiffersFromExpectedType() { final String value = "Booh!"; @@ -76,9 +65,6 @@ public void optionValueTypeDiffersFromExpectedType() { .withCauseInstanceOf(ClassCastException.class); } - /** - * - */ @Test public void optionValueIsExpected() { final boolean responseRequired = false; diff --git a/java/src/test/java/org/eclipse/ditto/client/options/internal/UserProvidedOptionsTest.java b/java/src/test/java/org/eclipse/ditto/client/options/internal/UserProvidedOptionsTest.java index fe7576cf..97104da3 100644 --- a/java/src/test/java/org/eclipse/ditto/client/options/internal/UserProvidedOptionsTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/options/internal/UserProvidedOptionsTest.java @@ -44,27 +44,19 @@ public final class UserProvidedOptionsTest { private Option[] options = null; private UserProvidedOptions underTest = null; - /** - * - */ + @Before public void setUp() { options = new Option[]{responseRequiredOptionMock}; underTest = UserProvidedOptions.of(options); } - /** - * - */ @Test public void assertImmutability() { assertInstancesOf(UserProvidedOptions.class, areImmutable(), provided(Option.class).isAlsoImmutable(), assumingFields("options").areSafelyCopiedUnmodifiableCollectionsWithImmutableElements()); } - /** - * - */ @Test public void tryToCreateInstanceWithNullOptionArray() { assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> UserProvidedOptions.of(null)) @@ -72,9 +64,6 @@ public void tryToCreateInstanceWithNullOptionArray() { .withMessageContaining("null"); } - /** - * - */ @Test public void tryToAcceptNullVisitor() { assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> underTest.accept(null)) @@ -82,9 +71,6 @@ public void tryToAcceptNullVisitor() { .withMessageContaining("null"); } - /** - * - */ @Test public void visitorIsProvidedWithAllOptions() { final OptionVisitor>> visitor = new OptionCollectingVisitor(); @@ -93,9 +79,6 @@ public void visitorIsProvidedWithAllOptions() { assertThat(visitor.getValue()).contains(Arrays.asList(options)); } - /** - * - */ @Test public void visitorIsFinishedAfterFirstOption() { final OptionVisitor>> visitor = new OptionCollectingVisitor(1); diff --git a/legal/3rd-party-dependencies/compile.txt b/legal/3rd-party-dependencies/compile.txt index fcd2167d..cd2f5b6b 100644 --- a/legal/3rd-party-dependencies/compile.txt +++ b/legal/3rd-party-dependencies/compile.txt @@ -1,4 +1,5 @@ - com.eclipsesource.minimal-json:minimal-json:jar:0.9.5:compile - com.neovisionaries:nv-websocket-client:jar:2.9:compile - org.atteo.classindex:classindex:jar:3.7:compile - org.slf4j:slf4j-api:jar:1.7.25:compile +com.eclipsesource.minimal-json:minimal-json:jar:0.9.5:compile +com.neovisionaries:nv-websocket-client:jar:2.9:compile +org.atteo.classindex:classindex:jar:3.8:compile +org.reactivestreams:reactive-streams:jar:1.0.3:compile +org.slf4j:slf4j-api:jar:1.7.28:compile diff --git a/legal/3rd-party-dependencies/cqs.md b/legal/3rd-party-dependencies/cqs.md index 73c44788..bcc191e6 100644 --- a/legal/3rd-party-dependencies/cqs.md +++ b/legal/3rd-party-dependencies/cqs.md @@ -6,67 +6,82 @@ |---|---|---|---| |com.eclipsesource.minimal-json|minimal-json|0.9.5| [16296](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=16296) | |com.neovisionaries|nv-websocket-client|2.9| [20409](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=20409) | -|org.atteo.classindex|classindex|3.7| [18906](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=18906) TODO: update | -|org.slf4j|slf4j-api|1.7.25| [14404](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=14404) | +|org.atteo.classindex|classindex|3.8| [20713](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=20713) | +|org.reactivestreams|reactive-streams|1.0.3| [16332](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=16332) | +|org.slf4j|slf4j-api|1.7.28| [14404](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=14404) | + +## Eclipse CQs - Runtime + +| Group ID | Artifact ID | Version | CQ | +|---|---|---|---| ## Works-With dependencies | Group ID | Artifact ID | Version | CQ | |---|---|---|---| -|com.google.code.findbugs|jsr305|3.0.1| []() | -|biz.aQute.bnd|bndlib|2.4.0| []() | -|ch.qos.logback|logback-classic|1.2.3| []() | -|ch.qos.logback|logback-core|1.2.3| []() | -|junit|junit|4.12| []() | -|net.bytebuddy|byte-buddy-agent|1.7.0| []() | -|net.bytebuddy|byte-buddy|1.7.0| []() | -|net.javacrumbs.json-unit|json-unit-core|1.28.1| []() | -|net.javacrumbs.json-unit|json-unit|1.28.1| []() | -|nl.jqno.equalsverifier|equalsverifier|3.0.3| []() | -|org.apache.felix|org.apache.felix.framework|5.4.0| []() | -|org.apache.servicemix.bundles|org.apache.servicemix.bundles.javax-inject|1_2| []() | -|org.assertj|assertj-core|3.11.1| []() | -|org.hamcrest|hamcrest-core|1.3| []() | -|org.json|json|20090211| []() | -|org.mockito|mockito-core|2.9.0| []() | -|org.mutabilitydetector|MutabilityDetector|0.9.6| []() | -|org.objenesis|objenesis|2.6| []() | -|org.ops4j.base|ops4j-base-io|1.5.0| []() | -|org.ops4j.base|ops4j-base-lang|1.5.0| []() | -|org.ops4j.base|ops4j-base-monitors|1.5.0| []() | -|org.ops4j.base|ops4j-base-net|1.5.0| []() | -|org.ops4j.base|ops4j-base-spi|1.5.0| []() | -|org.ops4j.base|ops4j-base-store|1.5.0| []() | -|org.ops4j.base|ops4j-base-util-property|1.5.0| []() | -|org.ops4j.pax.exam|pax-exam-container-native|4.8.0| []() | -|org.ops4j.pax.exam|pax-exam|4.8.0| []() | -|org.ops4j.pax.exam|pax-exam-junit4|4.8.0| []() | -|org.ops4j.pax.exam|pax-exam-link-mvn|4.8.0| []() | -|org.ops4j.pax.exam|pax-exam-spi|4.8.0| []() | -|org.ops4j.pax.swissbox|pax-swissbox-core|1.8.2| []() | -|org.ops4j.pax.swissbox|pax-swissbox-lifecycle|1.8.2| []() | -|org.ops4j.pax.swissbox|pax-swissbox-optional-jcl|1.8.2| []() | -|org.ops4j.pax.swissbox|pax-swissbox-property|1.8.2| []() | -|org.ops4j.pax.swissbox|pax-swissbox-tracker|1.8.2| []() | -|org.ops4j.pax.tinybundles|tinybundles|2.1.1| []() | -|org.ops4j.pax.url|pax-url-aether|2.4.2| []() | -|org.ops4j.pax.url|pax-url-classpath|2.4.2| []() | -|org.ops4j.pax.url|pax-url-commons|2.4.2| []() | -|org.ops4j.pax.url|pax-url-link|2.4.2| []() | -|org.skyscreamer|jsonassert|1.2.3| []() | -|org.slf4j|jcl-over-slf4j|1.6.6| []() | -|com.github.siom79.japicmp|japicmp-maven-plugin|0.11.0| []() | -|com.mycila|license-maven-plugin|3.0| []() | -|org.apache.felix|maven-bundle-plugin|3.5.0| []() | -|org.apache.maven.plugins|maven-assembly-plugin|3.1.0| []() | -|org.apache.maven.plugins|maven-clean-plugin|2.5| []() | -|org.apache.maven.plugins|maven-compiler-plugin|3.7.0| []() | -|org.apache.maven.plugins|maven-deploy-plugin|2.8.2| []() | -|org.apache.maven.plugins|maven-failsafe-plugin|3.0.0-M1| []() | -|org.apache.maven.plugins|maven-install-plugin|2.5.2| []() | -|org.apache.maven.plugins|maven-resources-plugin|3.0.2| []() | -|org.apache.maven.plugins|maven-scm-plugin|1.9.5| []() | -|org.apache.maven.plugins|maven-site-plugin|3.3| []() | -|org.apache.maven.plugins|maven-surefire-plugin|3.0.0-M1| []() | -|org.codehaus.mojo|license-maven-plugin|1.17| []() | -|org.codehaus.mojo|versions-maven-plugin|2.5| []() | +|com.google.code.findbugs|jsr305|3.0.1| [14511](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=14511) | +|biz.aQute.bnd|bndlib|2.4.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|ch.qos.logback|logback-classic|1.2.3| [16305](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=16305) | +|ch.qos.logback|logback-core|1.2.3| [16305](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=16305) | +|com.beust|jcommander|1.72| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|junit|junit|4.12| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|net.bytebuddy|byte-buddy-agent|1.9.10| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|net.bytebuddy|byte-buddy|1.9.10| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|net.javacrumbs.json-unit|json-unit-core|1.28.1| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|net.javacrumbs.json-unit|json-unit|1.28.1| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|nl.jqno.equalsverifier|equalsverifier|3.0.3| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache-extras.beanshell|bsh|2.0b6| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.apache.felix|org.apache.felix.framework|6.0.3| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.apache.servicemix.bundles|org.apache.servicemix.bundles.javax-inject|1_2| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.assertj|assertj-core|3.12.0| [19841](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=19841) | +|org.codehaus.mojo|animal-sniffer-annotations|1.9| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.hamcrest|hamcrest-core|1.3| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.json|json|20090211| [19841](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=19841) | +|org.mockito|mockito-core|3.1.0| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.mutabilitydetector|MutabilityDetector|0.10.2| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.objenesis|objenesis|2.6| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.ops4j.base|ops4j-base-io|1.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.base|ops4j-base-lang|1.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.base|ops4j-base-monitors|1.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.base|ops4j-base-net|1.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.base|ops4j-base-spi|1.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.base|ops4j-base-store|1.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.base|ops4j-base-util-property|1.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.exam|pax-exam-container-native|4.13.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.exam|pax-exam|4.13.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.exam|pax-exam-junit4|4.13.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.exam|pax-exam-link-mvn|4.13.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.exam|pax-exam-spi|4.13.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.swissbox|pax-swissbox-core|1.8.2| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.swissbox|pax-swissbox-lifecycle|1.8.2| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.swissbox|pax-swissbox-optional-jcl|1.8.2| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.swissbox|pax-swissbox-property|1.8.2| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.swissbox|pax-swissbox-tracker|1.8.2| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.tinybundles|tinybundles|2.1.1| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.url|pax-url-aether|2.4.5| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.url|pax-url-classpath|2.4.5| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.url|pax-url-commons|2.4.5| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.ops4j.pax.url|pax-url-link|2.4.5| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.reactivestreams|reactive-streams-examples|1.0.3| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.reactivestreams|reactive-streams-tck|1.0.3| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.skyscreamer|jsonassert|1.2.3| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.slf4j|jcl-over-slf4j|1.6.6| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.testng|test|6.14.3| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|com.github.siom79.japicmp|japicmp-maven-plugin|0.14.3| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|com.mycila|license-maven-plugin|3.0| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.felix|maven-bundle-plugin|3.5.0| [21989](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=21989) | +|org.apache.maven.plugins|maven-assembly-plugin|3.1.0| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-clean-plugin|2.5| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-compiler-plugin|3.7.0| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-dependency-plugin|3.0.2| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-deploy-plugin|2.8.2| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-failsafe-plugin|3.0.0-M4| [19841](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=19841) | +|org.apache.maven.plugins|maven-install-plugin|2.5.2| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-resources-plugin|3.0.2| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-scm-plugin|1.9.5| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-site-plugin|3.3| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.apache.maven.plugins|maven-surefire-plugin|3.0.0-M4| [19841](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=19841) | +|org.codehaus.mojo|flatten-maven-plugin|1.0.1| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.codehaus.mojo|license-maven-plugin|1.17| [19841](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=19841) | +|org.codehaus.mojo|versions-maven-plugin|2.5| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | +|org.jacoco|jacoco-maven-plugin|0.8.5| [17676](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=17676) | diff --git a/legal/3rd-party-dependencies/generateCqsMd.sh b/legal/3rd-party-dependencies/generateCqsMd.sh index f87e4946..39765ee7 100755 --- a/legal/3rd-party-dependencies/generateCqsMd.sh +++ b/legal/3rd-party-dependencies/generateCqsMd.sh @@ -17,6 +17,12 @@ echo "| Group ID | Artifact ID | Version | CQ |" echo "|---|---|---|---|" cat compile.txt|cut -d':' -f1,2,4|sed -e 's/:/|/g'|while read i; do echo "|$i| []() |";done echo "" +echo "## Eclipse CQs - Runtime" +echo "" +echo "| Group ID | Artifact ID | Version | CQ |" +echo "|---|---|---|---|" +cat runtime.txt|cut -d':' -f1,2,4|sed -e 's/:/|/g'|while read i; do echo "|$i| []() |";done +echo "" echo "## Works-With dependencies" echo "" echo "| Group ID | Artifact ID | Version | CQ |" diff --git a/legal/3rd-party-dependencies/listDeps.sh b/legal/3rd-party-dependencies/listDeps.sh index 9a85026a..2ff51a04 100755 --- a/legal/3rd-party-dependencies/listDeps.sh +++ b/legal/3rd-party-dependencies/listDeps.sh @@ -11,13 +11,20 @@ # SPDX-License-Identifier: EPL-2.0 cd ../../java/ mvn dependency:list -DexcludeGroupIds=org.eclipse.ditto,rubygems -Dsort=true -DoutputFile=dependencies.txt -find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:compile'|sort|uniq > ../legal/3rd-party-dependencies//compile.txt -find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:test'|sort|uniq > ../legal/3rd-party-dependencies//test.txt -find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:provided'|sort|uniq > ../legal/3rd-party-dependencies//provided.txt +find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:compile'| tr -d '[:blank:]'| sed -e 's/(optional)//' -e 's/:compile.*/:compile/'|sort|uniq > ../legal/3rd-party-dependencies//compile.txt +find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:runtime'| tr -d '[:blank:]'| sed -e 's/(optional)//' -e 's/:runtime.*/:runtime/'|sort|uniq > ../legal/3rd-party-dependencies//runtime.txt +find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:test'| tr -d '[:blank:]'| sed -e 's/(optional)//' -e 's/:test.*/:test/'|sort|uniq > ../legal/3rd-party-dependencies//test.txt +find . -name dependencies.txt|while read i; do cat $i;done|grep '.*:.*:provided'| tr -d '[:blank:]'| sed -e 's/(optional)//' -e 's/:provided.*/:provided/'|sort|uniq > ../legal/3rd-party-dependencies//provided.txt + +# Cleanup temp files find . -name dependencies.txt|while read i; do rm $i;done + cd ../legal/3rd-party-dependencies/ + +# exclude compile dependencies from provided.txt + sort + remove duplicates cat compile.txt|cut -d':' -f1-4|while read i; do grep -h $i provided.txt;done|sort|uniq|while read x; do sed -i.bak -e s/$x// provided.txt ;done sed -i.bak '/^[[:space:]]*$/d' provided.txt +# exclude compile+provided dependencies from test.txt + sort + remove duplicates cat compile.txt provided.txt|cut -d':' -f1-4|while read i; do grep -h $i test.txt;done|sort|uniq|while read x; do sed -i.bak -e s/$x// test.txt ;done sed -i.bak '/^[[:space:]]*$/d' test.txt rm *.bak diff --git a/legal/3rd-party-dependencies/listMvnPlugins.sh b/legal/3rd-party-dependencies/listMvnPlugins.sh index 2ccfe267..11263537 100755 --- a/legal/3rd-party-dependencies/listMvnPlugins.sh +++ b/legal/3rd-party-dependencies/listMvnPlugins.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2019 Contributors to the Eclipse Foundation +# Copyright (c) 2017 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -11,7 +11,7 @@ # SPDX-License-Identifier: EPL-2.0 cd ../../java/ mvn dependency:list dependency:resolve-plugins -DoutputFile=plugins.txt -find . -name plugins.txt|while read i; do cat $i;done|grep '.*:.*:runtime$'|sort|uniq > ../legal/3rd-party-dependencies/maven-plugins.txt +find . -name plugins.txt|while read i; do cat $i;done|grep '.*:.*:runtime$'| tr -d '[:blank:]'|sort|uniq > ../legal/3rd-party-dependencies/maven-plugins.txt find . -name plugins.txt|while read i; do rm $i;done cd ../legal/3rd-party-dependencies/ diff --git a/legal/3rd-party-dependencies/maven-plugins.txt b/legal/3rd-party-dependencies/maven-plugins.txt index a4798c5b..b672947c 100644 --- a/legal/3rd-party-dependencies/maven-plugins.txt +++ b/legal/3rd-party-dependencies/maven-plugins.txt @@ -1,15 +1,18 @@ - com.github.siom79.japicmp:japicmp-maven-plugin:maven-plugin:0.11.0:runtime - com.mycila:license-maven-plugin:maven-plugin:3.0:runtime - org.apache.felix:maven-bundle-plugin:maven-plugin:3.5.0:runtime - org.apache.maven.plugins:maven-assembly-plugin:maven-plugin:3.1.0:runtime - org.apache.maven.plugins:maven-clean-plugin:maven-plugin:2.5:runtime - org.apache.maven.plugins:maven-compiler-plugin:maven-plugin:3.7.0:runtime - org.apache.maven.plugins:maven-deploy-plugin:maven-plugin:2.8.2:runtime - org.apache.maven.plugins:maven-failsafe-plugin:maven-plugin:3.0.0-M1:runtime - org.apache.maven.plugins:maven-install-plugin:maven-plugin:2.5.2:runtime - org.apache.maven.plugins:maven-resources-plugin:maven-plugin:3.0.2:runtime - org.apache.maven.plugins:maven-scm-plugin:maven-plugin:1.9.5:runtime - org.apache.maven.plugins:maven-site-plugin:maven-plugin:3.3:runtime - org.apache.maven.plugins:maven-surefire-plugin:maven-plugin:3.0.0-M1:runtime - org.codehaus.mojo:license-maven-plugin:maven-plugin:1.17:runtime - org.codehaus.mojo:versions-maven-plugin:maven-plugin:2.5:runtime +com.github.siom79.japicmp:japicmp-maven-plugin:maven-plugin:0.14.3:runtime +com.mycila:license-maven-plugin:maven-plugin:3.0:runtime +org.apache.felix:maven-bundle-plugin:maven-plugin:3.5.0:runtime +org.apache.maven.plugins:maven-assembly-plugin:maven-plugin:3.1.0:runtime +org.apache.maven.plugins:maven-clean-plugin:maven-plugin:2.5:runtime +org.apache.maven.plugins:maven-compiler-plugin:maven-plugin:3.7.0:runtime +org.apache.maven.plugins:maven-dependency-plugin:maven-plugin:3.0.2:runtime +org.apache.maven.plugins:maven-deploy-plugin:maven-plugin:2.8.2:runtime +org.apache.maven.plugins:maven-failsafe-plugin:maven-plugin:3.0.0-M4:runtime +org.apache.maven.plugins:maven-install-plugin:maven-plugin:2.5.2:runtime +org.apache.maven.plugins:maven-resources-plugin:maven-plugin:3.0.2:runtime +org.apache.maven.plugins:maven-scm-plugin:maven-plugin:1.9.5:runtime +org.apache.maven.plugins:maven-site-plugin:maven-plugin:3.3:runtime +org.apache.maven.plugins:maven-surefire-plugin:maven-plugin:3.0.0-M4:runtime +org.codehaus.mojo:flatten-maven-plugin:maven-plugin:1.0.1:runtime +org.codehaus.mojo:license-maven-plugin:maven-plugin:1.17:runtime +org.codehaus.mojo:versions-maven-plugin:maven-plugin:2.5:runtime +org.jacoco:jacoco-maven-plugin:maven-plugin:0.8.5:runtime diff --git a/legal/3rd-party-dependencies/provided.txt b/legal/3rd-party-dependencies/provided.txt index 08399945..ee0834c3 100644 --- a/legal/3rd-party-dependencies/provided.txt +++ b/legal/3rd-party-dependencies/provided.txt @@ -1 +1 @@ - com.google.code.findbugs:jsr305:jar:3.0.1:provided +com.google.code.findbugs:jsr305:jar:3.0.1:provided diff --git a/legal/3rd-party-dependencies/runtime.txt b/legal/3rd-party-dependencies/runtime.txt new file mode 100644 index 00000000..e69de29b diff --git a/legal/3rd-party-dependencies/test.txt b/legal/3rd-party-dependencies/test.txt index 3df71b89..bfa98253 100644 --- a/legal/3rd-party-dependencies/test.txt +++ b/legal/3rd-party-dependencies/test.txt @@ -1,41 +1,47 @@ - biz.aQute.bnd:bndlib:jar:2.4.0:test - ch.qos.logback:logback-classic:jar:1.2.3:test - ch.qos.logback:logback-core:jar:1.2.3:test - junit:junit:jar:4.12:test - net.bytebuddy:byte-buddy-agent:jar:1.7.0:test - net.bytebuddy:byte-buddy:jar:1.7.0:test - net.javacrumbs.json-unit:json-unit-core:jar:1.28.1:test - net.javacrumbs.json-unit:json-unit:jar:1.28.1:test - nl.jqno.equalsverifier:equalsverifier:jar:3.0.3:test - org.apache.felix:org.apache.felix.framework:jar:5.4.0:test - org.apache.servicemix.bundles:org.apache.servicemix.bundles.javax-inject:jar:1_2:test - org.assertj:assertj-core:jar:3.11.1:test - org.hamcrest:hamcrest-core:jar:1.3:test - org.json:json:jar:20090211:test - org.mockito:mockito-core:jar:2.9.0:test - org.mutabilitydetector:MutabilityDetector:jar:0.9.6:test - org.objenesis:objenesis:jar:2.6:test - org.ops4j.base:ops4j-base-io:jar:1.5.0:test - org.ops4j.base:ops4j-base-lang:jar:1.5.0:test - org.ops4j.base:ops4j-base-monitors:jar:1.5.0:test - org.ops4j.base:ops4j-base-net:jar:1.5.0:test - org.ops4j.base:ops4j-base-spi:jar:1.5.0:test - org.ops4j.base:ops4j-base-store:jar:1.5.0:test - org.ops4j.base:ops4j-base-util-property:jar:1.5.0:test - org.ops4j.pax.exam:pax-exam-container-native:jar:4.8.0:test - org.ops4j.pax.exam:pax-exam:jar:4.8.0:test - org.ops4j.pax.exam:pax-exam-junit4:jar:4.8.0:test - org.ops4j.pax.exam:pax-exam-link-mvn:jar:4.8.0:test - org.ops4j.pax.exam:pax-exam-spi:jar:4.8.0:test - org.ops4j.pax.swissbox:pax-swissbox-core:jar:1.8.2:test - org.ops4j.pax.swissbox:pax-swissbox-lifecycle:jar:1.8.2:test - org.ops4j.pax.swissbox:pax-swissbox-optional-jcl:jar:1.8.2:test - org.ops4j.pax.swissbox:pax-swissbox-property:jar:1.8.2:test - org.ops4j.pax.swissbox:pax-swissbox-tracker:jar:1.8.2:test - org.ops4j.pax.tinybundles:tinybundles:jar:2.1.1:test - org.ops4j.pax.url:pax-url-aether:jar:2.4.2:test - org.ops4j.pax.url:pax-url-classpath:jar:2.4.2:test - org.ops4j.pax.url:pax-url-commons:jar:2.4.2:test - org.ops4j.pax.url:pax-url-link:jar:2.4.2:test - org.skyscreamer:jsonassert:jar:1.2.3:test - org.slf4j:jcl-over-slf4j:jar:1.6.6:test +biz.aQute.bnd:bndlib:jar:2.4.0:test +ch.qos.logback:logback-classic:jar:1.2.3:test +ch.qos.logback:logback-core:jar:1.2.3:test +com.beust:jcommander:jar:1.72:test +junit:junit:jar:4.12:test +net.bytebuddy:byte-buddy-agent:jar:1.9.10:test +net.bytebuddy:byte-buddy:jar:1.9.10:test +net.javacrumbs.json-unit:json-unit-core:jar:1.28.1:test +net.javacrumbs.json-unit:json-unit:jar:1.28.1:test +nl.jqno.equalsverifier:equalsverifier:jar:3.0.3:test +org.apache-extras.beanshell:bsh:jar:2.0b6:test +org.apache.felix:org.apache.felix.framework:jar:6.0.3:test +org.apache.servicemix.bundles:org.apache.servicemix.bundles.javax-inject:jar:1_2:test +org.assertj:assertj-core:jar:3.12.0:test +org.codehaus.mojo:animal-sniffer-annotations:jar:1.9:test +org.hamcrest:hamcrest-core:jar:1.3:test +org.json:json:jar:20090211:test +org.mockito:mockito-core:jar:3.1.0:test +org.mutabilitydetector:MutabilityDetector:jar:0.10.2:test +org.objenesis:objenesis:jar:2.6:test +org.ops4j.base:ops4j-base-io:jar:1.5.0:test +org.ops4j.base:ops4j-base-lang:jar:1.5.0:test +org.ops4j.base:ops4j-base-monitors:jar:1.5.0:test +org.ops4j.base:ops4j-base-net:jar:1.5.0:test +org.ops4j.base:ops4j-base-spi:jar:1.5.0:test +org.ops4j.base:ops4j-base-store:jar:1.5.0:test +org.ops4j.base:ops4j-base-util-property:jar:1.5.0:test +org.ops4j.pax.exam:pax-exam-container-native:jar:4.13.0:test +org.ops4j.pax.exam:pax-exam:jar:4.13.0:test +org.ops4j.pax.exam:pax-exam-junit4:jar:4.13.0:test +org.ops4j.pax.exam:pax-exam-link-mvn:jar:4.13.0:test +org.ops4j.pax.exam:pax-exam-spi:jar:4.13.0:test +org.ops4j.pax.swissbox:pax-swissbox-core:jar:1.8.2:test +org.ops4j.pax.swissbox:pax-swissbox-lifecycle:jar:1.8.2:test +org.ops4j.pax.swissbox:pax-swissbox-optional-jcl:jar:1.8.2:test +org.ops4j.pax.swissbox:pax-swissbox-property:jar:1.8.2:test +org.ops4j.pax.swissbox:pax-swissbox-tracker:jar:1.8.2:test +org.ops4j.pax.tinybundles:tinybundles:jar:2.1.1:test +org.ops4j.pax.url:pax-url-aether:jar:2.4.5:test +org.ops4j.pax.url:pax-url-classpath:jar:2.4.5:test +org.ops4j.pax.url:pax-url-commons:jar:2.4.5:test +org.ops4j.pax.url:pax-url-link:jar:2.4.5:test +org.reactivestreams:reactive-streams-examples:jar:1.0.3:test +org.reactivestreams:reactive-streams-tck:jar:1.0.3:test +org.skyscreamer:jsonassert:jar:1.2.3:test +org.slf4j:jcl-over-slf4j:jar:1.6.6:test +org.testng:test diff --git a/legal/NOTICE-THIRD-PARTY.md b/legal/NOTICE-THIRD-PARTY.md index ccbe5019..41512936 100644 --- a/legal/NOTICE-THIRD-PARTY.md +++ b/legal/NOTICE-THIRD-PARTY.md @@ -16,17 +16,25 @@ * Source: https://github.com/TakahikoKawasaki/nv-websocket-client -## Atteo Class Index (3.7) +## Atteo Class Index (3.8) - * Maven coordinates: `org.atteo.classindex:classindex:3.7` + * Maven coordinates: `org.atteo.classindex:classindex:3.8` * License: [Apache-2.0](licenses/Apache-2.0.txt) * Project: http://atteo.org/static/classindex/classindex * Source: https://github.com/atteo/classindex/classindex -## SLF4J API Module (1.7.25) +## reactive-streams (1.0.3) - * Maven coordinates: `org.slf4j:slf4j-api:1.7.25` + * Maven coordinates: `org.reactivestreams:reactive-streams:1.0.3` + * License: [CC0](licenses/CC0.txt) + * Project: http://www.reactive-streams.org/ + * Source: https://github.com/reactive-streams/reactive-streams + + +## SLF4J API Module (1.7.28) + + * Maven coordinates: `org.slf4j:slf4j-api:1.7.28` * License: [MIT](licenses/MIT.txt) * Project: http://www.slf4j.org * Source: https://github.com/qos-ch/slf4j/slf4j-api diff --git a/legal/licenses/CC0.txt b/legal/licenses/CC0.txt new file mode 100644 index 00000000..0e259d42 --- /dev/null +++ b/legal/licenses/CC0.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work.