Skip to content

Commit

Permalink
enhance Ditto java client with a "disconnection listener"
Browse files Browse the repository at this point in the history
* called whenever the connection to the Ditto backend was disconnected
* provides a context of who initiated the disconnection + an optional cause
* provides means to e.g. close the client as a result or to perform a reconnect

in addition: fixed executor service creation in Ditto: by default a ScheduledExecutorService with a high "corePoolSize" was configured as default
* only use "scheduled" exector for schedluded tasks
* provide Ditto client sessionId for thread factories (to append in thread names)
* make it possible to configure user code provided "scheduled" executor

Signed-off-by: Thomas Jaeckle <thomas.jaeckle@bosch.io>
  • Loading branch information
thjaeckle committed Jul 8, 2021
1 parent 5a66866 commit 5452bc5
Show file tree
Hide file tree
Showing 25 changed files with 673 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,18 @@
* @since 1.0.0
*/
@Immutable
public final class AccessTokenAuthenticationConfiguration extends AbstractAuthenticationConfiguration {
public final class AccessTokenAuthenticationConfiguration extends AbstractAuthenticationConfiguration
implements TokenAuthenticationConfiguration {

private final String identifier;
private final JsonWebTokenSupplier jsonWebTokenSupplier;
private final Duration expiryGracePeriod;

private AccessTokenAuthenticationConfiguration(final String identifier,
final JsonWebTokenSupplier jsonWebTokenSupplier,
final Duration expiryGracePeriod,
final Map<String, String> additionalHeaders) {
super(identifier, additionalHeaders, null);
this.identifier = identifier;
this.jsonWebTokenSupplier = jsonWebTokenSupplier;
this.expiryGracePeriod = expiryGracePeriod;
private AccessTokenAuthenticationConfiguration(final AccessTokenAuthenticationConfigurationBuilder builder) {
super(builder.identifier, builder.additionalHeaders, builder.proxyConfiguration);
this.identifier = builder.identifier;
this.jsonWebTokenSupplier = checkNotNull(builder.jsonWebTokenSupplier, "jsonWebTokenSupplier");
this.expiryGracePeriod = checkNotNull(builder.expiryGracePeriod, "expiryGracePeriod");
}

/**
Expand All @@ -63,20 +61,12 @@ public String getIdentifier() {
return identifier;
}

/**
* Returns the access token supplier.
*
* @return the supplier.
*/
@Override
public JsonWebTokenSupplier getJsonWebTokenSupplier() {
return jsonWebTokenSupplier;
}

/**
* Returns the grace period which will be subtracted from token expiry to trigger the configured token supplier.
*
* @return the grace period.
*/
@Override
public Duration getExpiryGracePeriod() {
return expiryGracePeriod;
}
Expand Down Expand Up @@ -108,7 +98,7 @@ public String toString() {
return getClass().getSimpleName() + " [" +
super.toString() +
", identifier=" + identifier +
", accessTokenSupplier=" + jsonWebTokenSupplier +
", jsonWebTokenSupplier=" + jsonWebTokenSupplier +
", expiryGracePeriod=" + expiryGracePeriod +
"]";
}
Expand All @@ -124,6 +114,7 @@ public static class AccessTokenAuthenticationConfigurationBuilder
private String identifier;
private JsonWebTokenSupplier jsonWebTokenSupplier;
private Duration expiryGracePeriod = DEFAULT_EXPIRY_GRACE_PERIOD;
@Nullable private ProxyConfiguration proxyConfiguration;

/**
* Sets the identifier to authenticate.
Expand Down Expand Up @@ -168,14 +159,15 @@ public AccessTokenAuthenticationConfigurationBuilder withAdditionalHeader(final

@Override
public AccessTokenAuthenticationConfigurationBuilder proxyConfiguration(
final ProxyConfiguration proxyConfiguration) {
@Nullable final ProxyConfiguration proxyConfiguration) {

this.proxyConfiguration = proxyConfiguration;
return this;
}

@Override
public AccessTokenAuthenticationConfiguration build() {
return new AccessTokenAuthenticationConfiguration(identifier, jsonWebTokenSupplier, expiryGracePeriod,
additionalHeaders);
return new AccessTokenAuthenticationConfiguration(this);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,23 @@
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;

import org.eclipse.ditto.client.messaging.JsonWebTokenSupplier;

/**
* A {@link org.eclipse.ditto.client.configuration.AuthenticationConfiguration} for OAuth 2 client credentials
* authentication.
*
* @since 1.0.0
*/
@Immutable
public final class ClientCredentialsAuthenticationConfiguration extends AbstractAuthenticationConfiguration {
public final class ClientCredentialsAuthenticationConfiguration extends AbstractAuthenticationConfiguration
implements TokenAuthenticationConfiguration {

private final String tokenEndpoint;
private final String clientId;
private final String clientSecret;
private final List<String> scopes;
private final JsonWebTokenSupplier jsonWebTokenSupplier;
private final Duration expiryGracePeriod;

public ClientCredentialsAuthenticationConfiguration(
Expand All @@ -50,6 +54,7 @@ public ClientCredentialsAuthenticationConfiguration(
clientId = checkNotNull(builder.clientId, "clientId");
clientSecret = checkNotNull(builder.clientSecret, "clientSecret");
scopes = Collections.unmodifiableList(new ArrayList<>(builder.scopes));
jsonWebTokenSupplier = checkNotNull(builder.jsonWebTokenSupplier, "jsonWebTokenSupplier");
expiryGracePeriod = checkNotNull(builder.expiryGracePeriod, "expiryGracePeriod");
}

Expand Down Expand Up @@ -96,11 +101,12 @@ public Collection<String> getScopes() {
return scopes;
}

/**
* Returns the expiry grace period.
*
* @return the period.
*/
@Override
public JsonWebTokenSupplier getJsonWebTokenSupplier() {
return jsonWebTokenSupplier;
}

@Override
public Duration getExpiryGracePeriod() {
return expiryGracePeriod;
}
Expand All @@ -121,12 +127,14 @@ public boolean equals(@Nullable final Object o) {
Objects.equals(clientId, that.clientId) &&
Objects.equals(clientSecret, that.clientSecret) &&
Objects.equals(scopes, that.scopes) &&
Objects.equals(jsonWebTokenSupplier, that.jsonWebTokenSupplier) &&
Objects.equals(expiryGracePeriod, that.expiryGracePeriod);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), tokenEndpoint, clientId, clientSecret, scopes, expiryGracePeriod);
return Objects.hash(super.hashCode(), tokenEndpoint, clientId, clientSecret, scopes, jsonWebTokenSupplier,
expiryGracePeriod);
}

@Override
Expand All @@ -137,6 +145,7 @@ public String toString() {
", clientId=" + clientId +
", clientSecret=" + clientSecret +
", scopes=" + scopes +
", jsonWebTokenSupplier=" + jsonWebTokenSupplier +
", expiryGracePeriod=" + expiryGracePeriod +
"]";
}
Expand All @@ -151,14 +160,14 @@ public static final class ClientCredentialsAuthenticationConfigurationBuilder
private String clientId;
private String clientSecret;
private Collection<String> scopes;
private JsonWebTokenSupplier jsonWebTokenSupplier;
private Duration expiryGracePeriod;
@Nullable private ProxyConfiguration proxyConfiguration;
private final Map<String, String> additionalHeaders;

private ClientCredentialsAuthenticationConfigurationBuilder() {
scopes = Collections.emptyList();
expiryGracePeriod = DEFAULT_EXPIRY_GRACE_PERIOD;
proxyConfiguration = null;
additionalHeaders = new HashMap<>();
}

Expand Down Expand Up @@ -207,6 +216,18 @@ public ClientCredentialsAuthenticationConfigurationBuilder scopes(final Collecti
return this;
}

/**
* Sets the access token supplier to authenticate.
*
* @param jsonWebTokenSupplier the supplier.
* @return this builder.
*/
public ClientCredentialsAuthenticationConfigurationBuilder accessTokenSupplier(
final JsonWebTokenSupplier jsonWebTokenSupplier) {
this.jsonWebTokenSupplier = jsonWebTokenSupplier;
return this;
}

/**
* Sets the expiry grace period.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2021 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.configuration;

import java.util.Optional;

/**
* Context provided to registered {@code disconnectedListener}s provided when building {@code MessagingConfiguration}
* of a new Ditto client instance.
*
* @since 2.1.0
*/
public interface DisconnectedContext {

/**
* Returns the {@link Source} providing the source of the disconnect.
*
* @return the source of the disonnect.
*/
Source getSource();

/**
* Provides the optional cause (e.g. the last {@code DittoRuntimeException} or a websocket exception) before the
* disconnect.
*
* @return the optional cause of the disconnect.
*/
Optional<Throwable> getCause();

/**
* Returns a {@link DisconnectionHandler} providing means to either destroy the client channel, prevent an automatic
* reconnect or to explicitly perform a reconnect.
*
* @return the disconnection handler providing options how to handle the disconnect.
*/
DisconnectionHandler handleDisconnect();

/**
* An enumeration of the possible sources which initiated a disconnect.
*/
enum Source {
/**
* The server closed the connection, potentially providing a cause before
* (sent as {@code DittoRuntimeException} wrapped in an error {@code Adaptable} before).
*/
SERVER,

/**
* The client closed the connection - without request by the user code.
* This can e.g. happen if:
* <ul>
* <li>authentication at the Ditto backend failed</li>
* <li>an initialization or network error occurred (e.g. unknown/unresolvable host, proxy problems)</li>
* <li>the WebSocket handshake with the provided Ditto backend endpoint failed</li>
* </ul>
*/
CLIENT,

/**
* The user code explicitly disconnected.
*/
USER_CODE
}

/**
* A handler provided to the user code providing options to handle an encountered disconnect.
*/
interface DisconnectionHandler {

/**
* Closes the underlying channel (e.g. twin, live or policies channel) including their
* {@code MessagingProvider}.
*
* @return this instance for chaining.
*/
DisconnectionHandler closeChannel();

/**
* Prevents the client from doing a (configured) automatic reconnect upon disconnection.
*
* @param preventReconnect whether to prevent the automatic reconnect or not.
* @return this instance for chaining.
*/
DisconnectionHandler preventConfiguredReconnect(boolean preventReconnect);

/**
* Performs a reconnect independent from the configured automatic reconnect upon disconnection.
* Can e.g. be used to perform a reconnect only for certain disconnection sources or causes.
*
* @return this instance for chaining.
*/
DisconnectionHandler performReconnect();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ public interface MessagingConfiguration {
*/
Optional<Consumer<Throwable>> getConnectionErrorHandler();

/**
* Returns the disconnected listener.
*
* @return the disconnected listener or an empty optional.
*/
Optional<Consumer<DisconnectedContext>> getDisconnectedListener();

/**
* Builder for creating an instance of {@code MessagingConfiguration} by utilizing Object Scoping and Method
* Chaining.
Expand Down Expand Up @@ -177,7 +184,15 @@ interface Builder {
* @param handler the handler that will be called with the cause of the connection error.
* @since 1.2.0
*/
Builder connectionErrorHandler(@Nullable final Consumer<Throwable> handler);
Builder connectionErrorHandler(@Nullable Consumer<Throwable> handler);

/**
* Register a contextListener which is notified whenever the connection is disconnected.
*
* @param contextListener the handler that will be called with details about the disconnection.
* @since 2.1.0
*/
Builder disconnectedListener(@Nullable Consumer<DisconnectedContext> contextListener);

/**
* Creates a new instance of {@code MessagingConfiguration}.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 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.configuration;

import java.time.Duration;

import org.eclipse.ditto.client.messaging.JsonWebTokenSupplier;

/**
* Additional JWT / Token specific configurations.
*
* @since 2.1.0
*/
public interface TokenAuthenticationConfiguration extends AuthenticationConfiguration {

/**
* Returns the access token supplier.
*
* @return the supplier.
*/
JsonWebTokenSupplier getJsonWebTokenSupplier();

/**
* Returns the grace period which will be subtracted from token expiry to trigger the configured token supplier.
*
* @return the grace period.
*/
Duration getExpiryGracePeriod();
}
Loading

0 comments on commit 5452bc5

Please sign in to comment.