From 3aad4eb6b7089744d6cc16cb6e134f131455b097 Mon Sep 17 00:00:00 2001 From: Thomas Jaeckle <thomas.jaeckle@bosch.io> Date: Fri, 21 Feb 2020 13:36:12 +0100 Subject: [PATCH 01/17] update Ditto version to 1.0.0-M2 --- java/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/pom.xml b/java/pom.xml index ba873ba8..91dc1314 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -190,7 +190,7 @@ <revision>0-SNAPSHOT</revision> <!-- ### Compile dependencies versions --> - <ditto.version>${revision}</ditto.version> + <ditto.version>1.0.0-M2</ditto.version> <minimal-json.version>0.9.5</minimal-json.version> <nv-websocket-client.version>2.9</nv-websocket-client.version> <classindex.version>3.8</classindex.version> From 1c02e04c58af354e05cbe5aee775b687bd751742 Mon Sep 17 00:00:00 2001 From: Thomas Jaeckle <thomas.jaeckle@bosch.io> Date: Fri, 21 Feb 2020 14:17:05 +0100 Subject: [PATCH 02/17] Fixed used Ditto version: 1.1.0-M2 --- java/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/pom.xml b/java/pom.xml index 91dc1314..b30e4777 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -190,7 +190,7 @@ <revision>0-SNAPSHOT</revision> <!-- ### Compile dependencies versions --> - <ditto.version>1.0.0-M2</ditto.version> + <ditto.version>1.1.0-M2</ditto.version> <minimal-json.version>0.9.5</minimal-json.version> <nv-websocket-client.version>2.9</nv-websocket-client.version> <classindex.version>3.8</classindex.version> From 43eeb95d4f11f11f3b9204e230a681a5ea476a65 Mon Sep 17 00:00:00 2001 From: Thomas Jaeckle <thomas.jaeckle@bosch.io> Date: Fri, 21 Feb 2020 16:56:32 +0100 Subject: [PATCH 03/17] added mandatory configuration for gpg plugin for release on JIRO --- java/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/java/pom.xml b/java/pom.xml index b30e4777..ede06f34 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -1166,6 +1166,12 @@ <goals> <goal>sign</goal> </goals> + <configuration> + <gpgArguments> + <arg>--pinentry-mode</arg> + <arg>loopback</arg> + </gpgArguments> + </configuration> </execution> </executions> </plugin> From b84ea1abe4b09bd8398246d5c90bd08ac12e7bf4 Mon Sep 17 00:00:00 2001 From: Thomas Jaeckle <thomas.jaeckle@bosch.io> Date: Thu, 27 Feb 2020 18:33:30 +0100 Subject: [PATCH 04/17] use actions/checkout@v2 --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 95efac6c..9afa5e17 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -23,7 +23,7 @@ jobs: java: [ '1.8' ] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: set up JDK ${{ matrix.java }} uses: actions/setup-java@v1 with: From 9010d3dcc06a881d7dca43aa752edcc8fd89e561 Mon Sep 17 00:00:00 2001 From: Thomas Jaeckle <thomas.jaeckle@bosch.io> Date: Thu, 27 Feb 2020 18:33:53 +0100 Subject: [PATCH 05/17] use actions/checkout@v2 for node --- .github/workflows/nodejs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 883361a7..37c3eadb 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -24,7 +24,7 @@ jobs: node-version: [10.x, 12.x] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: From e5dd921bb0a3ffc1b7590e7f5ff4d768031b2f27 Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Fri, 28 Feb 2020 10:51:07 +0100 Subject: [PATCH 06/17] Don't box instances of AuthenticationException into each other. This happens because #safeGet is called twice on #initialize. Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- .../messaging/internal/WebSocketMessagingProvider.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 780e07ae..4a28e0e2 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 @@ -286,7 +286,10 @@ private <T> T handleTimeoutException(final TimeoutException e) { private <T> T handleExecutionException(final ExecutionException e) { final Throwable cause = e.getCause(); - if (cause instanceof WebSocketException) { + if (cause instanceof AuthenticationException) { + // no unneccessary boxing of AuthenticationException + throw ((AuthenticationException) cause); + } else if (cause instanceof WebSocketException) { LOGGER.error("Got exception: {}", cause.getMessage()); if (isAuthenticationException((WebSocketException) cause)) { @@ -295,6 +298,7 @@ private <T> T handleExecutionException(final ExecutionException e) { throw AuthenticationException.forbidden(sessionId, cause); } } + throw AuthenticationException.of(sessionId, e); } From 12b21624c0e9141c4f8466471489515e45ba19ca Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Fri, 28 Feb 2020 10:52:32 +0100 Subject: [PATCH 07/17] Add specific exception message for AuthenticationExceptions when we know the status and the reason of the connection failure. Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- .../messaging/AuthenticationException.java | 19 +++++++++++++++++++ .../internal/WebSocketMessagingProvider.java | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java b/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java index 64c48742..1d7225bc 100755 --- a/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java +++ b/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java @@ -30,6 +30,9 @@ public class AuthenticationException extends RuntimeException { private static final String FORBIDDEN_MESSAGE_TEMPLATE = "Authentication of session <%s> failed because of insufficient permissions."; + private static final String STATUS_MESSAGE_TEMPLATE = + "Authentication of session <%s> failed with status <%d> and reason: %s"; + private static final long serialVersionUID = 4405868587578425044L; private AuthenticationException(final String message, final Throwable cause) { @@ -48,4 +51,20 @@ public static AuthenticationException forbidden(final String sessionId, final Th return new AuthenticationException(String.format(FORBIDDEN_MESSAGE_TEMPLATE, sessionId), cause); } + /** + * Creates an AutenticationException for the {@code status code its {@code reason}. + * @param sessionId the sessionId that failed to authenticate. + * @param cause the cause of the failure. + * @param status the status code of the failure. + * @param reason a reason message for the failure. + * @return the AuthenticationException with the provided information. + * @since 1.1.0 + */ + public static AuthenticationException withStatus(final String sessionId, final Throwable cause, + final int status, final String reason) { + return new AuthenticationException( + String.format(STATUS_MESSAGE_TEMPLATE, sessionId, status, reason), + cause); + } + } 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 4a28e0e2..0eda9c2a 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 @@ -296,6 +296,10 @@ private <T> T handleExecutionException(final ExecutionException e) { throw AuthenticationException.unauthorized(sessionId, cause); } else if (isForbidden((WebSocketException) cause)) { throw AuthenticationException.forbidden(sessionId, cause); + } else if (cause instanceof OpeningHandshakeException) { + final StatusLine statusLine = ((OpeningHandshakeException) cause).getStatusLine(); + throw AuthenticationException.withStatus(sessionId, cause, statusLine.getStatusCode(), + statusLine.getReasonPhrase()); } } From 49c309b0f86d1fc9c8fe761f6b1ca3577b489ab3 Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Fri, 28 Feb 2020 10:57:06 +0100 Subject: [PATCH 08/17] set JsonSchemaVersion to V_2 in README.md Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- java/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/README.md b/java/README.md index a2533e3b..b6f4cd24 100644 --- a/java/README.md +++ b/java/README.md @@ -71,7 +71,7 @@ AuthenticationProvider authenticationProvider = ```java MessagingProvider messagingProvider = MessagingProviders.webSocket(WebSocketMessagingConfiguration.newBuilder() .endpoint("wss://ditto.eclipse.org") - .jsonSchemaVersion(JsonSchemaVersion.V_1) + .jsonSchemaVersion(JsonSchemaVersion.V_2) .proxyConfiguration(proxyConfig) // optionally configure a proxy server // optionally configure a truststore containing the trusted CAs for SSL connection establishment .trustStoreConfiguration(TrustStoreConfiguration.newBuilder() From 2ba45bd634988961a9df52c36e61c65476ac8d30 Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Fri, 28 Feb 2020 11:23:33 +0100 Subject: [PATCH 09/17] fix javadoc failure Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- .../eclipse/ditto/client/messaging/AuthenticationException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java b/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java index 1d7225bc..7f5f5d51 100755 --- a/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java +++ b/java/src/main/java/org/eclipse/ditto/client/messaging/AuthenticationException.java @@ -52,7 +52,7 @@ public static AuthenticationException forbidden(final String sessionId, final Th } /** - * Creates an AutenticationException for the {@code status code its {@code reason}. + * Creates an AutenticationException for the {@code status} code its {@code reason}. * @param sessionId the sessionId that failed to authenticate. * @param cause the cause of the failure. * @param status the status code of the failure. From c12dee9eb9891b079bbebc582ad328d175a49d33 Mon Sep 17 00:00:00 2001 From: Michael Gantert <michael.gantert@bosch.io> Date: Thu, 5 Mar 2020 12:57:29 +0100 Subject: [PATCH 10/17] JaCoCo: Switch from binary (deprecated) to xml report. Signed-off-by: Michael Gantert <michael.gantert@bosch.io> --- java/pom.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index ba873ba8..5343571e 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -240,6 +240,8 @@ <ditto.thirdPartyLicences.excludedGroups> (org\.eclipse\.ditto.*) </ditto.thirdPartyLicences.excludedGroups> + + <sonar.coverage.jacoco.xmlReportPaths>${project.basedir}/target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths> </properties> <build> @@ -376,7 +378,7 @@ <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> - <version>0.8.3</version> + <version>0.8.5</version> </plugin> <plugin> <groupId>com.github.siom79.japicmp</groupId> @@ -704,7 +706,7 @@ <artifactId>jacoco-maven-plugin</artifactId> <executions> <execution> - <id>prepare</id> + <id>prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> @@ -712,7 +714,7 @@ <execution> <id>report</id> <goals> - <goal>report-aggregate</goal> + <goal>report</goal> </goals> </execution> </executions> From 6e1996d42051806450386d658c05d343998bf494 Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Mon, 16 Mar 2020 08:35:07 +0100 Subject: [PATCH 11/17] add missing argument not null assertions and update binary compatibility check version to 1.1.0-M2 Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- java/pom.xml | 2 +- .../client/internal/CommonManagementImpl.java | 71 ++++++++++++------- .../client/management/CommonManagement.java | 26 ++++--- .../ditto/client/DittoClientThingTest.java | 30 +++++++- 4 files changed, 89 insertions(+), 40 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index ba873ba8..6fd8d62e 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -233,7 +233,7 @@ <!-- globally set version for checking binary compatibility against --> <!-- whoever changes this to ${revision} or ${project.version} is responsible for API breakage caused by this! --> <!-- in other words: never do that here! exclude the 'breakages' locally in the japicmp maven plugin if you intentionally break something --> - <binary-compatibility-check.version>1.0.0</binary-compatibility-check.version> + <binary-compatibility-check.version>1.1.0-M2</binary-compatibility-check.version> <!-- skip until first release: --> <binary-compatibility-check.skip>false</binary-compatibility-check.skip> 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 9e232c49..488b7719 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 @@ -75,6 +75,9 @@ public abstract class CommonManagementImpl<T extends ThingHandle<F>, F extends FeatureHandle> implements CommonManagement<T, F> { + private static final String ARGUMENT_THING_ID = "thingId"; + private static final String ARGUMENT_THING = "thing"; + private static final String ARGUMENT_INITIAL_POLICY = "initialPolicy"; private static final Logger LOGGER = LoggerFactory.getLogger(CommonManagementImpl.class); private final TopicPath.Channel channel; @@ -280,20 +283,21 @@ public CompletableFuture<Thing> create(final JsonObject jsonObject, final Option } @Override - public CompletableFuture<Thing> create(final Policy policy, final Option<?>... options) { + public CompletableFuture<Thing> create(final Policy initialPolicy, final Option<?>... options) { + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); // as the backend adds the default namespace, we can here simply use the empty namespace. final Thing thing = ThingsModelFactory.newThingBuilder() .setId(ThingId.generateRandom()) .build(); - return processCreate(thing, policy.toJson(), options); + return processCreate(thing, initialPolicy.toJson(), options); } @Override public CompletableFuture<Thing> create(final ThingId thingId, final JsonObject initialPolicy, final Option<?>... options) { - argumentNotNull(thingId); + argumentNotNull(thingId, ARGUMENT_THING_ID); argumentNotEmpty(thingId); - argumentNotNull(initialPolicy); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); final Thing thing = ThingsModelFactory.newThingBuilder() .setId(ThingId.of(thingId)) @@ -305,9 +309,9 @@ public CompletableFuture<Thing> create(final ThingId thingId, final JsonObject i @Override public CompletableFuture<Thing> create(final ThingId thingId, final Policy initialPolicy, final Option<?>... options) { - argumentNotNull(thingId); + argumentNotNull(thingId, ARGUMENT_THING_ID); argumentNotEmpty(thingId); - argumentNotNull(initialPolicy); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); final Thing thing = ThingsModelFactory.newThingBuilder() .setId(ThingId.of(thingId)) @@ -316,37 +320,40 @@ public CompletableFuture<Thing> create(final ThingId thingId, final Policy initi } @Override - public CompletableFuture<Thing> create(final JsonObject jsonObject, final JsonObject initialPolicy, + public CompletableFuture<Thing> create(final JsonObject thing, final JsonObject initialPolicy, final Option<?>... options) { - argumentNotNull(jsonObject); - argumentNotNull(initialPolicy); + argumentNotNull(thing, ARGUMENT_THING); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); - final Thing thing = ThingsModelFactory.newThing(jsonObject); - - return processCreate(thing, initialPolicy, options); + return processCreate(ThingsModelFactory.newThing(thing), initialPolicy, options); } @Override - public CompletableFuture<Thing> create(final JsonObject jsonObject, final Policy initialPolicy, + public CompletableFuture<Thing> create(final JsonObject thing, final Policy initialPolicy, final Option<?>... options) { - argumentNotNull(jsonObject); - argumentNotNull(initialPolicy); - - final Thing thing = ThingsModelFactory.newThing(jsonObject); + argumentNotNull(thing, ARGUMENT_THING); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); - return processCreate(thing, initialPolicy.toJson(), options); + return processCreate(ThingsModelFactory.newThing(thing), initialPolicy.toJson(), options); } @Override public CompletableFuture<Thing> create(final Thing thing, final JsonObject initialPolicy, final Option<?>... options) { + argumentNotNull(thing, ARGUMENT_THING); + assertThatThingHasId(thing); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); + return processCreate(thing, initialPolicy, options); } - @Override public CompletableFuture<Thing> create(final Thing thing, final Policy initialPolicy, final Option<?>... options) { + argumentNotNull(thing, ARGUMENT_THING); + assertThatThingHasId(thing); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); + return processCreate(thing, initialPolicy.toJson(), options); } @@ -370,6 +377,8 @@ private CompletableFuture<Thing> processCreate(final Thing thing, @Nullable fina @Override public CompletableFuture<Optional<Thing>> put(final Thing thing, final Option<?>... options) { + argumentNotNull(thing, ARGUMENT_THING); + assertThatThingHasId(thing); return processPut(thing, null, options); } @@ -384,32 +393,40 @@ public CompletableFuture<Optional<Thing>> put(final JsonObject jsonObject, final } @Override - public CompletableFuture<Optional<Thing>> put(final JsonObject jsonObject, final JsonObject initialPolicy, + public CompletableFuture<Optional<Thing>> put(final JsonObject thing, final JsonObject initialPolicy, final Option<?>... options) { - argumentNotNull(jsonObject); + argumentNotNull(thing, ARGUMENT_THING); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); - final Thing thing = ThingsModelFactory.newThing(jsonObject); - return processPut(thing, initialPolicy, options); + return processPut(ThingsModelFactory.newThing(thing), initialPolicy, options); } @Override - public CompletableFuture<Optional<Thing>> put(final JsonObject jsonObject, final Policy initialPolicy, + public CompletableFuture<Optional<Thing>> put(final JsonObject thing, final Policy initialPolicy, final Option<?>... options) { - argumentNotNull(jsonObject); + argumentNotNull(thing, ARGUMENT_THING); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); - final Thing thing = ThingsModelFactory.newThing(jsonObject); - return processPut(thing, initialPolicy.toJson(), options); + return processPut(ThingsModelFactory.newThing(thing), initialPolicy.toJson(), options); } @Override public CompletableFuture<Optional<Thing>> put(final Thing thing, final JsonObject initialPolicy, final Option<?>... options) { + argumentNotNull(thing, ARGUMENT_THING); + assertThatThingHasId(thing); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); + return processPut(thing, initialPolicy, options); } @Override public CompletableFuture<Optional<Thing>> put(final Thing thing, final Policy initialPolicy, final Option<?>... options) { + argumentNotNull(thing, ARGUMENT_THING); + assertThatThingHasId(thing); + argumentNotNull(initialPolicy, ARGUMENT_INITIAL_POLICY); + return processPut(thing, initialPolicy.toJson(), options); } diff --git a/java/src/main/java/org/eclipse/ditto/client/management/CommonManagement.java b/java/src/main/java/org/eclipse/ditto/client/management/CommonManagement.java index d7170ff8..85a820d2 100755 --- a/java/src/main/java/org/eclipse/ditto/client/management/CommonManagement.java +++ b/java/src/main/java/org/eclipse/ditto/client/management/CommonManagement.java @@ -204,7 +204,8 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * org.eclipse.ditto.client.options.Options}. * @return completable future providing the created Thing object or a specific {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed - * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier. + * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier, or if + * {@code initialPolicy} is {@code null}. * @throws org.eclipse.ditto.model.things.ThingIdInvalidException if the {@code thingId} was invalid. * @since 1.1.0 */ @@ -220,7 +221,8 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * org.eclipse.ditto.client.options.Options}. * @return completable future providing the created Thing object or a specific {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed - * @throws IllegalArgumentException if {@code thingId} is {@code null} or empty. + * @throws IllegalArgumentException if {@code thingId} is {@code null} or empty, or if {@code initialPolicy} is + * {@code null}. * @throws org.eclipse.ditto.model.things.ThingIdInvalidException if the {@code thingId} was invalid. * @since 1.1.0 */ @@ -238,7 +240,7 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * @return completable future providing the created Thing object or a specific {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed * @throws IllegalArgumentException if {@code thing} is {@code null} or if it does not contain the field named - * {@code "thingId"}. + * {@code "thingId"}, or if {@code initialPolicy} is {@code null}. * @throws org.eclipse.ditto.model.base.exceptions.DittoJsonException if {@code thing} cannot be parsed to a {@link * Thing}. * @throws org.eclipse.ditto.model.things.ThingIdInvalidException if the {@code thingId} was invalid. @@ -255,7 +257,8 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * org.eclipse.ditto.client.options.Options}. * @return completable future providing the created Thing object or a specific {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed - * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier. + * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier, or if + * {@code initialPolicy} is {@code null}. * @throws org.eclipse.ditto.model.things.ThingIdInvalidException if the {@code thingId} was invalid. * @since 1.1.0 */ @@ -271,7 +274,8 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * org.eclipse.ditto.client.options.Options}. * @return completable future providing the created Thing object or a specific {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed - * @throws IllegalArgumentException if {@code thingId} is {@code null} or empty. + * @throws IllegalArgumentException if {@code thingId} is {@code null} or empty, or if {@code initialPolicy} is + * {@code null}. * @throws org.eclipse.ditto.model.things.ThingIdInvalidException if the {@code thingId} was invalid. * @since 1.1.0 */ @@ -289,7 +293,7 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * @return completable future providing the created Thing object or a specific {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed * @throws IllegalArgumentException if {@code thing} is {@code null} or if it does not contain the field named - * {@code "thingId"}. + * {@code "thingId"}, or if {@code initialPolicy} is {@code null}. * @throws org.eclipse.ditto.model.base.exceptions.DittoJsonException if {@code thing} cannot be parsed to a {@link * Thing}. * @throws org.eclipse.ditto.model.things.ThingIdInvalidException if the {@code thingId} was invalid. @@ -344,7 +348,8 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * @return completable future providing an {@link Optional} containing the created Thing object, in case the Thing * has been created, or an empty Optional, in case the Thing has been updated. Provides a {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed. - * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier. + * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier, or if + * {@code initialPolicy} is {@code null}. * @since 1.1.0 */ CompletableFuture<Optional<Thing>> put(Thing thing, JsonObject initialPolicy, Option<?>... options); @@ -364,7 +369,7 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * has been created, or an empty Optional, in case the Thing has been updated. Provides a {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed. * @throws IllegalArgumentException if {@code thing} is {@code null} or if it does not contain the field named - * {@code "thingId"}. + * {@code "thingId"}, or if {@code initialPolicy} is {@code null}. * @throws org.eclipse.ditto.model.base.exceptions.DittoJsonException if {@code thing} cannot be parsed to a {@link * Thing}. * @since 1.1.0 @@ -383,7 +388,8 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * @return completable future providing an {@link Optional} containing the created Thing object, in case the Thing * has been created, or an empty Optional, in case the Thing has been updated. Provides a {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed. - * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier. + * @throws IllegalArgumentException if {@code thing} is {@code null} or has no identifier, or if + * {@code initialPolicy} is {@code null}. * @since 1.1.0 */ CompletableFuture<Optional<Thing>> put(Thing thing, Policy initialPolicy, Option<?>... options); @@ -403,7 +409,7 @@ public interface CommonManagement<T extends ThingHandle, F extends FeatureHandle * has been created, or an empty Optional, in case the Thing has been updated. Provides a {@link * org.eclipse.ditto.model.base.exceptions.DittoRuntimeException} if the operation failed. * @throws IllegalArgumentException if {@code thing} is {@code null} or if it does not contain the field named - * {@code "thingId"}. + * {@code "thingId"}, or if {@code initialPolicy} is {@code null}. * @throws org.eclipse.ditto.model.base.exceptions.DittoJsonException if {@code thing} cannot be parsed to a {@link * Thing}. * @since 1.1.0 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 faf358ca..761b4470 100644 --- a/java/src/test/java/org/eclipse/ditto/client/DittoClientThingTest.java +++ b/java/src/test/java/org/eclipse/ditto/client/DittoClientThingTest.java @@ -31,6 +31,7 @@ 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.auth.AuthorizationModelFactory; import org.eclipse.ditto.model.base.auth.AuthorizationSubject; @@ -39,6 +40,7 @@ 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.policies.Policy; import org.eclipse.ditto.model.policies.PolicyId; import org.eclipse.ditto.model.things.Feature; import org.eclipse.ditto.model.things.Thing; @@ -371,7 +373,7 @@ public void testCreateThingWithInlinePolicy() throws InterruptedException { } @Test - public void testCreateThingWithInitialJSONPolicy() throws InterruptedException { + public void testCreateThingWithInitialPolicyJson() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); messaging.onSend(m -> { @@ -388,6 +390,12 @@ public void testCreateThingWithInitialJSONPolicy() throws InterruptedException { Assertions.assertThat(latch.await(TIMEOUT, TIME_UNIT)).isTrue(); } + @Test + public void testCreateThingWithInitialPolicyJsonNullable() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> client.twin().create(THING_ID, (JsonObject) null)); + } + @Test public void testCreateThingWithInitialPolicy() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); @@ -406,6 +414,12 @@ public void testCreateThingWithInitialPolicy() throws InterruptedException { Assertions.assertThat(latch.await(TIMEOUT, TIME_UNIT)).isTrue(); } + @Test + public void testCreateThingWithInitialPolicyNull() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> client.twin().create(THING_ID, (Policy) null)); + } + @Test public void testPutThingWithInlinePolicy() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); @@ -425,7 +439,7 @@ public void testPutThingWithInlinePolicy() throws InterruptedException { } @Test - public void testPutThingWithInitialJSONPolicy() throws InterruptedException { + public void testPutThingWithInitialPolicyJson() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); messaging.onSend(m -> { @@ -442,6 +456,12 @@ public void testPutThingWithInitialJSONPolicy() throws InterruptedException { Assertions.assertThat(latch.await(TIMEOUT, TIME_UNIT)).isTrue(); } + @Test + public void testPutThingWithInitialPolicyJsonNull() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> client.twin().put(THING, (JsonObject) null)); + } + @Test public void testPutThingWithInitialPolicy() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); @@ -460,6 +480,12 @@ public void testPutThingWithInitialPolicy() throws InterruptedException { Assertions.assertThat(latch.await(TIMEOUT, TIME_UNIT)).isTrue(); } + @Test + public void testPutThingWithInitialPolicyNull() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> client.twin().put(THING, (Policy) null)); + } + @Test public void testCreateThingWithOptionCopyPolicy() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); From 4e6ecdc6fbcd8c8630e11bfbf0b3d9126f1fa810 Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Mon, 16 Mar 2020 08:54:42 +0100 Subject: [PATCH 12/17] revert updating binary compatibility check version to 1.1.0-M2 as the check should only be done against real releases Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- java/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/pom.xml b/java/pom.xml index 6fd8d62e..ba873ba8 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -233,7 +233,7 @@ <!-- globally set version for checking binary compatibility against --> <!-- whoever changes this to ${revision} or ${project.version} is responsible for API breakage caused by this! --> <!-- in other words: never do that here! exclude the 'breakages' locally in the japicmp maven plugin if you intentionally break something --> - <binary-compatibility-check.version>1.1.0-M2</binary-compatibility-check.version> + <binary-compatibility-check.version>1.0.0</binary-compatibility-check.version> <!-- skip until first release: --> <binary-compatibility-check.skip>false</binary-compatibility-check.skip> From 7cc1b8bd8505b0a70142e73a4efed2bd7067052c Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Tue, 17 Mar 2020 10:38:32 +0100 Subject: [PATCH 13/17] use github action checkout@v2 to fix bug "fatal: reference is not a tree" Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- .github/workflows/maven.yml | 2 +- .github/workflows/nodejs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 95efac6c..9afa5e17 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -23,7 +23,7 @@ jobs: java: [ '1.8' ] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: set up JDK ${{ matrix.java }} uses: actions/setup-java@v1 with: diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 883361a7..37c3eadb 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -24,7 +24,7 @@ jobs: node-version: [10.x, 12.x] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: From 33269b0b3f236bd1655a64a0120524c0e7067d01 Mon Sep 17 00:00:00 2001 From: Florian Fendt <Florian.Fendt@bosch.io> Date: Tue, 17 Mar 2020 11:25:04 +0100 Subject: [PATCH 14/17] set ditto.version to ${revision} Signed-off-by: Florian Fendt <Florian.Fendt@bosch.io> --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index b9174cae..41284a25 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -190,7 +190,7 @@ <revision>0-SNAPSHOT</revision> <!-- ### Compile dependencies versions --> - <ditto.version>1.1.0-M2</ditto.version> + <ditto.version>${revision}</ditto.version> <minimal-json.version>0.9.5</minimal-json.version> <nv-websocket-client.version>2.9</nv-websocket-client.version> <classindex.version>3.8</classindex.version> @@ -240,7 +240,7 @@ <ditto.thirdPartyLicences.excludedGroups> (org\.eclipse\.ditto.*) </ditto.thirdPartyLicences.excludedGroups> - + <sonar.coverage.jacoco.xmlReportPaths>${project.basedir}/target/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths> </properties> From 9a67b5c4a927ae3f207927d904dc1b7e00ef1f4c Mon Sep 17 00:00:00 2001 From: stmaute <stefan.maute@bosch.io> Date: Tue, 7 Apr 2020 12:29:24 +0200 Subject: [PATCH 15/17] append ws path only if it isn't already appended; Signed-off-by: stmaute <stefan.maute@bosch.io> --- .../WebSocketMessagingConfiguration.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) 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 50cf7ae2..6b9f25ab 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 @@ -20,6 +20,8 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -81,6 +83,7 @@ private static final class WebSocketMessagingConfigurationBuilder implements Mes private static final List<String> ALLOWED_URI_SCHEME = Arrays.asList("wss", "ws"); private static final String WS_PATH = "/ws/"; + private static final String WS_URL_TEMPLATE = "/ws/%d"; private JsonSchemaVersion jsonSchemaVersion = JsonSchemaVersion.LATEST; private URI endpointUri; @@ -128,15 +131,25 @@ public MessagingConfiguration.Builder trustStoreConfiguration( @Override public MessagingConfiguration build() { - final URI wsEndpointUri = appendWsPath(this.endpointUri, jsonSchemaVersion); + final URI wsEndpointUri= appendWsPathIfNecessary(this.endpointUri, jsonSchemaVersion); return new WebSocketMessagingConfiguration(jsonSchemaVersion, wsEndpointUri, reconnectEnabled, proxyConfiguration, trustStoreConfiguration); } - private static URI appendWsPath(final URI baseUri, final JsonSchemaVersion schemaVersion) { - final String pathWithoutTrailingSlashes = baseUri.getPath().replaceFirst("/+$", ""); - final String newPath = pathWithoutTrailingSlashes + WS_PATH + schemaVersion.toString(); - return baseUri.resolve(newPath); + private static URI appendWsPathIfNecessary(final URI baseUri, final JsonSchemaVersion schemaVersion) { + if (needToAppendWsPath(baseUri, schemaVersion)) { + final String pathWithoutTrailingSlashes = baseUri.getPath().replaceFirst("/+$", ""); + final String newPath = pathWithoutTrailingSlashes + WS_PATH + schemaVersion.toString(); + return baseUri.resolve(newPath); + } else { + return baseUri; + } + } + + private static boolean needToAppendWsPath(final URI baseUri, final JsonSchemaVersion schemaVersion) { + final Pattern pattern = Pattern.compile(String.format(WS_URL_TEMPLATE, schemaVersion.toInt())); + final Matcher matcher = pattern.matcher(baseUri.toString()); + return !matcher.find(); } } From 20a7449d9f80542f092adafb3cac80fd387a6bbb Mon Sep 17 00:00:00 2001 From: stmaute <stefan.maute@bosch.io> Date: Tue, 7 Apr 2020 14:27:35 +0200 Subject: [PATCH 16/17] improved regex for ws path check; Signed-off-by: stmaute <stefan.maute@bosch.io> --- .../configuration/WebSocketMessagingConfiguration.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 6b9f25ab..64406704 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 @@ -83,7 +83,7 @@ private static final class WebSocketMessagingConfigurationBuilder implements Mes private static final List<String> ALLOWED_URI_SCHEME = Arrays.asList("wss", "ws"); private static final String WS_PATH = "/ws/"; - private static final String WS_URL_TEMPLATE = "/ws/%d"; + private static final String WS_PATH_REGEX = "/ws/(1|2)/?"; private JsonSchemaVersion jsonSchemaVersion = JsonSchemaVersion.LATEST; private URI endpointUri; @@ -137,7 +137,7 @@ public MessagingConfiguration build() { } private static URI appendWsPathIfNecessary(final URI baseUri, final JsonSchemaVersion schemaVersion) { - if (needToAppendWsPath(baseUri, schemaVersion)) { + if (needToAppendWsPath(baseUri)) { final String pathWithoutTrailingSlashes = baseUri.getPath().replaceFirst("/+$", ""); final String newPath = pathWithoutTrailingSlashes + WS_PATH + schemaVersion.toString(); return baseUri.resolve(newPath); @@ -146,8 +146,8 @@ private static URI appendWsPathIfNecessary(final URI baseUri, final JsonSchemaVe } } - private static boolean needToAppendWsPath(final URI baseUri, final JsonSchemaVersion schemaVersion) { - final Pattern pattern = Pattern.compile(String.format(WS_URL_TEMPLATE, schemaVersion.toInt())); + private static boolean needToAppendWsPath(final URI baseUri) { + final Pattern pattern = Pattern.compile(WS_PATH_REGEX); final Matcher matcher = pattern.matcher(baseUri.toString()); return !matcher.find(); } From 892c59397ed9e2ff3d2c8f1ce4561eff57bb4574 Mon Sep 17 00:00:00 2001 From: stmaute <stefan.maute@bosch.io> Date: Wed, 8 Apr 2020 09:59:42 +0200 Subject: [PATCH 17/17] check if schemaVerison and apiVersion in ws path match and throw IllegalArgumentException if not; Signed-off-by: stmaute <stefan.maute@bosch.io> --- .../WebSocketMessagingConfiguration.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) 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 64406704..849556c9 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 @@ -12,9 +12,9 @@ */ package org.eclipse.ditto.client.configuration; -import static org.eclipse.ditto.model.base.common.ConditionChecker.checkArgument; -import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; +import org.eclipse.ditto.model.base.json.JsonSchemaVersion; +import javax.annotation.Nullable; import java.net.URI; import java.text.MessageFormat; import java.util.Arrays; @@ -23,9 +23,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nullable; - -import org.eclipse.ditto.model.base.json.JsonSchemaVersion; +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkArgument; +import static org.eclipse.ditto.model.base.common.ConditionChecker.checkNotNull; /** * Provides Ditto WebSocket messaging specific configuration. @@ -138,10 +137,11 @@ public MessagingConfiguration build() { private static URI appendWsPathIfNecessary(final URI baseUri, final JsonSchemaVersion schemaVersion) { if (needToAppendWsPath(baseUri)) { - final String pathWithoutTrailingSlashes = baseUri.getPath().replaceFirst("/+$", ""); + final String pathWithoutTrailingSlashes = removeTrailingSlashFromPath(baseUri.getPath()); final String newPath = pathWithoutTrailingSlashes + WS_PATH + schemaVersion.toString(); return baseUri.resolve(newPath); } else { + checkIfBaseUriAndSchemaVersionMatch(baseUri, schemaVersion); return baseUri; } } @@ -152,6 +152,20 @@ private static boolean needToAppendWsPath(final URI baseUri) { return !matcher.find(); } + private static void checkIfBaseUriAndSchemaVersionMatch(final URI baseUri, final JsonSchemaVersion schemaVersion) { + final String path = removeTrailingSlashFromPath(baseUri.getPath()); + final String apiVersion = path.substring(path.length() - 1, path.length()); + if (!schemaVersion.toString().equals(apiVersion)) { + throw new IllegalArgumentException("The jsonSchemaVersion and apiVersion of the endpoint do not match. " + + "Either remove the ws path from the endpoint or " + + "use the same jsonSchemaVersion as in the ws path of the endpoint."); + } + } + + private static String removeTrailingSlashFromPath(final String path) { + return path.replaceFirst("/+$", ""); + } + } }