From 9b35f06a701a9a7e9dd8a6bcf101cf9280a49828 Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Tue, 14 May 2019 10:51:28 -0700 Subject: [PATCH 01/22] Port changes for RBAC to central repo --- eventhubs/data-plane/azure-eventhubs/pom.xml | 8 + .../AzureActiveDirectoryTokenProvider.java | 60 +++++++ .../azure/eventhubs/EventHubClient.java | 142 ++++++++++++++++ .../eventhubs/EventHubClientOptions.java | 39 +++++ .../azure/eventhubs/ITokenProvider.java | 11 ++ .../ManagedServiceIdentityTokenProvider.java | 150 +++++++++++++++++ .../azure/eventhubs/SecurityToken.java | 30 ++++ .../azure/eventhubs/impl/AmqpUtil.java | 5 + .../azure/eventhubs/impl/CBSChannel.java | 44 ++++- .../azure/eventhubs/impl/ClientConstants.java | 1 + .../eventhubs/impl/EventHubClientImpl.java | 126 ++++++++------ .../azure/eventhubs/impl/MessageReceiver.java | 128 +++++++------- .../azure/eventhubs/impl/MessageSender.java | 78 +++++---- .../eventhubs/impl/MessagingFactory.java | 156 +++++++++++++++--- .../SharedAccessSignatureTokenProvider.java | 33 +++- 15 files changed, 809 insertions(+), 202 deletions(-) create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ITokenProvider.java create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java diff --git a/eventhubs/data-plane/azure-eventhubs/pom.xml b/eventhubs/data-plane/azure-eventhubs/pom.xml index 6f567e713080..a1ae860e51d4 100644 --- a/eventhubs/data-plane/azure-eventhubs/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs/pom.xml @@ -30,4 +30,12 @@ scm:git:https://github.com/Azure/azure-sdk-for-java + + + com.microsoft.azure + adal4j + 1.6.4 + + + diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java new file mode 100644 index 000000000000..36865908fa0a --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.azure.eventhubs; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +import com.microsoft.aad.adal4j.AuthenticationCallback; +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.azure.eventhubs.impl.ClientConstants; + +public final class AzureActiveDirectoryTokenProvider implements ITokenProvider { + public final static String EVENTHUBS_REGISTERED_AUDIENCE = "https://eventhubs.azure.net/"; + + private final AuthenticationContext authenticationContext; + private final ITokenAcquirer tokenAcquirer; + + public AzureActiveDirectoryTokenProvider( + final AuthenticationContext authenticationContext, + final ITokenAcquirer tokenAcquirer) { + this.authenticationContext = authenticationContext; + this.tokenAcquirer = tokenAcquirer; + } + + @Override + public CompletableFuture getToken(String resource, Duration timeout) { + final CompletableFuture result = new CompletableFuture<>(); + this.tokenAcquirer.acquireToken(this.authenticationContext, new EventHubsAuthenticationCallback(result)); + return result; + } + + public static class EventHubsAuthenticationCallback implements AuthenticationCallback { + final CompletableFuture result; + + public EventHubsAuthenticationCallback(final CompletableFuture result) { + this.result = result; + } + + @Override + public void onSuccess(AuthenticationResult authenticationResult) { + this.result.complete(new SecurityToken(ClientConstants.JWT_TOKEN_TYPE, + authenticationResult.getAccessToken(), + authenticationResult.getExpiresOnDate())); + } + + @Override + public void onFailure(Throwable throwable) { + this.result.completeExceptionally(throwable); + } + } + + public interface ITokenAcquirer { + Future acquireToken( + final AuthenticationContext authenticationContext, + final AuthenticationCallback authenticationCallback); + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java index ea49239ed07e..d3402af96765 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java @@ -3,12 +3,19 @@ package com.microsoft.azure.eventhubs; +import com.microsoft.aad.adal4j.AsymmetricKeyCredential; +import com.microsoft.aad.adal4j.AuthenticationCallback; +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; import com.microsoft.azure.eventhubs.impl.EventHubClientImpl; import com.microsoft.azure.eventhubs.impl.ExceptionUtil; import java.io.IOException; +import java.net.URI; import java.nio.channels.UnresolvedAddressException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; /** @@ -65,6 +72,141 @@ static CompletableFuture create(final String connectionString, f return EventHubClient.create(connectionString, null, executor); } + /** + * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. + * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. + *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. + * + * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName + * @param eventHubName EventHub name + * @param tokenProvider The {@link ITokenProvider} implementation to be used to authenticate + * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. + * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. + * @return EventHubClient which can be used to create Senders and Receivers to EventHub + * @throws EventHubException If the EventHubs service encountered problems during connection creation. + * @throws IOException If the underlying Proton-J layer encounter network errors. + */ + public static CompletableFuture create( + final URI endpointAddress, + final String eventHubName, + final ITokenProvider tokenProvider, + final ScheduledExecutorService executor, + final EventHubClientOptions options) throws EventHubException, IOException { + EventHubClientOptions effectiveOptions = (options != null) ? options : new EventHubClientOptions(); + return EventHubClientImpl.create(endpointAddress, eventHubName, tokenProvider, executor, effectiveOptions); + } + + /** + * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. + * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. + *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. + * + * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName + * @param eventHubName EventHub name + * @param authenticationContext The Azure Active Directory {@link AuthenticationContext} + * @param clientCredential The Azure Active Directory {@link ClientCredential} + * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. + * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. + * @return EventHubClient which can be used to create Senders and Receivers to EventHub + * @throws EventHubException If the EventHubs service encountered problems during connection creation. + * @throws IOException If the underlying Proton-J layer encounter network errors. + */ + public static CompletableFuture create( + final URI endpointAddress, + final String eventHubName, + final AuthenticationContext authenticationContext, + final ClientCredential clientCredential, + final ScheduledExecutorService executor, + final EventHubClientOptions options) throws EventHubException, IOException { + if (authenticationContext == null) { + throw new IllegalArgumentException("authenticationContext cannot be null"); + } + if (clientCredential == null) { + throw new IllegalArgumentException("clientCredential cannot be null"); + } + + ITokenProvider tokenProvider = new AzureActiveDirectoryTokenProvider( + authenticationContext, + new AzureActiveDirectoryTokenProvider.ITokenAcquirer() { + @Override + public Future acquireToken( + final AuthenticationContext authenticationContext, + final AuthenticationCallback authenticationCallback) { + return authenticationContext.acquireToken( + AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE, + clientCredential, + authenticationCallback); + } + }); + return create(endpointAddress, eventHubName, tokenProvider, executor, options); + } + + /** + * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. + * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. + *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. + * + * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName + * @param eventHubName EventHub name + * @param authenticationContext The Azure Active Directory {@link AuthenticationContext} + * @param credential The Azure Active Directory {@link AsymmetricKeyCredential} + * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. + * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. + * @return EventHubClient which can be used to create Senders and Receivers to EventHub + * @throws EventHubException If the EventHubs service encountered problems during connection creation. + * @throws IOException If the underlying Proton-J layer encounter network errors. + */ + public static CompletableFuture create( + final URI endpointAddress, + final String eventHubName, + final AuthenticationContext authenticationContext, + final AsymmetricKeyCredential credential, + final ScheduledExecutorService executor, + final EventHubClientOptions options) throws EventHubException, IOException { + if (authenticationContext == null) { + throw new IllegalArgumentException("authenticationContext cannot be null"); + } + if (credential == null) { + throw new IllegalArgumentException("credential cannot be null"); + } + + ITokenProvider tokenProvider = new AzureActiveDirectoryTokenProvider( + authenticationContext, + new AzureActiveDirectoryTokenProvider.ITokenAcquirer() { + @Override + public Future acquireToken( + final AuthenticationContext authenticationContext, + final AuthenticationCallback authenticationCallback) { + return authenticationContext.acquireToken( + AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE, + credential, + authenticationCallback); + } + }); + return create(endpointAddress, eventHubName, tokenProvider, executor, options); + } + + /** + * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. + * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. + *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. + * + * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName + * @param eventHubName EventHub name + * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. + * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. + * @return EventHubClient which can be used to create Senders and Receivers to EventHub + * @throws EventHubException If the EventHubs service encountered problems during connection creation. + * @throws IOException If the underlying Proton-J layer encounter network errors. + */ + public static CompletableFuture createWithManagedServiceIdentity( + final URI endpointAddress, + final String eventHubName, + final ScheduledExecutorService executor, + final EventHubClientOptions options) throws EventHubException, IOException { + return create(endpointAddress, eventHubName, new ManagedServiceIdentityTokenProvider(), executor, options); + } + /** * Factory method to create an instance of {@link EventHubClient} using the supplied connectionString. * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java new file mode 100644 index 000000000000..c7ce62faa497 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.azure.eventhubs; + +import java.time.Duration; + +public class EventHubClientOptions { + private Duration operationTimeout = null; + private TransportType transportType = null; + private RetryPolicy retryPolicy = null; + + public EventHubClientOptions() { + } + + public void setOperationTimeout(Duration operationTimeout) { + this.operationTimeout = operationTimeout; + } + + public Duration getOperationTimeout() { + return this.operationTimeout; + } + + public void setTransportType(TransportType transportType) { + this.transportType = transportType; + } + + public TransportType getTransportType() { + return this.transportType; + } + + public void setRetryPolicy(RetryPolicy retryPolicy) { + this.retryPolicy = retryPolicy; + } + + public RetryPolicy getRetryPolicy() { + return this.retryPolicy; + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ITokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ITokenProvider.java new file mode 100644 index 000000000000..21c668711ccc --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ITokenProvider.java @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.azure.eventhubs; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +public interface ITokenProvider { + CompletableFuture getToken(final String resource, final Duration timeout); +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java new file mode 100644 index 000000000000..db76e20fe533 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java @@ -0,0 +1,150 @@ +package com.microsoft.azure.eventhubs; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +import com.google.gson.Gson; +import com.microsoft.azure.eventhubs.impl.ClientConstants; +import com.microsoft.azure.eventhubs.impl.StringUtil; + +public class ManagedServiceIdentityTokenProvider implements ITokenProvider { + static final String LOCAL_VM_REST_MSI_ENDPOINT_URL = "http://localhost:50342/oauth2/token"; + static final String LOCAL_VM_MSI_URL_FORMAT = "%s?resource=%s"; + static final String LOCAL_VM_MSI_URL = String.format(LOCAL_VM_MSI_URL_FORMAT, + LOCAL_VM_REST_MSI_ENDPOINT_URL, + AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE); + static final String METADATA_HEADER_NAME = "Metadata"; + + static final String API_VERSION = "api-version=2017-09-01"; + static final String MSI_SECRET_ENV_VARIABLE = "MSI_SECRET"; + static final String MSI_ENDPOINT_ENV_VARIABLE = "MSI_ENDPOINT"; + static final String DYNAMIC_MSI_URL_FORMAT = "%s?resource=%s&%s"; + static final String SECRET_HEADER_NAME = "Secret"; + + @Override + public CompletableFuture getToken( + final String resource, + final Duration timeout) { + + final CompletableFuture getTokenTask = new CompletableFuture<>(); + + final String dynamicMsiEndpointUrl = System.getenv(MSI_ENDPOINT_ENV_VARIABLE); + final String msiSecret = System.getenv(MSI_SECRET_ENV_VARIABLE); + + final boolean useDynamicMsiUrl; + + if (StringUtil.isNullOrEmpty(dynamicMsiEndpointUrl) || StringUtil.isNullOrEmpty(msiSecret)) { + useDynamicMsiUrl = false; + } + else { + useDynamicMsiUrl = true; + } + + final URL msiUrl; + try { + msiUrl = useDynamicMsiUrl + ? new URL(String.format(DYNAMIC_MSI_URL_FORMAT, dynamicMsiEndpointUrl, AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE, API_VERSION)) + : new URL(LOCAL_VM_MSI_URL); + } catch (MalformedURLException malformedUrlException) { + getTokenTask.completeExceptionally(malformedUrlException); + return getTokenTask; + } + + final HttpURLConnection httpConnection; + try { + httpConnection = (HttpURLConnection) msiUrl.openConnection(); + }catch (IOException ioException) { + getTokenTask.completeExceptionally(ioException); + return getTokenTask; + } + + if (useDynamicMsiUrl) { + httpConnection.setRequestProperty(SECRET_HEADER_NAME, msiSecret); + } + else { + httpConnection.setRequestProperty(METADATA_HEADER_NAME, "true"); + } + + try { + httpConnection.setRequestMethod("GET"); + } catch(ProtocolException protocolException) { + getTokenTask.completeExceptionally(protocolException); + return getTokenTask; + } + + httpConnection.setDoInput(true); + + try { + httpConnection.connect(); + } catch (IOException ioException) { + getTokenTask.completeExceptionally(ioException); + return getTokenTask; + } + + try { + final StringBuilder responseBuilder = new StringBuilder(); + try (Reader reader = new InputStreamReader(httpConnection.getInputStream(), StandardCharsets.UTF_8)) { + final char[] buffer = new char[1024]; + int numBytesRead = -1; + while ((numBytesRead = reader.read(buffer)) != -1) { + responseBuilder.append(buffer, 0, numBytesRead); + } + } catch (IOException ioException) { + getTokenTask.completeExceptionally(ioException); + return getTokenTask; + } + + final Gson gson = new Gson(); + final MSIToken token = gson.fromJson(responseBuilder.toString(), MSIToken.class); + + getTokenTask.complete(new SecurityToken(ClientConstants.JWT_TOKEN_TYPE, token.access_token, new Date(token.expires_on))); + return getTokenTask; + }finally { + httpConnection.disconnect(); + } + } + + private static class MSIToken + { + private String access_token; + private String refresh_token; + // Token validity in number of seconds + private int expires_in; + // Seconds from 1970-01-01T0:0:0Z UTC when the token will expire + private long expires_on; + // Seconds from 1970-01-01T0:0:0Z UTC after which the token takes effect + private long not_before; + // Resource for which token is requested + private String resource; + // Token type + private String token_type; + + public String getAccessToken() { + return access_token; + } + public int getExpiresIn() { + return expires_in; + } + public long getExpiresOn() { + return expires_on; + } + public long getNotBefore() { + return not_before; + } + public String getResource() { + return resource; + } + public String getTokenType() { + return token_type; + } + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java new file mode 100644 index 000000000000..13a68a2b6962 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.azure.eventhubs; + +import java.util.Date; + +public final class SecurityToken { + private final String tokenType; + private final String token; + private final Date validTo; + + public SecurityToken(final String tokenType, final String token, final Date validTo) { + this.tokenType = tokenType; + this.token = token; + this.validTo = validTo; + } + + public String getTokenType() { + return this.tokenType; + } + + public String getToken() { + return this.token; + } + + public Date validTo() { + return this.validTo; + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/AmqpUtil.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/AmqpUtil.java index e4b1e9a6e2ff..ec97dc294ccb 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/AmqpUtil.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/AmqpUtil.java @@ -11,6 +11,7 @@ import org.apache.qpid.proton.amqp.messaging.MessageAnnotations; import org.apache.qpid.proton.message.Message; +import java.util.Date; import java.util.Locale; public final class AmqpUtil { @@ -121,6 +122,10 @@ private static int sizeof(Object obj) { return Double.BYTES; } + if (obj instanceof Date) { + return 32; + } + throw new IllegalArgumentException(String.format(Locale.US, "Encoding Type: %s is not supported", obj.getClass())); } } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/CBSChannel.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/CBSChannel.java index 4dcfcd6c5c38..c7e3f073715c 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/CBSChannel.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/CBSChannel.java @@ -8,18 +8,24 @@ import org.apache.qpid.proton.amqp.messaging.ApplicationProperties; import org.apache.qpid.proton.message.Message; +import com.microsoft.azure.eventhubs.SecurityToken; + import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; final class CBSChannel { - + final ScheduledExecutorService executor; final FaultTolerantObject innerChannel; CBSChannel( final SessionProvider sessionProvider, final AmqpConnection connection, - final String clientId) { - + final String clientId, + final ScheduledExecutorService executor) { + this.executor = executor; RequestResponseCloser closer = new RequestResponseCloser(); this.innerChannel = new FaultTolerantObject<>( new RequestResponseOpener(sessionProvider, clientId, "cbs-session", "cbs", ClientConstants.CBS_ADDRESS, connection), @@ -29,27 +35,47 @@ final class CBSChannel { public void sendToken( final ReactorDispatcher dispatcher, - final String token, + final CompletableFuture tokenFuture, final String tokenAudience, - final OperationResult sendTokenCallback) { + final OperationResult sendTokenCallback, + final Consumer errorCallback) { + tokenFuture.thenAcceptAsync((token) -> { + innerSendToken(dispatcher, token, tokenAudience, sendTokenCallback); + }, this.executor) + .whenCompleteAsync((empty, exception) -> { + // TODO: whenCompleteAsync presents a Throwable. But many of the error callbacks expect + // an Exception. For now, do a cast here. Will we ever actually get an error that is + // not an Exception? + if ((exception != null) && (exception instanceof Exception)) { + errorCallback.accept((Exception)exception); + } + }, this.executor); + } + private void innerSendToken( + final ReactorDispatcher dispatcher, + final SecurityToken token, + final String tokenAudience, + final OperationResult sendTokenCallback) { final Message request = Proton.message(); final Map properties = new HashMap<>(); properties.put(ClientConstants.PUT_TOKEN_OPERATION, ClientConstants.PUT_TOKEN_OPERATION_VALUE); - properties.put(ClientConstants.PUT_TOKEN_TYPE, ClientConstants.SAS_TOKEN_TYPE); + properties.put(ClientConstants.PUT_TOKEN_TYPE, token.getTokenType()); + properties.put(ClientConstants.PUT_TOKEN_EXPIRY, token.validTo()); properties.put(ClientConstants.PUT_TOKEN_AUDIENCE, tokenAudience); + final ApplicationProperties applicationProperties = new ApplicationProperties(properties); request.setApplicationProperties(applicationProperties); - request.setBody(new AmqpValue(token)); + request.setBody(new AmqpValue(token.getToken())); final MessageOperationResult messageOperation = new MessageOperationResult(response -> sendTokenCallback.onComplete(null), sendTokenCallback::onError); final OperationResultBase operation = new OperationResultBase<>( result -> result.request(request, messageOperation), sendTokenCallback::onError); - + this.innerChannel.runOnOpenedObject(dispatcher, operation); } - + public void close( final ReactorDispatcher reactorDispatcher, final OperationResult closeCallback) { diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java index 0cd1b70972cf..ded3ce6da5cb 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java @@ -45,6 +45,7 @@ public final class ClientConstants { public static final String PUT_TOKEN_OPERATION_VALUE = "put-token"; public static final String PUT_TOKEN_TYPE = "type"; public static final String SAS_TOKEN_TYPE = "servicebus.windows.net:sastoken"; + public static final String JWT_TOKEN_TYPE = "jwt"; public static final String PUT_TOKEN_AUDIENCE = "name"; public static final String PUT_TOKEN_EXPIRY = "expiration"; public static final String PUT_TOKEN_STATUS_CODE = "status-code"; diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java index a352172bb1dc..0711dc397b4e 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java @@ -8,17 +8,21 @@ import com.microsoft.azure.eventhubs.EventData; import com.microsoft.azure.eventhubs.EventDataBatch; import com.microsoft.azure.eventhubs.EventHubClient; +import com.microsoft.azure.eventhubs.EventHubClientOptions; import com.microsoft.azure.eventhubs.EventHubException; import com.microsoft.azure.eventhubs.EventHubRuntimeInformation; import com.microsoft.azure.eventhubs.EventPosition; +import com.microsoft.azure.eventhubs.ITokenProvider; import com.microsoft.azure.eventhubs.OperationCancelledException; import com.microsoft.azure.eventhubs.PartitionReceiver; import com.microsoft.azure.eventhubs.PartitionRuntimeInformation; import com.microsoft.azure.eventhubs.PartitionSender; import com.microsoft.azure.eventhubs.ReceiverOptions; import com.microsoft.azure.eventhubs.RetryPolicy; +import com.microsoft.azure.eventhubs.impl.MessagingFactory.MessagingFactoryBuilder; import java.io.IOException; +import java.net.URI; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Duration; @@ -49,18 +53,25 @@ public final class EventHubClientImpl extends ClientEntity implements EventHubCl private CompletableFuture createSender; - private EventHubClientImpl(final ConnectionStringBuilder connectionString, final ScheduledExecutorService executor) { + private EventHubClientImpl(final String eventHubName, final ScheduledExecutorService executor) { super(StringUtil.getRandomString("EC"), null, executor); - this.eventHubName = connectionString.getEventHubName(); + this.eventHubName = eventHubName; this.senderCreateSync = new Object(); } public static CompletableFuture create( final String connectionString, final RetryPolicy retryPolicy, final ScheduledExecutorService executor) throws IOException { + if (StringUtil.isNullOrWhiteSpace(connectionString)) { + throw new IllegalArgumentException("Connection string cannot be null or empty"); + } + if (executor == null) { + throw new IllegalArgumentException("Executor cannot be null"); + } + final ConnectionStringBuilder connStr = new ConnectionStringBuilder(connectionString); - final EventHubClientImpl eventHubClient = new EventHubClientImpl(connStr, executor); + final EventHubClientImpl eventHubClient = new EventHubClientImpl(connStr.getEventHubName(), executor); return MessagingFactory.createFromConnectionString(connectionString, retryPolicy, executor) .thenApplyAsync(new Function() { @@ -73,6 +84,42 @@ public EventHubClient apply(MessagingFactory factory) { }, executor); } + public static CompletableFuture create( + final URI endpoint, + final String eventHubName, + final ITokenProvider tokenProvider, + final ScheduledExecutorService executor, + final EventHubClientOptions options) throws IOException { + if (StringUtil.isNullOrWhiteSpace(endpoint.getHost())) { + throw new IllegalArgumentException("Endpoint must contain a hostname"); + } + if (StringUtil.isNullOrWhiteSpace(eventHubName)) { + throw new IllegalArgumentException("Event hub name cannot be null or empty"); + } + if (tokenProvider == null) { + throw new IllegalArgumentException("Token provider cannot be null"); + } + if (options == null) { + throw new IllegalArgumentException("Options cannot be null"); + } + + final EventHubClientImpl eventHubClient = new EventHubClientImpl(eventHubName, executor); + final MessagingFactoryBuilder builder = new MessagingFactoryBuilder(endpoint.getHost(), tokenProvider, executor). + setOperationTimeout(options.getOperationTimeout()). + setTransportType(options.getTransportType()). + setRetryPolicy(options.getRetryPolicy()); + + return builder.build() + .thenApplyAsync(new Function() { + @Override + public EventHubClient apply(MessagingFactory factory) { + eventHubClient.underlyingFactory = factory; + eventHubClient.timer = new Timer(factory); + return eventHubClient; + } + }, executor); + } + public String getEventHubName() { return eventHubName; } @@ -250,38 +297,24 @@ public void accept(MessageSender a) { @Override public CompletableFuture getRuntimeInformation() { - CompletableFuture future1 = null; - throwIfClosed(); Map request = new HashMap(); request.put(ClientConstants.MANAGEMENT_ENTITY_TYPE_KEY, ClientConstants.MANAGEMENT_EVENTHUB_ENTITY_TYPE); request.put(ClientConstants.MANAGEMENT_ENTITY_NAME_KEY, this.eventHubName); request.put(ClientConstants.MANAGEMENT_OPERATION_KEY, ClientConstants.READ_OPERATION_VALUE); - future1 = this.addManagementToken(request); - - if (future1 == null) { - future1 = managementWithRetry(request).thenComposeAsync(new Function, CompletableFuture>() { - @Override - public CompletableFuture apply(Map rawdata) { - CompletableFuture future2 = new CompletableFuture(); - future2.complete(new EventHubRuntimeInformation( + return addManagementToken(request).thenComposeAsync((requestWithToken) -> managementWithRetry(requestWithToken), this.executor). + thenApplyAsync((rawdata) -> { + return new EventHubRuntimeInformation( (String) rawdata.get(ClientConstants.MANAGEMENT_ENTITY_NAME_KEY), ((Date) rawdata.get(ClientConstants.MANAGEMENT_RESULT_CREATED_AT)).toInstant(), (int) rawdata.get(ClientConstants.MANAGEMENT_RESULT_PARTITION_COUNT), - (String[]) rawdata.get(ClientConstants.MANAGEMENT_RESULT_PARTITION_IDS))); - return future2; - } - }, this.executor); - } - - return future1; + (String[]) rawdata.get(ClientConstants.MANAGEMENT_RESULT_PARTITION_IDS)); + }, this.executor); } @Override public CompletableFuture getPartitionRuntimeInformation(String partitionId) { - CompletableFuture future1 = null; - throwIfClosed(); Map request = new HashMap(); @@ -289,40 +322,25 @@ public CompletableFuture getPartitionRuntimeInforma request.put(ClientConstants.MANAGEMENT_ENTITY_NAME_KEY, this.eventHubName); request.put(ClientConstants.MANAGEMENT_PARTITION_NAME_KEY, partitionId); request.put(ClientConstants.MANAGEMENT_OPERATION_KEY, ClientConstants.READ_OPERATION_VALUE); - future1 = this.addManagementToken(request); - - if (future1 == null) { - future1 = managementWithRetry(request).thenComposeAsync(new Function, CompletableFuture>() { - @Override - public CompletableFuture apply(Map rawData) { - CompletableFuture future2 = new CompletableFuture(); - future2.complete(new PartitionRuntimeInformation( - (String) rawData.get(ClientConstants.MANAGEMENT_ENTITY_NAME_KEY), - (String) rawData.get(ClientConstants.MANAGEMENT_PARTITION_NAME_KEY), - (long) rawData.get(ClientConstants.MANAGEMENT_RESULT_BEGIN_SEQUENCE_NUMBER), - (long) rawData.get(ClientConstants.MANAGEMENT_RESULT_LAST_ENQUEUED_SEQUENCE_NUMBER), - (String) rawData.get(ClientConstants.MANAGEMENT_RESULT_LAST_ENQUEUED_OFFSET), - ((Date) rawData.get(ClientConstants.MANAGEMENT_RESULT_LAST_ENQUEUED_TIME_UTC)).toInstant(), - (boolean) rawData.get(ClientConstants.MANAGEMENT_RESULT_PARTITION_IS_EMPTY))); - return future2; - } - }, this.executor); - } - - return future1; + return addManagementToken(request).thenComposeAsync((requestWithToken) -> managementWithRetry(requestWithToken), this.executor). + thenApplyAsync((rawdata) -> { + return new PartitionRuntimeInformation( + (String) rawdata.get(ClientConstants.MANAGEMENT_ENTITY_NAME_KEY), + (String) rawdata.get(ClientConstants.MANAGEMENT_PARTITION_NAME_KEY), + (long) rawdata.get(ClientConstants.MANAGEMENT_RESULT_BEGIN_SEQUENCE_NUMBER), + (long) rawdata.get(ClientConstants.MANAGEMENT_RESULT_LAST_ENQUEUED_SEQUENCE_NUMBER), + (String) rawdata.get(ClientConstants.MANAGEMENT_RESULT_LAST_ENQUEUED_OFFSET), + ((Date) rawdata.get(ClientConstants.MANAGEMENT_RESULT_LAST_ENQUEUED_TIME_UTC)).toInstant(), + (boolean) rawdata.get(ClientConstants.MANAGEMENT_RESULT_PARTITION_IS_EMPTY)); + }, this.executor); } - private CompletableFuture addManagementToken(Map request) { - CompletableFuture retval = null; - try { - String audience = String.format(Locale.US, "amqp://%s/%s", this.underlyingFactory.getHostName(), this.eventHubName); - String token = this.underlyingFactory.getTokenProvider().getToken(audience, ClientConstants.TOKEN_REFRESH_INTERVAL); - request.put(ClientConstants.MANAGEMENT_SECURITY_TOKEN_KEY, token); - } catch (InvalidKeyException | NoSuchAlgorithmException | IOException e) { - retval = new CompletableFuture(); - retval.completeExceptionally(e); - } - return retval; + private CompletableFuture> addManagementToken(Map request) { + String audience = String.format(Locale.US, "amqp://%s/%s", this.underlyingFactory.getHostName(), this.eventHubName); + return this.underlyingFactory.getTokenProvider().getToken(audience, ClientConstants.TOKEN_REFRESH_INTERVAL).thenApplyAsync((securityToken) -> { + request.put(ClientConstants.MANAGEMENT_SECURITY_TOKEN_KEY, securityToken.toString()); + return request; + }, this.executor); } private CompletableFuture> managementWithRetry(Map request) { diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageReceiver.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageReceiver.java index 655709389517..d2e160f2e1b8 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageReceiver.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageReceiver.java @@ -24,8 +24,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.time.Instant; import java.time.ZonedDateTime; @@ -77,7 +75,7 @@ public final class MessageReceiver extends ClientEntity implements AmqpReceiver, private volatile CompletableFuture closeTimer; private int prefetchCount; private Exception lastKnownLinkError; - private String linkCreationTime; + private String linkCreationTime; // Used when looking at Java dumps, do not remove. private MessageReceiver(final MessagingFactory factory, final String name, @@ -149,40 +147,39 @@ public void run() { new Runnable() { @Override public void run() { - try { - underlyingFactory.getCBSChannel().sendToken( - underlyingFactory.getReactorDispatcher(), - underlyingFactory.getTokenProvider().getToken(tokenAudience, ClientConstants.TOKEN_VALIDITY), - tokenAudience, - new OperationResult() { - @Override - public void onComplete(Void result) { - if (TRACE_LOGGER.isDebugEnabled()) { - TRACE_LOGGER.debug( - String.format(Locale.US, - "clientId[%s], path[%s], linkName[%s] - token renewed", - getClientId(), receivePath, getReceiveLinkName())); - } + underlyingFactory.getCBSChannel().sendToken( + underlyingFactory.getReactorDispatcher(), + underlyingFactory.getTokenProvider().getToken(tokenAudience, ClientConstants.TOKEN_VALIDITY), + tokenAudience, + new OperationResult() { + @Override + public void onComplete(Void result) { + if (TRACE_LOGGER.isDebugEnabled()) { + TRACE_LOGGER.debug( + String.format(Locale.US, + "clientId[%s], path[%s], linkName[%s] - token renewed", + getClientId(), receivePath, getReceiveLinkName())); } - - @Override - public void onError(Exception error) { - if (TRACE_LOGGER.isInfoEnabled()) { - TRACE_LOGGER.info( - String.format(Locale.US, - "clientId[%s], path[%s], linkName[%s], tokenRenewalFailure[%s]", - getClientId(), receivePath, getReceiveLinkName(), error.getMessage())); - } + } + + @Override + public void onError(Exception error) { + if (TRACE_LOGGER.isInfoEnabled()) { + TRACE_LOGGER.info( + String.format(Locale.US, + "clientId[%s], path[%s], linkName[%s], tokenRenewalFailure[%s]", + getClientId(), receivePath, getReceiveLinkName(), error.getMessage())); } - }); - } catch (IOException | NoSuchAlgorithmException | InvalidKeyException | RuntimeException exception) { - if (TRACE_LOGGER.isInfoEnabled()) { - TRACE_LOGGER.info( - String.format(Locale.US, - "clientId[%s], path[%s], linkName[%s], tokenRenewalScheduleFailure[%s]", - getClientId(), receivePath, getReceiveLinkName(), exception.getMessage())); - } - } + } + }, + (exception) -> { + if (TRACE_LOGGER.isInfoEnabled()) { + TRACE_LOGGER.info( + String.format(Locale.US, + "clientId[%s], path[%s], linkName[%s], tokenRenewalScheduleFailure[%s]", + getClientId(), receivePath, getReceiveLinkName(), exception.getMessage())); + } + }); } }, ClientConstants.TOKEN_REFRESH_INTERVAL, @@ -578,41 +575,40 @@ public void accept(ErrorCondition t, Exception u) { } }; - try { - this.underlyingFactory.getCBSChannel().sendToken( - this.underlyingFactory.getReactorDispatcher(), - this.underlyingFactory.getTokenProvider().getToken(tokenAudience, ClientConstants.TOKEN_VALIDITY), - tokenAudience, - new OperationResult() { - @Override - public void onComplete(Void result) { - if (MessageReceiver.this.getIsClosingOrClosed()) { - return; - } - underlyingFactory.getSession( - receivePath, - onSessionOpen, - onSessionOpenFailed); + this.underlyingFactory.getCBSChannel().sendToken( + this.underlyingFactory.getReactorDispatcher(), + this.underlyingFactory.getTokenProvider().getToken(tokenAudience, ClientConstants.TOKEN_VALIDITY), + tokenAudience, + new OperationResult() { + @Override + public void onComplete(Void result) { + if (MessageReceiver.this.getIsClosingOrClosed()) { + return; } + underlyingFactory.getSession( + receivePath, + onSessionOpen, + onSessionOpenFailed); + } - @Override - public void onError(Exception error) { - final Exception completionException; - if (error != null && error instanceof AmqpException) { - completionException = ExceptionUtil.toException(((AmqpException) error).getError()); - if (completionException != error && completionException.getCause() == null) { - completionException.initCause(error); - } - } else { - completionException = error; + @Override + public void onError(Exception error) { + final Exception completionException; + if (error != null && error instanceof AmqpException) { + completionException = ExceptionUtil.toException(((AmqpException) error).getError()); + if (completionException != error && completionException.getCause() == null) { + completionException.initCause(error); } - - MessageReceiver.this.onError(completionException); + } else { + completionException = error; } - }); - } catch (IOException | NoSuchAlgorithmException | InvalidKeyException | RuntimeException exception) { - MessageReceiver.this.onError(exception); - } + + MessageReceiver.this.onError(completionException); + } + }, + (exception) -> { + MessageReceiver.this.onError(exception); + }); } // CONTRACT: message should be delivered to the caller of MessageReceiver.receive() only via Poll on prefetchqueue diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageSender.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageSender.java index 248643813aab..bde8756daf47 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageSender.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessageSender.java @@ -35,8 +35,6 @@ import java.io.IOException; import java.io.Serializable; import java.nio.BufferOverflowException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.time.Instant; import java.time.ZonedDateTime; @@ -120,7 +118,6 @@ public void onEvent() { new Runnable() { @Override public void run() { - try { underlyingFactory.getCBSChannel().sendToken( underlyingFactory.getReactorDispatcher(), underlyingFactory.getTokenProvider().getToken(tokenAudience, ClientConstants.TOKEN_VALIDITY), @@ -143,15 +140,15 @@ public void onError(Exception error) { getClientId(), sendPath, getSendLinkName(), error.getMessage())); } } + }, + (exception) -> { + if (TRACE_LOGGER.isWarnEnabled()) { + TRACE_LOGGER.warn(String.format(Locale.US, + "clientId[%s], path[%s], linkName[%s] - tokenRenewalScheduleFailure[%s]", + getClientId(), sendPath, getSendLinkName(), exception.getMessage())); + } }); - } catch (IOException | NoSuchAlgorithmException | InvalidKeyException | RuntimeException exception) { - if (TRACE_LOGGER.isWarnEnabled()) { - TRACE_LOGGER.warn(String.format(Locale.US, - "clientId[%s], path[%s], linkName[%s] - tokenRenewalScheduleFailure[%s]", - getClientId(), sendPath, getSendLinkName(), exception.getMessage())); - } } - } }, ClientConstants.TOKEN_REFRESH_INTERVAL, this.underlyingFactory); @@ -701,41 +698,40 @@ public void accept(ErrorCondition t, Exception u) { } }; - try { - this.underlyingFactory.getCBSChannel().sendToken( - this.underlyingFactory.getReactorDispatcher(), - this.underlyingFactory.getTokenProvider().getToken(tokenAudience, ClientConstants.TOKEN_VALIDITY), - tokenAudience, - new OperationResult() { - @Override - public void onComplete(Void result) { - if (MessageSender.this.getIsClosingOrClosed()) { - return; - } - underlyingFactory.getSession( - sendPath, - onSessionOpen, - onSessionOpenError); + this.underlyingFactory.getCBSChannel().sendToken( + this.underlyingFactory.getReactorDispatcher(), + this.underlyingFactory.getTokenProvider().getToken(tokenAudience, ClientConstants.TOKEN_VALIDITY), + tokenAudience, + new OperationResult() { + @Override + public void onComplete(Void result) { + if (MessageSender.this.getIsClosingOrClosed()) { + return; } + underlyingFactory.getSession( + sendPath, + onSessionOpen, + onSessionOpenError); + } - @Override - public void onError(Exception error) { - final Exception completionException; - if (error != null && error instanceof AmqpException) { - completionException = ExceptionUtil.toException(((AmqpException) error).getError()); - if (completionException != error && completionException.getCause() == null) { - completionException.initCause(error); - } - } else { - completionException = error; + @Override + public void onError(Exception error) { + final Exception completionException; + if (error != null && error instanceof AmqpException) { + completionException = ExceptionUtil.toException(((AmqpException) error).getError()); + if (completionException != error && completionException.getCause() == null) { + completionException.initCause(error); } - - MessageSender.this.onError(completionException); + } else { + completionException = error; } - }); - } catch (IOException | NoSuchAlgorithmException | InvalidKeyException | RuntimeException exception) { - MessageSender.this.onError(exception); - } + + MessageSender.this.onError(completionException); + } + }, + (exception) -> { + MessageSender.this.onError(exception); + }); } private void scheduleLinkOpenTimeout(TimeoutTracker timeout) { diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java index 2509288948c0..45460d8554ff 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java @@ -7,9 +7,12 @@ import com.microsoft.azure.eventhubs.CommunicationException; import com.microsoft.azure.eventhubs.ConnectionStringBuilder; import com.microsoft.azure.eventhubs.EventHubException; +import com.microsoft.azure.eventhubs.ITokenProvider; import com.microsoft.azure.eventhubs.OperationCancelledException; import com.microsoft.azure.eventhubs.RetryPolicy; import com.microsoft.azure.eventhubs.TimeoutException; +import com.microsoft.azure.eventhubs.TransportType; + import org.apache.qpid.proton.amqp.Symbol; import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.engine.BaseHandler; @@ -54,7 +57,7 @@ public final class MessagingFactory extends ClientEntity implements AmqpConnecti private final Object reactorLock; private final Object cbsChannelCreateLock; private final Object mgmtChannelCreateLock; - private final SharedAccessSignatureTokenProvider tokenProvider; + private final ITokenProvider tokenProvider; private final ReactorFactory reactorFactory; private Reactor reactor; @@ -67,39 +70,63 @@ public final class MessagingFactory extends ClientEntity implements AmqpConnecti private CompletableFuture open; private CompletableFuture openTimer; private CompletableFuture closeTimer; - private String reactorCreationTime; + private String reactorCreationTime; // used when looking at Java dumps, do not remove - MessagingFactory(final ConnectionStringBuilder builder, + MessagingFactory(final String hostname, + final Duration operationTimeout, + final TransportType transportType, + final ITokenProvider tokenProvider, final RetryPolicy retryPolicy, final ScheduledExecutorService executor, final ReactorFactory reactorFactory) { super(StringUtil.getRandomString("MF"), null, executor); - this.hostName = builder.getEndpoint().getHost(); + if (StringUtil.isNullOrWhiteSpace(hostname)) { + throw new IllegalArgumentException("Endpoint hostname cannot be null or empty"); + } + if (operationTimeout == null) { + throw new IllegalArgumentException("Operation timeout cannot be null"); + } + if (transportType == null) { + throw new IllegalArgumentException("Transport type cannot be null"); + } + if (tokenProvider == null) { + throw new IllegalArgumentException("Token provider cannot be null"); + } + if (retryPolicy == null) { + throw new IllegalArgumentException("Retry policy cannot be null"); + } + if (executor == null) { + throw new IllegalArgumentException("Executor cannot be null"); + } + if (reactorFactory == null) { + throw new IllegalArgumentException("Reactor factory cannot be null"); + } + + this.hostName = hostname; this.reactorFactory = reactorFactory; - this.operationTimeout = builder.getOperationTimeout(); + this.operationTimeout = operationTimeout; this.retryPolicy = retryPolicy; + this.connectionHandler = ConnectionHandler.create(transportType, this, this.getClientId()); + this.tokenProvider = tokenProvider; + this.registeredLinks = new LinkedList<>(); this.reactorLock = new Object(); - this.connectionHandler = ConnectionHandler.create(builder.getTransportType(), this, this.getClientId()); this.cbsChannelCreateLock = new Object(); this.mgmtChannelCreateLock = new Object(); - this.tokenProvider = builder.getSharedAccessSignature() == null - ? new SharedAccessSignatureTokenProvider(builder.getSasKeyName(), builder.getSasKey()) - : new SharedAccessSignatureTokenProvider(builder.getSharedAccessSignature()); this.closeTask = new CompletableFuture<>(); } public static CompletableFuture createFromConnectionString(final String connectionString, final ScheduledExecutorService executor) throws IOException { - return createFromConnectionString(connectionString, RetryPolicy.getDefault(), executor); + return createFromConnectionString(connectionString, null, executor); } public static CompletableFuture createFromConnectionString( final String connectionString, final RetryPolicy retryPolicy, final ScheduledExecutorService executor) throws IOException { - return createFromConnectionString(connectionString, retryPolicy, executor, new ReactorFactory()); + return createFromConnectionString(connectionString, retryPolicy, executor, null); } public static CompletableFuture createFromConnectionString( @@ -107,12 +134,89 @@ public static CompletableFuture createFromConnectionString( final RetryPolicy retryPolicy, final ScheduledExecutorService executor, final ReactorFactory reactorFactory) throws IOException { - final ConnectionStringBuilder builder = new ConnectionStringBuilder(connectionString); - final MessagingFactory messagingFactory = new MessagingFactory(builder, - (retryPolicy != null) ? retryPolicy : RetryPolicy.getDefault(), - executor, - reactorFactory); + final ConnectionStringBuilder csb = new ConnectionStringBuilder(connectionString); + final ITokenProvider tokenProvider = ((csb.getSharedAccessSignature() == null) ? + new SharedAccessSignatureTokenProvider(csb.getSasKeyName(), csb.getSasKey()) : + new SharedAccessSignatureTokenProvider(csb.getSharedAccessSignature())); + final MessagingFactoryBuilder builder = new MessagingFactoryBuilder(csb.getEndpoint().getHost(), tokenProvider, executor). + setOperationTimeout(csb.getOperationTimeout()). + setTransportType(csb.getTransportType()). + setRetryPolicy(retryPolicy). + setReactorFactory(reactorFactory); + return builder.build(); + } + + public static class MessagingFactoryBuilder { + // These parameters must always be specified by the caller + private final String hostname; + private final ITokenProvider tokenProvider; + private final ScheduledExecutorService executor; + + // Optional parameters with defaults + private Duration operationTimeout = DefaultOperationTimeout; + private TransportType transportType = TransportType.AMQP; + private RetryPolicy retryPolicy = RetryPolicy.getDefault(); + private ReactorFactory reactorFactory = new ReactorFactory(); + + public MessagingFactoryBuilder(final String hostname, final ITokenProvider tokenProvider, final ScheduledExecutorService executor) { + if (StringUtil.isNullOrWhiteSpace(hostname)) { + throw new IllegalArgumentException("Endpoint hostname cannot be null or empty"); + } + this.hostname = hostname; + + if (tokenProvider == null) { + throw new IllegalArgumentException("Token provider cannot be null"); + } + this.tokenProvider = tokenProvider; + + if (executor == null) { + throw new IllegalArgumentException("Executor cannot be null"); + } + this.executor = executor; + } + + public MessagingFactoryBuilder setOperationTimeout(Duration operationTimeout) { + if (operationTimeout != null) { + this.operationTimeout = operationTimeout; + } + return this; + } + + public MessagingFactoryBuilder setTransportType(TransportType transportType) { + if (transportType != null) { + this.transportType = transportType; + } + return this; + } + + public MessagingFactoryBuilder setRetryPolicy(RetryPolicy retryPolicy) { + if (retryPolicy != null) { + this.retryPolicy = retryPolicy; + } + return this; + } + + public MessagingFactoryBuilder setReactorFactory(ReactorFactory reactorFactory) { + if (reactorFactory != null) { + this.reactorFactory = reactorFactory; + } + return this; + } + + public CompletableFuture build() throws IOException { + final MessagingFactory messagingFactory = new MessagingFactory(this.hostname, + this.operationTimeout, + this.transportType, + this.tokenProvider, + this.retryPolicy, + this.executor, + this.reactorFactory); + return MessagingFactory.factoryStartup(messagingFactory); + } + } + private static CompletableFuture factoryStartup(MessagingFactory messagingFactory) throws IOException + { messagingFactory.createConnection(); final Timer timer = new Timer(messagingFactory); @@ -130,18 +234,18 @@ public void run() { // if scheduling messagingfactory openTimer fails - notify user and stop messagingFactory.openTimer.handleAsync( - (unUsed, exception) -> { - if (exception != null && !(exception instanceof CancellationException)) { - messagingFactory.open.completeExceptionally(exception); - messagingFactory.getReactor().stop(); - } + (unUsed, exception) -> { + if (exception != null && !(exception instanceof CancellationException)) { + messagingFactory.open.completeExceptionally(exception); + messagingFactory.getReactor().stop(); + } - return null; - }, messagingFactory.executor); + return null; + }, messagingFactory.executor); return messagingFactory.open; } - + @Override public String getHostName() { return this.hostName; @@ -159,7 +263,7 @@ public ReactorDispatcher getReactorDispatcher() { } } - public SharedAccessSignatureTokenProvider getTokenProvider() { + public ITokenProvider getTokenProvider() { return this.tokenProvider; } @@ -184,7 +288,7 @@ private void startReactor(final ReactorHandler reactorHandler) throws IOExceptio public CBSChannel getCBSChannel() { synchronized (this.cbsChannelCreateLock) { if (this.cbsChannel == null) { - this.cbsChannel = new CBSChannel(this, this, this.getClientId()); + this.cbsChannel = new CBSChannel(this, this, this.getClientId(), this.executor); } } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java index e5b412bc8a3a..047b629bb637 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java @@ -5,6 +5,10 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; + +import com.microsoft.azure.eventhubs.ITokenProvider; +import com.microsoft.azure.eventhubs.SecurityToken; + import java.io.IOException; import java.net.URLEncoder; import java.security.InvalidKeyException; @@ -12,11 +16,14 @@ import java.time.Duration; import java.time.Instant; import java.util.Base64; +import java.util.Date; import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import static java.nio.charset.StandardCharsets.UTF_8; -public class SharedAccessSignatureTokenProvider { +public class SharedAccessSignatureTokenProvider implements ITokenProvider { final String keyName; final String sharedAccessKey; final String sharedAccessSignature; @@ -29,7 +36,7 @@ public class SharedAccessSignatureTokenProvider { this.sharedAccessSignature = null; } - public SharedAccessSignatureTokenProvider(final String sharedAccessSignature) { + SharedAccessSignatureTokenProvider(final String sharedAccessSignature) { this.keyName = null; this.sharedAccessKey = null; this.sharedAccessSignature = sharedAccessSignature; @@ -77,9 +84,23 @@ public static String generateSharedAccessSignature( URLEncoder.encode(keyName, utf8Encoding)); } - public String getToken(final String resource, final Duration tokenTimeToLive) throws IOException, InvalidKeyException, NoSuchAlgorithmException { - return this.sharedAccessSignature == null - ? generateSharedAccessSignature(this.keyName, this.sharedAccessKey, resource, tokenTimeToLive) - : this.sharedAccessSignature; + public CompletableFuture getToken(final String resource, final Duration tokenTimeToLive) { + final CompletableFuture result = new CompletableFuture<>(); + final String token; + + if (this.sharedAccessSignature == null) { + try { + // Generation is nonblocking so there is no need to run it async. + token = generateSharedAccessSignature(this.keyName, this.sharedAccessKey, resource, ClientConstants.TOKEN_VALIDITY); + } catch (NoSuchAlgorithmException|IOException|InvalidKeyException e) { + result.completeExceptionally(e); + return result; + } + result.complete(new SecurityToken(ClientConstants.SAS_TOKEN_TYPE, token, Date.from(Instant.now().plus(ClientConstants.TOKEN_VALIDITY)))); + } else { + result.complete(new SecurityToken(ClientConstants.SAS_TOKEN_TYPE, this.sharedAccessSignature, Date.from(Instant.now().plus(ClientConstants.TOKEN_VALIDITY)))); + } + + return result; } } From 1336e17752a3fb56306ce294dc125121c3a06e22 Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Thu, 16 May 2019 17:56:16 -0700 Subject: [PATCH 02/22] Parallel latest dotnet changes from Serkant --- .../eventprocessorhost/PartitionManager.java | 2 +- .../eventprocessorhost/PartitionPump.java | 2 +- .../RealEventHubUtilities.java | 4 +- .../azure/eventprocessorhost/Repros.java | 2 +- .../extensions/appender/EventHubsManager.java | 2 +- eventhubs/data-plane/azure-eventhubs/pom.xml | 9 +- .../AuthorizationFailedException.java | 2 +- .../AzureActiveDirectoryTokenProvider.java | 60 +++---- .../eventhubs/ConnectionStringBuilder.java | 31 +++- .../azure/eventhubs/ErrorContext.java | 4 +- .../microsoft/azure/eventhubs/EventData.java | 6 +- .../azure/eventhubs/EventHubClient.java | 150 ++++++------------ .../azure/eventhubs/JsonSecurityToken.java | 21 +++ .../ManagedIdentityTokenProvider.java | 25 +++ .../ManagedServiceIdentityTokenProvider.java | 150 ------------------ .../azure/eventhubs/PartitionSender.java | 2 +- .../eventhubs/QuotaExceededException.java | 3 +- .../azure/eventhubs/SecurityToken.java | 10 +- .../impl/ActiveClientTokenManager.java | 2 +- .../azure/eventhubs/impl/ClientConstants.java | 1 + .../eventhubs/impl/EventHubClientImpl.java | 14 +- .../eventhubs/impl/MessagingFactory.java | 16 +- .../SharedAccessSignatureTokenProvider.java | 9 +- .../concurrency/ConcurrentReceiversTest.java | 4 +- .../concurrency/EventHubClientTest.java | 2 +- .../connstrbuilder/TransportTypeTest.java | 6 +- .../eventhubs/eventdata/BackCompatTest.java | 2 +- .../eventdata/EventDataBatchTest.java | 2 +- .../eventdata/InteropAmqpPropertiesTest.java | 2 +- .../eventdata/InteropEventBodyTest.java | 2 +- .../ClientEntityCreateTest.java | 12 +- .../MsgFactoryOpenCloseTest.java | 28 ++-- .../exceptioncontracts/ReactorFaultTest.java | 4 +- .../exceptioncontracts/ReceiverEpochTest.java | 2 +- .../SecurityExceptionsTest.java | 20 +-- .../SendLargeMessageTest.java | 4 +- .../eventhubs/proxy/ProxySelectorTest.java | 2 +- .../sendrecv/EventDataBatchAPITest.java | 2 +- .../sendrecv/ReceiveParallelManualTest.java | 8 +- .../sendrecv/ReceivePumpEventHubTest.java | 2 +- .../azure/eventhubs/sendrecv/ReceiveTest.java | 2 +- .../sendrecv/ReceiverIdentifierTest.java | 2 +- .../sendrecv/ReceiverRuntimeMetricsTest.java | 2 +- .../sendrecv/RequestResponseTest.java | 10 +- .../azure/eventhubs/sendrecv/SendTest.java | 2 +- .../sendrecv/SetPrefetchCountTest.java | 2 +- 46 files changed, 266 insertions(+), 385 deletions(-) create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java delete mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java index 8f419a35e6e2..bb17cfeb4b3f 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java @@ -47,7 +47,7 @@ CompletableFuture cachePartitionIds() { final CompletableFuture cleanupFuture = new CompletableFuture(); // Stage 0A: get EventHubClient for the event hub - retval = EventHubClient.create(this.hostContext.getEventHubConnectionString(), this.hostContext.getRetryPolicy(), this.hostContext.getExecutor()) + retval = EventHubClient.createFromConnectionString(this.hostContext.getEventHubConnectionString(), this.hostContext.getRetryPolicy(), this.hostContext.getExecutor()) // Stage 0B: set up a way to close the EventHubClient when we're done .thenApplyAsync((ehClient) -> { final EventHubClient saveForCleanupClient = ehClient; diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java index d2e16d1e3c09..59da3f8f07e2 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java @@ -175,7 +175,7 @@ private CompletableFuture openClients() { CompletableFuture startOpeningFuture = null; try { - startOpeningFuture = EventHubClient.create(this.hostContext.getEventHubConnectionString(), + startOpeningFuture = EventHubClient.createFromConnectionString(this.hostContext.getEventHubConnectionString(), this.hostContext.getRetryPolicy(), this.hostContext.getExecutor()); } catch (EventHubException | IOException e2) { // Marking startOpeningFuture as completed exceptionally will cause all the diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/RealEventHubUtilities.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/RealEventHubUtilities.java index b3174c613ea7..492b9080fdb8 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/RealEventHubUtilities.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/RealEventHubUtilities.java @@ -37,7 +37,7 @@ ArrayList setup(boolean skipIfFakeEH, int fakePartitions) throws EventHu ArrayList partitionIds = setupWithoutSenders(skipIfFakeEH, fakePartitions); // EventHubClient is source of all senders - this.client = EventHubClient.createSync(this.hubConnectionString.toString(), TestUtilities.EXECUTOR_SERVICE); + this.client = EventHubClient.createFromConnectionStringSync(this.hubConnectionString.toString(), TestUtilities.EXECUTOR_SERVICE); return partitionIds; } @@ -132,7 +132,7 @@ ArrayList getPartitionIdsForTest() throws EventHubException, IOException this.cachedPartitionIds = new ArrayList(); ehCacheCheck(true); - EventHubClient idClient = EventHubClient.createSync(this.hubConnectionString.toString(), TestUtilities.EXECUTOR_SERVICE); + EventHubClient idClient = EventHubClient.createFromConnectionStringSync(this.hubConnectionString.toString(), TestUtilities.EXECUTOR_SERVICE); try { EventHubRuntimeInformation info = idClient.getRuntimeInformation().get(); String[] ids = info.getPartitionIds(); diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java index 8d0c6dcf9b40..69153562b544 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java @@ -229,7 +229,7 @@ public void rawEpochStealing() throws Exception { System.out.println("\nParked: " + parkedCount + " SELECTING: " + selectingList); System.out.println("Client " + clientSerialNumber + " starting"); - EventHubClient client = EventHubClient.createSync(utils.getConnectionString(true).toString(), TestUtilities.EXECUTOR_SERVICE); + EventHubClient client = EventHubClient.createFromConnectionStringSync(utils.getConnectionString(true).toString(), TestUtilities.EXECUTOR_SERVICE); PartitionReceiver receiver = client.createReceiver(utils.getConsumerGroup(), "0", EventPosition.fromStartOfStream()).get(); //client.createEpochReceiver(utils.getConsumerGroup(), "0", PartitionReceiver.START_OF_STREAM, 1).get(); diff --git a/eventhubs/data-plane/azure-eventhubs-extensions/src/main/java/com/microsoft/azure/eventhubs/extensions/appender/EventHubsManager.java b/eventhubs/data-plane/azure-eventhubs-extensions/src/main/java/com/microsoft/azure/eventhubs/extensions/appender/EventHubsManager.java index c4c9a9e6ad3e..aa22b28b1200 100644 --- a/eventhubs/data-plane/azure-eventhubs-extensions/src/main/java/com/microsoft/azure/eventhubs/extensions/appender/EventHubsManager.java +++ b/eventhubs/data-plane/azure-eventhubs-extensions/src/main/java/com/microsoft/azure/eventhubs/extensions/appender/EventHubsManager.java @@ -43,6 +43,6 @@ public void send(final Iterable messages) throws EventHubException { } public void startup() throws EventHubException, IOException { - this.eventHubSender = EventHubClient.createSync(this.eventHubConnectionString, EXECUTOR_SERVICE); + this.eventHubSender = EventHubClient.createFromConnectionStringSync(this.eventHubConnectionString, EXECUTOR_SERVICE); } } diff --git a/eventhubs/data-plane/azure-eventhubs/pom.xml b/eventhubs/data-plane/azure-eventhubs/pom.xml index a1ae860e51d4..e53b26bd47c6 100644 --- a/eventhubs/data-plane/azure-eventhubs/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs/pom.xml @@ -33,8 +33,13 @@ com.microsoft.azure - adal4j - 1.6.4 + azure-client-authentication + 1.6.7 + + + com.auth0 + java-jwt + 3.8.0 diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AuthorizationFailedException.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AuthorizationFailedException.java index 3905f043321c..436f5fd9b69c 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AuthorizationFailedException.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AuthorizationFailedException.java @@ -8,7 +8,7 @@ /** * Authorization failed exception is thrown when error is encountered during authorizing user's permission to run the intended operations. * When encountered this exception user should check whether the token/key provided in the connection string (e.g. one passed to - * {@link EventHubClient#create(String, ScheduledExecutorService)}) is valid, and has correct execution right for the intended operations (e.g. + * {@link EventHubClient#createFromConnectionString(String, ScheduledExecutorService)}) is valid, and has correct execution right for the intended operations (e.g. * Receive call will need Listen claim associated with the key/token). * * @see http://go.microsoft.com/fwlink/?LinkId=761101 diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java index 36865908fa0a..96e6e50c0aa5 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java @@ -3,58 +3,42 @@ package com.microsoft.azure.eventhubs; +import java.text.ParseException; import java.time.Duration; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; +import java.util.concurrent.CompletionException; -import com.microsoft.aad.adal4j.AuthenticationCallback; -import com.microsoft.aad.adal4j.AuthenticationContext; -import com.microsoft.aad.adal4j.AuthenticationResult; import com.microsoft.azure.eventhubs.impl.ClientConstants; public final class AzureActiveDirectoryTokenProvider implements ITokenProvider { - public final static String EVENTHUBS_REGISTERED_AUDIENCE = "https://eventhubs.azure.net/"; + public final static String COMMON_AUTHORITY = "https://login.microsoftonline.com/common"; - private final AuthenticationContext authenticationContext; - private final ITokenAcquirer tokenAcquirer; + private final AuthenticationCallback authCallback; + private final String authority; + private final Object authCallbackState; public AzureActiveDirectoryTokenProvider( - final AuthenticationContext authenticationContext, - final ITokenAcquirer tokenAcquirer) { - this.authenticationContext = authenticationContext; - this.tokenAcquirer = tokenAcquirer; + final AuthenticationCallback authenticationCallback, + final String authority, + final Object state) { + this.authCallbackState = state; + this.authority = authority; + this.authCallback = authenticationCallback; } @Override public CompletableFuture getToken(String resource, Duration timeout) { - final CompletableFuture result = new CompletableFuture<>(); - this.tokenAcquirer.acquireToken(this.authenticationContext, new EventHubsAuthenticationCallback(result)); - return result; + return this.authCallback.acquireToken(ClientConstants.EVENTHUBS_AUDIENCE, this.authority, this.authCallbackState) + .thenApply((rawToken) -> { + try { + return new JsonSecurityToken(rawToken, resource); + } catch (ParseException e) { + throw new CompletionException(e); + } + }); } - public static class EventHubsAuthenticationCallback implements AuthenticationCallback { - final CompletableFuture result; - - public EventHubsAuthenticationCallback(final CompletableFuture result) { - this.result = result; - } - - @Override - public void onSuccess(AuthenticationResult authenticationResult) { - this.result.complete(new SecurityToken(ClientConstants.JWT_TOKEN_TYPE, - authenticationResult.getAccessToken(), - authenticationResult.getExpiresOnDate())); - } - - @Override - public void onFailure(Throwable throwable) { - this.result.completeExceptionally(throwable); - } - } - - public interface ITokenAcquirer { - Future acquireToken( - final AuthenticationContext authenticationContext, - final AuthenticationCallback authenticationCallback); + public interface AuthenticationCallback { + CompletableFuture acquireToken(final String audience, final String authority, final Object state); } } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java index d203026ddec2..cf1151f5d401 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java @@ -61,6 +61,7 @@ public final class ConnectionStringBuilder { static final String SHARED_ACCESS_KEY_CONFIG_NAME = "SharedAccessKey"; static final String SHARED_ACCESS_SIGNATURE_CONFIG_NAME = "SharedAccessSignature"; static final String TRANSPORT_TYPE_CONFIG_NAME = "TransportType"; + static final String AUTHENTICATION_CONFIG_NAME = "Authentication"; private static final String ALL_KEY_ENUMERATE_REGEX = "(" + HOST_NAME_CONFIG_NAME + "|" + ENDPOINT_CONFIG_NAME + "|" + SHARED_ACCESS_KEY_NANE_CONFIG_NAME + "|" + SHARED_ACCESS_KEY_CONFIG_NAME + "|" + SHARED_ACCESS_SIGNATURE_CONFIG_NAME + "|" + ENTITY_PATH_CONFIG_NAME + "|" + OPERATION_TIMEOUT_CONFIG_NAME @@ -76,6 +77,7 @@ public final class ConnectionStringBuilder { private String sharedAccessSignature; private Duration operationTimeout; private TransportType transportType; + private String authentication; /** * Creates an empty {@link ConnectionStringBuilder}. At minimum, a namespace name, an entity path, SAS key name, and SAS key @@ -278,6 +280,26 @@ public ConnectionStringBuilder setTransportType(final TransportType transportTyp this.transportType = transportType; return this; } + + /** + * Get the authentication type in the Connection String. + * + * @return authentication + */ + public String getAuthentication() { + return this.authentication; + } + + /** + * Set the authentication type in the Connection String. The only valid values are "Managed Identity" or null. + * + * @param authentication + * @return the {@link ConnectionStringBuilder} instance being set. + */ + public ConnectionStringBuilder setAuthentication(final String authentication) { + this.authentication = authentication; + return this; + } /** * Returns an inter-operable connection string that can be used to connect to EventHubs instances. @@ -321,6 +343,11 @@ public String toString() { connectionStringBuilder.append(String.format(Locale.US, "%s%s%s%s", TRANSPORT_TYPE_CONFIG_NAME, KEY_VALUE_SEPARATOR, this.transportType.toString(), KEY_VALUE_PAIR_DELIMITER)); } + + if (!StringUtil.isNullOrWhiteSpace(this.authentication)) { + connectionStringBuilder.append(String.format(Locale.US, "%s%s%s%s", AUTHENTICATION_CONFIG_NAME, + KEY_VALUE_SEPARATOR, this.authentication, KEY_VALUE_PAIR_DELIMITER)); + } connectionStringBuilder.deleteCharAt(connectionStringBuilder.length() - 1); return connectionStringBuilder.toString(); @@ -409,7 +436,9 @@ private void parseConnectionString(final String connectionString) { String.format(Locale.US, "Invalid value specified for property '%s' in the ConnectionString.", TRANSPORT_TYPE_CONFIG_NAME), exception); } - } else { + } else if (key.equalsIgnoreCase(AUTHENTICATION_CONFIG_NAME)) { + this.authentication = values[valueIndex]; + } else { throw new IllegalConnectionStringFormatException( String.format(Locale.US, "Illegal connection string parameter name: %s", key)); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ErrorContext.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ErrorContext.java index ebf4ecaf3037..ef55c00f1f3a 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ErrorContext.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ErrorContext.java @@ -9,7 +9,9 @@ import java.util.Locale; public abstract class ErrorContext implements Serializable { - private final String namespaceName; + private static final long serialVersionUID = -841174412304936908L; + + private final String namespaceName; protected ErrorContext(final String namespaceName) { this.namespaceName = namespaceName; diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventData.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventData.java index e2091f2977e6..128eccf75f7c 100755 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventData.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventData.java @@ -50,7 +50,7 @@ public interface EventData extends Serializable, Comparable { * * @param data the actual payload of data in bytes to be Sent to EventHubs. * @return EventData the created {@link EventData} to send to EventHubs. - * @see EventHubClient#create(String, ScheduledExecutorService) + * @see EventHubClient#createFromConnectionString(String, ScheduledExecutorService) */ static EventData create(final byte[] data) { return new EventDataImpl(data); @@ -74,7 +74,7 @@ static EventData create(final byte[] data) { * @param offset Offset in the byte[] to read from ; inclusive index * @param length length of the byte[] to be read, starting from offset * @return EventData the created {@link EventData} to send to EventHubs. - * @see EventHubClient#create(String, ScheduledExecutorService) + * @see EventHubClient#createFromConnectionString(String, ScheduledExecutorService) */ static EventData create(final byte[] data, final int offset, final int length) { return new EventDataImpl(data, offset, length); @@ -96,7 +96,7 @@ static EventData create(final byte[] data, final int offset, final int length) { * * @param buffer ByteBuffer which references the payload of the Event to be sent to EventHubs * @return EventData the created {@link EventData} to send to EventHubs. - * @see EventHubClient#create(String, ScheduledExecutorService) + * @see EventHubClient#createFromConnectionString(String, ScheduledExecutorService) */ static EventData create(final ByteBuffer buffer) { return new EventDataImpl(buffer); diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java index d3402af96765..955aba74929f 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java @@ -3,11 +3,6 @@ package com.microsoft.azure.eventhubs; -import com.microsoft.aad.adal4j.AsymmetricKeyCredential; -import com.microsoft.aad.adal4j.AuthenticationCallback; -import com.microsoft.aad.adal4j.AuthenticationContext; -import com.microsoft.aad.adal4j.AuthenticationResult; -import com.microsoft.aad.adal4j.ClientCredential; import com.microsoft.azure.eventhubs.impl.EventHubClientImpl; import com.microsoft.azure.eventhubs.impl.ExceptionUtil; @@ -15,20 +10,19 @@ import java.net.URI; import java.nio.channels.UnresolvedAddressException; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; /** * Anchor class - all EventHub client operations STARTS here. * - * @see EventHubClient#create(String, ScheduledExecutorService) + * @see EventHubClient#createFromConnectionString(String, ScheduledExecutorService) */ public interface EventHubClient { String DEFAULT_CONSUMER_GROUP_NAME = "$Default"; /** - * Synchronous version of {@link #create(String, ScheduledExecutorService)}. + * Synchronous version of {@link #createFromConnectionString(String, ScheduledExecutorService)}. * * @param connectionString The connection string to be used. See {@link ConnectionStringBuilder} to construct a connectionString. * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. @@ -36,13 +30,13 @@ public interface EventHubClient { * @throws EventHubException If Service Bus service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ - static EventHubClient createSync(final String connectionString, final ScheduledExecutorService executor) + static EventHubClient createFromConnectionStringSync(final String connectionString, final ScheduledExecutorService executor) throws EventHubException, IOException { - return EventHubClient.createSync(connectionString, null, executor); + return createFromConnectionStringSync(connectionString, null, executor); } /** - * Synchronous version of {@link #create(String, ScheduledExecutorService)}. + * Synchronous version of {@link #createFromConnectionString(String, ScheduledExecutorService)}. * * @param connectionString The connection string to be used. See {@link ConnectionStringBuilder} to construct a connectionString. * @param retryPolicy A custom {@link RetryPolicy} to be used when communicating with EventHub. @@ -51,9 +45,9 @@ static EventHubClient createSync(final String connectionString, final ScheduledE * @throws EventHubException If Service Bus service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ - static EventHubClient createSync(final String connectionString, final RetryPolicy retryPolicy, final ScheduledExecutorService executor) + static EventHubClient createFromConnectionStringSync(final String connectionString, final RetryPolicy retryPolicy, final ScheduledExecutorService executor) throws EventHubException, IOException { - return ExceptionUtil.syncWithIOException(() -> create(connectionString, retryPolicy, executor).get()); + return ExceptionUtil.syncWithIOException(() -> createFromConnectionString(connectionString, retryPolicy, executor).get()); } /** @@ -67,11 +61,31 @@ static EventHubClient createSync(final String connectionString, final RetryPolic * @throws EventHubException If Service Bus service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ - static CompletableFuture create(final String connectionString, final ScheduledExecutorService executor) + static CompletableFuture createFromConnectionString(final String connectionString, final ScheduledExecutorService executor) throws EventHubException, IOException { - return EventHubClient.create(connectionString, null, executor); + return createFromConnectionString(connectionString, null, executor); } + /** + * Factory method to create an instance of {@link EventHubClient} using the supplied connectionString. + * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. + *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. + * + * @param connectionString The connection string to be used. See {@link ConnectionStringBuilder} to construct a connectionString. + * @param retryPolicy A custom {@link RetryPolicy} to be used when communicating with EventHub. + * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. + * @return CompletableFuture{@literal } which can be used to create Senders and Receivers to EventHub + * @throws EventHubException If Service Bus service encountered problems during connection creation. + * @throws IOException If the underlying Proton-J layer encounter network errors. + */ + static CompletableFuture createFromConnectionString( + final String connectionString, final RetryPolicy retryPolicy, final ScheduledExecutorService executor) + throws EventHubException, IOException { + return EventHubClientImpl.create(connectionString, retryPolicy, executor); + } + + + /** * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. @@ -79,66 +93,46 @@ static CompletableFuture create(final String connectionString, f * * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName * @param eventHubName EventHub name - * @param tokenProvider The {@link ITokenProvider} implementation to be used to authenticate + * @param authCallback A callback which returns a JSON Web Token obtained from AAD. * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. * @return EventHubClient which can be used to create Senders and Receivers to EventHub * @throws EventHubException If the EventHubs service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ - public static CompletableFuture create( + public static CompletableFuture createWithAzureActiveDirectory( final URI endpointAddress, final String eventHubName, - final ITokenProvider tokenProvider, + final AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback, final ScheduledExecutorService executor, final EventHubClientOptions options) throws EventHubException, IOException { - EventHubClientOptions effectiveOptions = (options != null) ? options : new EventHubClientOptions(); - return EventHubClientImpl.create(endpointAddress, eventHubName, tokenProvider, executor, effectiveOptions); + return createWithAzureActiveDirectory(endpointAddress, eventHubName, authCallback, AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY, executor, options); } - + /** * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. * - * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName - * @param eventHubName EventHub name - * @param authenticationContext The Azure Active Directory {@link AuthenticationContext} - * @param clientCredential The Azure Active Directory {@link ClientCredential} + * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName + * @param eventHubName EventHub name + * @param authCallback A callback which returns a JSON Web Token obtained from AAD. + * @param authority Address of the AAD authority to issue the token. * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. * @return EventHubClient which can be used to create Senders and Receivers to EventHub * @throws EventHubException If the EventHubs service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ - public static CompletableFuture create( + public static CompletableFuture createWithAzureActiveDirectory( final URI endpointAddress, final String eventHubName, - final AuthenticationContext authenticationContext, - final ClientCredential clientCredential, + final AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback, + final String authority, final ScheduledExecutorService executor, final EventHubClientOptions options) throws EventHubException, IOException { - if (authenticationContext == null) { - throw new IllegalArgumentException("authenticationContext cannot be null"); - } - if (clientCredential == null) { - throw new IllegalArgumentException("clientCredential cannot be null"); - } - - ITokenProvider tokenProvider = new AzureActiveDirectoryTokenProvider( - authenticationContext, - new AzureActiveDirectoryTokenProvider.ITokenAcquirer() { - @Override - public Future acquireToken( - final AuthenticationContext authenticationContext, - final AuthenticationCallback authenticationCallback) { - return authenticationContext.acquireToken( - AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE, - clientCredential, - authenticationCallback); - } - }); - return create(endpointAddress, eventHubName, tokenProvider, executor, options); + ITokenProvider tokenProvider = new AzureActiveDirectoryTokenProvider(authCallback, authority, null); + return createWithTokenProvider(endpointAddress, eventHubName, tokenProvider, executor, options); } /** @@ -148,81 +142,41 @@ public Future acquireToken( * * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName * @param eventHubName EventHub name - * @param authenticationContext The Azure Active Directory {@link AuthenticationContext} - * @param credential The Azure Active Directory {@link AsymmetricKeyCredential} * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. * @return EventHubClient which can be used to create Senders and Receivers to EventHub * @throws EventHubException If the EventHubs service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ - public static CompletableFuture create( + public static CompletableFuture createWithManagedIdentity( final URI endpointAddress, final String eventHubName, - final AuthenticationContext authenticationContext, - final AsymmetricKeyCredential credential, final ScheduledExecutorService executor, final EventHubClientOptions options) throws EventHubException, IOException { - if (authenticationContext == null) { - throw new IllegalArgumentException("authenticationContext cannot be null"); - } - if (credential == null) { - throw new IllegalArgumentException("credential cannot be null"); - } - - ITokenProvider tokenProvider = new AzureActiveDirectoryTokenProvider( - authenticationContext, - new AzureActiveDirectoryTokenProvider.ITokenAcquirer() { - @Override - public Future acquireToken( - final AuthenticationContext authenticationContext, - final AuthenticationCallback authenticationCallback) { - return authenticationContext.acquireToken( - AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE, - credential, - authenticationCallback); - } - }); - return create(endpointAddress, eventHubName, tokenProvider, executor, options); + return createWithTokenProvider(endpointAddress, eventHubName, new ManagedIdentityTokenProvider(), executor, options); } - + /** * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. * - * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName - * @param eventHubName EventHub name + * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName + * @param eventHubName EventHub name + * @param tokenProvider The {@link ITokenProvider} implementation to be used to authenticate * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. * @return EventHubClient which can be used to create Senders and Receivers to EventHub * @throws EventHubException If the EventHubs service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ - public static CompletableFuture createWithManagedServiceIdentity( + public static CompletableFuture createWithTokenProvider( final URI endpointAddress, final String eventHubName, + final ITokenProvider tokenProvider, final ScheduledExecutorService executor, final EventHubClientOptions options) throws EventHubException, IOException { - return create(endpointAddress, eventHubName, new ManagedServiceIdentityTokenProvider(), executor, options); - } - - /** - * Factory method to create an instance of {@link EventHubClient} using the supplied connectionString. - * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. - *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. - * - * @param connectionString The connection string to be used. See {@link ConnectionStringBuilder} to construct a connectionString. - * @param retryPolicy A custom {@link RetryPolicy} to be used when communicating with EventHub. - * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. - * @return CompletableFuture{@literal } which can be used to create Senders and Receivers to EventHub - * @throws EventHubException If Service Bus service encountered problems during connection creation. - * @throws IOException If the underlying Proton-J layer encounter network errors. - */ - static CompletableFuture create( - final String connectionString, final RetryPolicy retryPolicy, final ScheduledExecutorService executor) - throws EventHubException, IOException { - return EventHubClientImpl.create(connectionString, retryPolicy, executor); + return EventHubClientImpl.create(endpointAddress, eventHubName, tokenProvider, executor, options); } /** diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java new file mode 100644 index 000000000000..107080c9558c --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java @@ -0,0 +1,21 @@ +package com.microsoft.azure.eventhubs; + +import java.text.ParseException; +import java.util.Date; + +import com.microsoft.azure.eventhubs.impl.ClientConstants; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.JWTParser; + +public class JsonSecurityToken extends SecurityToken { + public JsonSecurityToken(final String rawToken, final String audience) throws ParseException { + super(rawToken, GetExpirationDateTimeUtcFromToken(rawToken), audience, ClientConstants.JWT_TOKEN_TYPE); + } + + static Date GetExpirationDateTimeUtcFromToken(final String token) throws ParseException { + JWT jwt = JWTParser.parse(token); + JWTClaimsSet claims = jwt.getJWTClaimsSet(); + return claims.getExpirationTime(); + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java new file mode 100644 index 000000000000..9af36d152965 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java @@ -0,0 +1,25 @@ +package com.microsoft.azure.eventhubs; + +import java.io.IOException; +import java.text.ParseException; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import com.microsoft.azure.credentials.MSICredentials; + +public class ManagedIdentityTokenProvider implements ITokenProvider { + @Override + public CompletableFuture getToken(final String resource, final Duration timeout) { + final MSICredentials credentials = new MSICredentials(); + + return CompletableFuture.supplyAsync(() -> { + try { + String rawToken = credentials.getToken(resource); + return new JsonSecurityToken(rawToken, resource); + } catch (IOException | ParseException e) { + throw new CompletionException(e); + } + }); + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java deleted file mode 100644 index db76e20fe533..000000000000 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedServiceIdentityTokenProvider.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.microsoft.azure.eventhubs; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.ProtocolException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.sql.Date; -import java.time.Duration; -import java.util.concurrent.CompletableFuture; - -import com.google.gson.Gson; -import com.microsoft.azure.eventhubs.impl.ClientConstants; -import com.microsoft.azure.eventhubs.impl.StringUtil; - -public class ManagedServiceIdentityTokenProvider implements ITokenProvider { - static final String LOCAL_VM_REST_MSI_ENDPOINT_URL = "http://localhost:50342/oauth2/token"; - static final String LOCAL_VM_MSI_URL_FORMAT = "%s?resource=%s"; - static final String LOCAL_VM_MSI_URL = String.format(LOCAL_VM_MSI_URL_FORMAT, - LOCAL_VM_REST_MSI_ENDPOINT_URL, - AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE); - static final String METADATA_HEADER_NAME = "Metadata"; - - static final String API_VERSION = "api-version=2017-09-01"; - static final String MSI_SECRET_ENV_VARIABLE = "MSI_SECRET"; - static final String MSI_ENDPOINT_ENV_VARIABLE = "MSI_ENDPOINT"; - static final String DYNAMIC_MSI_URL_FORMAT = "%s?resource=%s&%s"; - static final String SECRET_HEADER_NAME = "Secret"; - - @Override - public CompletableFuture getToken( - final String resource, - final Duration timeout) { - - final CompletableFuture getTokenTask = new CompletableFuture<>(); - - final String dynamicMsiEndpointUrl = System.getenv(MSI_ENDPOINT_ENV_VARIABLE); - final String msiSecret = System.getenv(MSI_SECRET_ENV_VARIABLE); - - final boolean useDynamicMsiUrl; - - if (StringUtil.isNullOrEmpty(dynamicMsiEndpointUrl) || StringUtil.isNullOrEmpty(msiSecret)) { - useDynamicMsiUrl = false; - } - else { - useDynamicMsiUrl = true; - } - - final URL msiUrl; - try { - msiUrl = useDynamicMsiUrl - ? new URL(String.format(DYNAMIC_MSI_URL_FORMAT, dynamicMsiEndpointUrl, AzureActiveDirectoryTokenProvider.EVENTHUBS_REGISTERED_AUDIENCE, API_VERSION)) - : new URL(LOCAL_VM_MSI_URL); - } catch (MalformedURLException malformedUrlException) { - getTokenTask.completeExceptionally(malformedUrlException); - return getTokenTask; - } - - final HttpURLConnection httpConnection; - try { - httpConnection = (HttpURLConnection) msiUrl.openConnection(); - }catch (IOException ioException) { - getTokenTask.completeExceptionally(ioException); - return getTokenTask; - } - - if (useDynamicMsiUrl) { - httpConnection.setRequestProperty(SECRET_HEADER_NAME, msiSecret); - } - else { - httpConnection.setRequestProperty(METADATA_HEADER_NAME, "true"); - } - - try { - httpConnection.setRequestMethod("GET"); - } catch(ProtocolException protocolException) { - getTokenTask.completeExceptionally(protocolException); - return getTokenTask; - } - - httpConnection.setDoInput(true); - - try { - httpConnection.connect(); - } catch (IOException ioException) { - getTokenTask.completeExceptionally(ioException); - return getTokenTask; - } - - try { - final StringBuilder responseBuilder = new StringBuilder(); - try (Reader reader = new InputStreamReader(httpConnection.getInputStream(), StandardCharsets.UTF_8)) { - final char[] buffer = new char[1024]; - int numBytesRead = -1; - while ((numBytesRead = reader.read(buffer)) != -1) { - responseBuilder.append(buffer, 0, numBytesRead); - } - } catch (IOException ioException) { - getTokenTask.completeExceptionally(ioException); - return getTokenTask; - } - - final Gson gson = new Gson(); - final MSIToken token = gson.fromJson(responseBuilder.toString(), MSIToken.class); - - getTokenTask.complete(new SecurityToken(ClientConstants.JWT_TOKEN_TYPE, token.access_token, new Date(token.expires_on))); - return getTokenTask; - }finally { - httpConnection.disconnect(); - } - } - - private static class MSIToken - { - private String access_token; - private String refresh_token; - // Token validity in number of seconds - private int expires_in; - // Seconds from 1970-01-01T0:0:0Z UTC when the token will expire - private long expires_on; - // Seconds from 1970-01-01T0:0:0Z UTC after which the token takes effect - private long not_before; - // Resource for which token is requested - private String resource; - // Token type - private String token_type; - - public String getAccessToken() { - return access_token; - } - public int getExpiresIn() { - return expires_in; - } - public long getExpiresOn() { - return expires_on; - } - public long getNotBefore() { - return not_before; - } - public String getResource() { - return resource; - } - public String getTokenType() { - return token_type; - } - } -} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/PartitionSender.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/PartitionSender.java index c191c9881698..3ae79cecdb48 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/PartitionSender.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/PartitionSender.java @@ -13,7 +13,7 @@ * if you do not care about sending events to specific partitions. Instead, use {@link EventHubClient#send} method. * * @see EventHubClient#createPartitionSender(String) - * @see EventHubClient#create(String, ScheduledExecutorService) + * @see EventHubClient#createFromConnectionString(String, ScheduledExecutorService) */ public interface PartitionSender { diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/QuotaExceededException.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/QuotaExceededException.java index e0ff9b50d376..10533ea5c753 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/QuotaExceededException.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/QuotaExceededException.java @@ -4,8 +4,9 @@ package com.microsoft.azure.eventhubs; public class QuotaExceededException extends EventHubException { + private static final long serialVersionUID = 1L; - public QuotaExceededException(String message) { + public QuotaExceededException(String message) { super(false, message); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java index 13a68a2b6962..da977623307b 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java @@ -5,15 +5,17 @@ import java.util.Date; -public final class SecurityToken { +public class SecurityToken { private final String tokenType; private final String token; private final Date validTo; + private final String audience; - public SecurityToken(final String tokenType, final String token, final Date validTo) { + public SecurityToken(final String token, final Date validTo, final String audience, final String tokenType) { this.tokenType = tokenType; this.token = token; this.validTo = validTo; + this.audience = audience; } public String getTokenType() { @@ -27,4 +29,8 @@ public String getToken() { public Date validTo() { return this.validTo; } + + public String getAudience() { + return this.audience; + } } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ActiveClientTokenManager.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ActiveClientTokenManager.java index 63a20902717f..4c4bcd884420 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ActiveClientTokenManager.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ActiveClientTokenManager.java @@ -19,7 +19,7 @@ final class ActiveClientTokenManager { private final Duration tokenRefreshInterval; private final SchedulerProvider schedulerProvider; private final Timer timerScheduler; - private CompletableFuture timer; + private CompletableFuture timer; ActiveClientTokenManager( final ClientEntity clientEntity, diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java index ded3ce6da5cb..d21cef26be60 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java @@ -50,6 +50,7 @@ public final class ClientConstants { public static final String PUT_TOKEN_EXPIRY = "expiration"; public static final String PUT_TOKEN_STATUS_CODE = "status-code"; public static final String PUT_TOKEN_STATUS_DESCRIPTION = "status-description"; + public final static String EVENTHUBS_AUDIENCE = "https://eventhubs.azure.net/"; public static final String MANAGEMENT_ADDRESS = "$management"; public static final String MANAGEMENT_EVENTHUB_ENTITY_TYPE = AmqpConstants.VENDOR + ":eventhub"; public static final String MANAGEMENT_PARTITION_ENTITY_TYPE = AmqpConstants.VENDOR + ":partition"; diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java index 0711dc397b4e..68ec31a8506a 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java @@ -23,8 +23,6 @@ import java.io.IOException; import java.net.URI; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.util.Date; import java.util.HashMap; @@ -99,15 +97,13 @@ public static CompletableFuture create( if (tokenProvider == null) { throw new IllegalArgumentException("Token provider cannot be null"); } - if (options == null) { - throw new IllegalArgumentException("Options cannot be null"); - } final EventHubClientImpl eventHubClient = new EventHubClientImpl(eventHubName, executor); - final MessagingFactoryBuilder builder = new MessagingFactoryBuilder(endpoint.getHost(), tokenProvider, executor). - setOperationTimeout(options.getOperationTimeout()). - setTransportType(options.getTransportType()). - setRetryPolicy(options.getRetryPolicy()); + final MessagingFactoryBuilder builder = new MessagingFactoryBuilder(endpoint.getHost(), tokenProvider, executor); + if (options != null) { + builder.setOperationTimeout(options.getOperationTimeout()).setTransportType(options.getTransportType()). + setRetryPolicy(options.getRetryPolicy()); + } return builder.build() .thenApplyAsync(new Function() { diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java index 45460d8554ff..8601cdf8016d 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java @@ -4,10 +4,12 @@ package com.microsoft.azure.eventhubs.impl; +import com.google.common.base.Strings; import com.microsoft.azure.eventhubs.CommunicationException; import com.microsoft.azure.eventhubs.ConnectionStringBuilder; import com.microsoft.azure.eventhubs.EventHubException; import com.microsoft.azure.eventhubs.ITokenProvider; +import com.microsoft.azure.eventhubs.ManagedIdentityTokenProvider; import com.microsoft.azure.eventhubs.OperationCancelledException; import com.microsoft.azure.eventhubs.RetryPolicy; import com.microsoft.azure.eventhubs.TimeoutException; @@ -135,9 +137,17 @@ public static CompletableFuture createFromConnectionString( final ScheduledExecutorService executor, final ReactorFactory reactorFactory) throws IOException { final ConnectionStringBuilder csb = new ConnectionStringBuilder(connectionString); - final ITokenProvider tokenProvider = ((csb.getSharedAccessSignature() == null) ? - new SharedAccessSignatureTokenProvider(csb.getSasKeyName(), csb.getSasKey()) : - new SharedAccessSignatureTokenProvider(csb.getSharedAccessSignature())); + ITokenProvider tokenProvider = null; + if (!StringUtil.isNullOrWhiteSpace(csb.getSharedAccessSignature())) { + tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSharedAccessSignature()); + } else if (!StringUtil.isNullOrWhiteSpace(csb.getSasKey())) { + tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSasKeyName(), csb.getSasKey()); + } else if ((csb.getAuthentication() != null) && csb.getAuthentication().equalsIgnoreCase("Managed Identity")) { + tokenProvider = new ManagedIdentityTokenProvider(); + } else { + throw new IllegalArgumentException("Connection string must specify a Shared Access Signature, Shared Access Key, or Managed Identity"); + } + final MessagingFactoryBuilder builder = new MessagingFactoryBuilder(csb.getEndpoint().getHost(), tokenProvider, executor). setOperationTimeout(csb.getOperationTimeout()). setTransportType(csb.getTransportType()). diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java index 047b629bb637..544ffe2b0ed5 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/SharedAccessSignatureTokenProvider.java @@ -19,7 +19,6 @@ import java.util.Date; import java.util.Locale; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import static java.nio.charset.StandardCharsets.UTF_8; @@ -86,19 +85,17 @@ public static String generateSharedAccessSignature( public CompletableFuture getToken(final String resource, final Duration tokenTimeToLive) { final CompletableFuture result = new CompletableFuture<>(); - final String token; if (this.sharedAccessSignature == null) { try { // Generation is nonblocking so there is no need to run it async. - token = generateSharedAccessSignature(this.keyName, this.sharedAccessKey, resource, ClientConstants.TOKEN_VALIDITY); + final String token = generateSharedAccessSignature(this.keyName, this.sharedAccessKey, resource, ClientConstants.TOKEN_VALIDITY); + result.complete(new SecurityToken(token, Date.from(Instant.now().plus(ClientConstants.TOKEN_VALIDITY)), resource, ClientConstants.SAS_TOKEN_TYPE)); } catch (NoSuchAlgorithmException|IOException|InvalidKeyException e) { result.completeExceptionally(e); - return result; } - result.complete(new SecurityToken(ClientConstants.SAS_TOKEN_TYPE, token, Date.from(Instant.now().plus(ClientConstants.TOKEN_VALIDITY)))); } else { - result.complete(new SecurityToken(ClientConstants.SAS_TOKEN_TYPE, this.sharedAccessSignature, Date.from(Instant.now().plus(ClientConstants.TOKEN_VALIDITY)))); + result.complete(new SecurityToken(this.sharedAccessSignature, Date.from(Instant.now().plus(ClientConstants.TOKEN_VALIDITY)), resource, ClientConstants.SAS_TOKEN_TYPE)); } return result; diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/ConcurrentReceiversTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/ConcurrentReceiversTest.java index b8fc071c9930..2e9e4ea18971 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/ConcurrentReceiversTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/ConcurrentReceiversTest.java @@ -41,7 +41,7 @@ public class ConcurrentReceiversTest extends ApiTestBase { public static void initialize() throws InterruptedException, ExecutionException, EventHubException, IOException { connStr = TestContext.getConnectionString(); - sender = EventHubClient.create(connStr.toString(), TestContext.EXECUTOR_SERVICE).get(); + sender = EventHubClient.createFromConnectionString(connStr.toString(), TestContext.EXECUTOR_SERVICE).get(); partitionCount = sender.getRuntimeInformation().get().getPartitionCount(); receivers = new PartitionReceiver[partitionCount]; consumerGroupName = TestContext.getConsumerGroupName(); @@ -56,7 +56,7 @@ public static void cleanup() throws EventHubException { @Test() public void testParallelCreationOfReceivers() throws EventHubException, IOException, InterruptedException, ExecutionException, TimeoutException { - ehClient = EventHubClient.createSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); ReceiveAtleastOneEventValidator[] counter = new ReceiveAtleastOneEventValidator[partitionCount]; @SuppressWarnings("unchecked") CompletableFuture[] validationSignals = new CompletableFuture[partitionCount]; diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/EventHubClientTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/EventHubClientTest.java index d60ffbd78244..e30f7dc402af 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/EventHubClientTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/concurrency/EventHubClientTest.java @@ -32,7 +32,7 @@ public void testParallelEventHubClients() throws Exception { try { ConnectionStringBuilder connectionString = TestContext.getConnectionString(); for (int i = 0; i < noOfClients; i++) { - createFutures[i] = EventHubClient.create(connectionString.toString(), executorService); + createFutures[i] = EventHubClient.createFromConnectionString(connectionString.toString(), executorService); } CompletableFuture.allOf(createFutures).get(); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/connstrbuilder/TransportTypeTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/connstrbuilder/TransportTypeTest.java index 1b6ce12cc4a8..115b907599f8 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/connstrbuilder/TransportTypeTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/connstrbuilder/TransportTypeTest.java @@ -34,7 +34,7 @@ public void transportTypeAmqpCreatesConnectionWithPort5671() throws Exception { ConnectionStringBuilder builder = new ConnectionStringBuilder(TestContext.getConnectionString().toString()); builder.setTransportType(TransportType.AMQP); - EventHubClient ehClient = EventHubClient.createSync(builder.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient ehClient = EventHubClient.createFromConnectionStringSync(builder.toString(), TestContext.EXECUTOR_SERVICE); try { EventHubClientImpl eventHubClientImpl = (EventHubClientImpl) ehClient; final Field factoryField = EventHubClientImpl.class.getDeclaredField("underlyingFactory"); @@ -63,7 +63,7 @@ public void transportTypeAmqpWebSocketsCreatesConnectionWithPort443() throws Exc ConnectionStringBuilder builder = new ConnectionStringBuilder(TestContext.getConnectionString().toString()); builder.setTransportType(TransportType.AMQP_WEB_SOCKETS); - EventHubClient ehClient = EventHubClient.createSync(builder.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient ehClient = EventHubClient.createFromConnectionStringSync(builder.toString(), TestContext.EXECUTOR_SERVICE); try { EventHubClientImpl eventHubClientImpl = (EventHubClientImpl) ehClient; final Field factoryField = EventHubClientImpl.class.getDeclaredField("underlyingFactory"); @@ -115,7 +115,7 @@ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { ConnectionStringBuilder builder = new ConnectionStringBuilder(TestContext.getConnectionString().toString()); builder.setTransportType(TransportType.AMQP_WEB_SOCKETS); - EventHubClient ehClient = EventHubClient.createSync(builder.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient ehClient = EventHubClient.createFromConnectionStringSync(builder.toString(), TestContext.EXECUTOR_SERVICE); try { EventHubClientImpl eventHubClientImpl = (EventHubClientImpl) ehClient; final Field factoryField = EventHubClientImpl.class.getDeclaredField("underlyingFactory"); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/BackCompatTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/BackCompatTest.java index bf47fc400b36..7f0ab332ee01 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/BackCompatTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/BackCompatTest.java @@ -63,7 +63,7 @@ public static void initialize() throws EventHubException, IOException, Interrupt final ConnectionStringBuilder connStrBuilder = TestContext.getConnectionString(); final String connectionString = connStrBuilder.toString(); - ehClient = EventHubClient.createSync(connectionString, TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString, TestContext.EXECUTOR_SERVICE); msgFactory = MessagingFactory.createFromConnectionString(connectionString, TestContext.EXECUTOR_SERVICE).get(); receiver = ehClient.createReceiverSync(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromEnqueuedTime(Instant.now())); partitionMsgSender = MessageSender.create(msgFactory, "link1", connStrBuilder.getEventHubName() + "/partitions/" + PARTITION_ID).get(); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/EventDataBatchTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/EventDataBatchTest.java index 29d374d024b0..3e267fdcba6c 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/EventDataBatchTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/EventDataBatchTest.java @@ -24,7 +24,7 @@ public class EventDataBatchTest extends ApiTestBase { @Test(expected = PayloadSizeExceededException.class) public void payloadExceededException() throws EventHubException, IOException { final ConnectionStringBuilder connStrBuilder = TestContext.getConnectionString(); - ehClient = EventHubClient.createSync(connStrBuilder.toString(), Executors.newScheduledThreadPool(1)); + ehClient = EventHubClient.createFromConnectionStringSync(connStrBuilder.toString(), Executors.newScheduledThreadPool(1)); final EventDataBatch batch = ehClient.createBatch(); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropAmqpPropertiesTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropAmqpPropertiesTest.java index d0a40e7b14fa..80477cf73b57 100755 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropAmqpPropertiesTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropAmqpPropertiesTest.java @@ -98,7 +98,7 @@ public static void initialize() throws EventHubException, IOException, Interrupt final ConnectionStringBuilder connStrBuilder = TestContext.getConnectionString(); final String connectionString = connStrBuilder.toString(); - ehClient = EventHubClient.createSync(connectionString, TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString, TestContext.EXECUTOR_SERVICE); msgFactory = MessagingFactory.createFromConnectionString(connectionString, TestContext.EXECUTOR_SERVICE).get(); receiver = ehClient.createReceiverSync(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromEnqueuedTime(Instant.now())); partitionMsgSender = MessageSender.create(msgFactory, "link1", connStrBuilder.getEventHubName() + "/partitions/" + PARTITION_ID).get(); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropEventBodyTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropEventBodyTest.java index 2781e685ef25..1b86eaf1bc9b 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropEventBodyTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/eventdata/InteropEventBodyTest.java @@ -48,7 +48,7 @@ public static void initialize() throws EventHubException, IOException, Interrupt final ConnectionStringBuilder connStrBuilder = TestContext.getConnectionString(); final String connectionString = connStrBuilder.toString(); - ehClient = EventHubClient.createSync(connectionString, TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString, TestContext.EXECUTOR_SERVICE); msgFactory = MessagingFactory.createFromConnectionString(connectionString, TestContext.EXECUTOR_SERVICE).get(); receiver = ehClient.createReceiverSync(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromEnqueuedTime(Instant.now())); partitionSender = ehClient.createPartitionSenderSync(PARTITION_ID); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ClientEntityCreateTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ClientEntityCreateTest.java index adb87f217159..f9e6bfcdc446 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ClientEntityCreateTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ClientEntityCreateTest.java @@ -40,7 +40,7 @@ public void createReceiverShouldRetryAndThrowTimeoutExceptionUponRepeatedTransie final ConnectionStringBuilder localConnStr = new ConnectionStringBuilder(connStr.toString()); localConnStr.setOperationTimeout(Duration.ofSeconds(SHORT_TIMEOUT)); // to retry atleast once - final EventHubClient eventHubClient = EventHubClient.createSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); try { eventHubClient.createReceiverSync("nonexistantcg", PARTITION_ID, EventPosition.fromStartOfStream()); @@ -63,7 +63,7 @@ public void createSenderShouldRetryAndThrowTimeoutExceptionUponRepeatedTransient final ConnectionStringBuilder localConnStr = new ConnectionStringBuilder(connStr.toString()); localConnStr.setOperationTimeout(Duration.ofSeconds(SHORT_TIMEOUT)); // to retry atleast once localConnStr.setEventHubName("nonexistanteventhub"); - final EventHubClient eventHubClient = EventHubClient.createSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); try { eventHubClient.createPartitionSenderSync(PARTITION_ID); @@ -86,7 +86,7 @@ public void createInternalSenderShouldRetryAndThrowTimeoutExceptionUponRepeatedT final ConnectionStringBuilder localConnStr = new ConnectionStringBuilder(connStr.toString()); localConnStr.setOperationTimeout(Duration.ofSeconds(SHORT_TIMEOUT)); // to retry atleast once localConnStr.setEventHubName("nonexistanteventhub"); - final EventHubClient eventHubClient = EventHubClient.createSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); try { eventHubClient.sendSync(EventData.create("Testmessage".getBytes())); @@ -137,7 +137,7 @@ public void accept(MessageReceiver messageReceiver) { try { ConnectionStringBuilder localConnectionStringBuilder = new ConnectionStringBuilder(connStr.toString()); localConnectionStringBuilder.setEventHubName(nonExistentEventHubName); - final EventHubClient eventHubClient = EventHubClient.createSync(localConnectionStringBuilder.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(localConnectionStringBuilder.toString(), TestContext.EXECUTOR_SERVICE); eventHubClient.createReceiverSync(EventHubClient.DEFAULT_CONSUMER_GROUP_NAME, PARTITION_ID, EventPosition.fromStartOfStream()); eventHubClient.closeSync(); } finally { @@ -185,7 +185,7 @@ public void accept(MessageSender messageSender) { try { ConnectionStringBuilder localConnectionStringBuilder = new ConnectionStringBuilder(connStr.toString()); localConnectionStringBuilder.setEventHubName(nonExistentEventHubName); - final EventHubClient eventHubClient = EventHubClient.createSync(localConnectionStringBuilder.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(localConnectionStringBuilder.toString(), TestContext.EXECUTOR_SERVICE); eventHubClient.createPartitionSenderSync(PARTITION_ID); eventHubClient.closeSync(); } finally { @@ -201,7 +201,7 @@ public void createReceiverShouldThrowRespectiveExceptionUponNonTransientErrors() final ConnectionStringBuilder localConnStr = new ConnectionStringBuilder(connStr.toString()); localConnStr.setOperationTimeout(Duration.ofSeconds(SHORT_TIMEOUT)); // to retry atleast once - final EventHubClient eventHubClient = EventHubClient.createSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(localConnStr.toString(), TestContext.EXECUTOR_SERVICE); try { eventHubClient.createReceiverSync("nonexistantcg", PARTITION_ID, EventPosition.fromStartOfStream()); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/MsgFactoryOpenCloseTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/MsgFactoryOpenCloseTest.java index ecb3278fafd2..a5d32ace9776 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/MsgFactoryOpenCloseTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/MsgFactoryOpenCloseTest.java @@ -44,7 +44,7 @@ public void verifyTaskQueueEmptyOnMsgFactoryGracefulClose() throws Exception { final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); try { - final EventHubClient ehClient = EventHubClient.createSync( + final EventHubClient ehClient = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), executor); @@ -72,7 +72,7 @@ public void verifyTaskQueueEmptyOnMsgFactoryWithPumpGracefulClose() throws Excep final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1); try { - final EventHubClient ehClient = EventHubClient.createSync( + final EventHubClient ehClient = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), executor); @@ -150,7 +150,7 @@ public void supplyClosedExecutorServiceToEventHubClient() throws Exception { final ScheduledExecutorService testClosed = new ScheduledThreadPoolExecutor(1); testClosed.shutdown(); - EventHubClient.createSync( + EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed); } @@ -159,7 +159,7 @@ public void supplyClosedExecutorServiceToEventHubClient() throws Exception { public void supplyClosedExecutorServiceToSendOperation() throws Exception { final ScheduledExecutorService testClosed = Executors.newScheduledThreadPool(1); - final EventHubClient temp = EventHubClient.createSync( + final EventHubClient temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed); temp.sendSync(EventData.create("test data - string".getBytes())); @@ -174,7 +174,7 @@ public void supplyClosedExecutorServiceToSendOperation() throws Exception { public void supplyClosedExecutorServiceToReceiveOperation() throws Exception { final ScheduledExecutorService testClosed = new ScheduledThreadPoolExecutor(1); - final PartitionReceiver temp = EventHubClient.createSync( + final PartitionReceiver temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed) .createReceiverSync(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromEndOfStream()); @@ -189,7 +189,7 @@ public void supplyClosedExecutorServiceToReceiveOperation() throws Exception { public void supplyClosedExecutorServiceToCreateLinkOperation() throws Exception { final ScheduledExecutorService testClosed = Executors.newScheduledThreadPool(1); - final EventHubClient temp = EventHubClient.createSync( + final EventHubClient temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed); @@ -204,7 +204,7 @@ public void supplyClosedExecutorServiceToCreateLinkOperation() throws Exception public void supplyClosedExecutorServiceToCreateSenderOperation() throws Exception { final ScheduledExecutorService testClosed = new ScheduledThreadPoolExecutor(1); - final EventHubClient temp = EventHubClient.createSync( + final EventHubClient temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed); @@ -218,7 +218,7 @@ public void supplyClosedExecutorServiceToCreateSenderOperation() throws Exceptio public void supplyClosedExecutorServiceToCreateReceiverOperation() throws Exception { final ScheduledExecutorService testClosed = Executors.newScheduledThreadPool(1); - final EventHubClient temp = EventHubClient.createSync( + final EventHubClient temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed); @@ -232,7 +232,7 @@ public void supplyClosedExecutorServiceToCreateReceiverOperation() throws Except public void supplyClosedExecutorServiceThenMgmtOperation() throws Throwable { final ScheduledThreadPoolExecutor testClosed = new ScheduledThreadPoolExecutor(1); - final EventHubClient temp = EventHubClient.createSync( + final EventHubClient temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed); @@ -250,7 +250,7 @@ public void supplyClosedExecutorServiceThenMgmtOperation() throws Throwable { public void supplyClosedExecutorServiceThenFactoryCloseOperation() throws Exception { final ScheduledExecutorService testClosed = Executors.newScheduledThreadPool(1); - final EventHubClient temp = EventHubClient.createSync( + final EventHubClient temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed); @@ -264,7 +264,7 @@ public void supplyClosedExecutorServiceThenFactoryCloseOperation() throws Except public void supplyClosedExecutorServiceThenSenderCloseOperation() throws Exception { final ScheduledThreadPoolExecutor testClosed = new ScheduledThreadPoolExecutor(1); - final PartitionSender temp = EventHubClient.createSync( + final PartitionSender temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed).createPartitionSenderSync(PARTITION_ID); @@ -278,7 +278,7 @@ public void supplyClosedExecutorServiceThenSenderCloseOperation() throws Excepti public void supplyClosedExecutorServiceThenReceiverCloseOperation() throws Exception { final ScheduledExecutorService testClosed = Executors.newScheduledThreadPool(1); - final PartitionReceiver temp = EventHubClient.createSync( + final PartitionReceiver temp = EventHubClient.createFromConnectionStringSync( TestContext.getConnectionString().toString(), testClosed).createReceiverSync(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromEndOfStream()); @@ -291,7 +291,7 @@ public void supplyClosedExecutorServiceThenReceiverCloseOperation() throws Excep @Test(expected = RejectedExecutionException.class) public void testEventHubClientSendAfterClose() throws Exception { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - final EventHubClient eventHubClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); eventHubClient.closeSync(); eventHubClient.sendSync(EventData.create("test message".getBytes())); } @@ -299,7 +299,7 @@ public void testEventHubClientSendAfterClose() throws Exception { @Test(expected = IllegalStateException.class) public void testEventHubClientSendCloseAfterSomeSends() throws Exception { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - final EventHubClient eventHubClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); eventHubClient.sendSync(EventData.create("test message".getBytes())); eventHubClient.closeSync(); eventHubClient.sendSync(EventData.create("test message".getBytes())); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReactorFaultTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReactorFaultTest.java index f3cb525cb257..2f56cfd238aa 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReactorFaultTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReactorFaultTest.java @@ -38,7 +38,7 @@ public static void initialize() { @Ignore("TODO: Investigate testcase. This fails.") @Test() public void verifyReactorRestartsOnProtonBugs() throws Exception { - final EventHubClient eventHubClient = EventHubClient.createSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); try { final PartitionReceiver partitionReceiver = eventHubClient.createEpochReceiverSync( "$default", "0", EventPosition.fromStartOfStream(), System.currentTimeMillis()); @@ -84,7 +84,7 @@ public void handle(org.apache.qpid.proton.engine.Event e) { @Test() public void verifyTransportAbort() throws Exception { - final EventHubClient eventHubClient = EventHubClient.createSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClient eventHubClient = EventHubClient.createFromConnectionStringSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); try { final PartitionReceiver partitionReceiver = eventHubClient.createEpochReceiverSync( "$default", "0", EventPosition.fromStartOfStream(), System.currentTimeMillis()); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReceiverEpochTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReceiverEpochTest.java index 75738ad5c27f..e82d3d907c4c 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReceiverEpochTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/ReceiverEpochTest.java @@ -35,7 +35,7 @@ public class ReceiverEpochTest extends ApiTestBase { @BeforeClass public static void initializeEventHub() throws EventHubException, IOException { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); } @AfterClass diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SecurityExceptionsTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SecurityExceptionsTest.java index b070a110e0ad..ef0695d54e9f 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SecurityExceptionsTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SecurityExceptionsTest.java @@ -36,7 +36,7 @@ public void testEventHubClientUnAuthorizedAccessKeyName() throws Throwable { .setSasKeyName("---------------wrongkey------------") .setSasKey(correctConnectionString.getSasKey()); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.sendSync(EventData.create("Test Message".getBytes())); } @@ -49,7 +49,7 @@ public void testEventHubClientUnAuthorizedAccessKey() throws Throwable { .setSasKeyName(correctConnectionString.getSasKeyName()) .setSasKey("--------------wrongvalue-----------"); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.sendSync(EventData.create("Test Message".getBytes())); } @@ -62,7 +62,7 @@ public void testEventHubClientInvalidAccessToken() throws Throwable { .setSharedAccessSignature("--------------invalidtoken-------------") .setOperationTimeout(Duration.ofSeconds(15)); - ehClient = EventHubClient.createSync(connectionString.toString(), RetryPolicy.getNoRetry(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), RetryPolicy.getNoRetry(), TestContext.EXECUTOR_SERVICE); try { ehClient.sendSync(EventData.create(("Test Message".getBytes()))); @@ -80,7 +80,7 @@ public void testEventHubClientNullKeyNameAndAccessToken() throws Throwable { .setSharedAccessSignature(null) .setOperationTimeout(Duration.ofSeconds(10)); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.sendSync(EventData.create(("Test Message".getBytes()))); } @@ -97,7 +97,7 @@ public void testEventHubClientUnAuthorizedAccessToken() throws Throwable { .setEventHubName(correctConnectionString.getEventHubName()) .setSharedAccessSignature(wrongToken); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.sendSync(EventData.create("Test Message".getBytes())); } @@ -114,7 +114,7 @@ public void testEventHubClientWrongResourceInAccessToken() throws Throwable { .setEventHubName(correctConnectionString.getEventHubName()) .setSharedAccessSignature(wrongToken); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.sendSync(EventData.create("Test Message".getBytes())); } @@ -127,7 +127,7 @@ public void testUnAuthorizedAccessSenderCreation() throws Throwable { .setSasKeyName("------------wrongkeyname----------") .setSasKey(correctConnectionString.getSasKey()); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.createPartitionSenderSync(PARTITION_ID); } @@ -140,7 +140,7 @@ public void testUnAuthorizedAccessReceiverCreation() throws Throwable { .setSasKeyName("---------------wrongkey------------") .setSasKey(correctConnectionString.getSasKey()); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.createReceiverSync(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromStartOfStream()); } @@ -153,7 +153,7 @@ public void testSendToNonExistentEventHub() throws Throwable { .setSasKeyName(correctConnectionString.getSasKeyName()) .setSasKey(correctConnectionString.getSasKey()); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.sendSync(EventData.create("test string".getBytes())); } @@ -166,7 +166,7 @@ public void testReceiveFromNonExistentEventHub() throws Throwable { .setSasKeyName(correctConnectionString.getSasKeyName()) .setSasKey(correctConnectionString.getSasKey()); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehClient.createReceiverSync(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromStartOfStream()); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SendLargeMessageTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SendLargeMessageTest.java index dee6bcbc38ed..4ae26dcbf1fa 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SendLargeMessageTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/exceptioncontracts/SendLargeMessageTest.java @@ -36,10 +36,10 @@ public static void initialize() throws Exception { } public static void initializeEventHubClients(ConnectionStringBuilder connStr) throws Exception { - ehClient = EventHubClient.createSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); sender = ehClient.createPartitionSender(PARTITION_ID).get(); - receiverHub = EventHubClient.createSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); + receiverHub = EventHubClient.createFromConnectionStringSync(connStr.toString(), TestContext.EXECUTOR_SERVICE); receiver = receiverHub.createReceiver(TestContext.getConsumerGroupName(), PARTITION_ID, EventPosition.fromEnqueuedTime(Instant.now())).get(); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/proxy/ProxySelectorTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/proxy/ProxySelectorTest.java index acadd6b1d19d..ae755cc32b4e 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/proxy/ProxySelectorTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/proxy/ProxySelectorTest.java @@ -52,7 +52,7 @@ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { builder.setOperationTimeout(Duration.ofSeconds(10)); try { - EventHubClient.createSync(builder.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient.createFromConnectionStringSync(builder.toString(), TestContext.EXECUTOR_SERVICE); Assert.fail(); } catch (EventHubException ex) { // The message can vary because it is returned from proton-j, so we don't want to compare against that. diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/EventDataBatchAPITest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/EventDataBatchAPITest.java index 7f4750197b37..9f2707e23dcc 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/EventDataBatchAPITest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/EventDataBatchAPITest.java @@ -43,7 +43,7 @@ public class EventDataBatchAPITest extends ApiTestBase { @BeforeClass public static void initializeEventHub() throws Exception { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); sender = ehClient.createPartitionSenderSync(PARTITION_ID); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveParallelManualTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveParallelManualTest.java index eb73673bf219..b6a9e8272439 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveParallelManualTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveParallelManualTest.java @@ -38,10 +38,10 @@ public static void initializeEventHub() throws Exception { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); ehClient = new EventHubClient[4]; - ehClient[0] = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); - ehClient[1] = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); - ehClient[2] = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); - ehClient[3] = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient[0] = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient[1] = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient[2] = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient[3] = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); } @AfterClass() diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceivePumpEventHubTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceivePumpEventHubTest.java index 30fba5766ef5..085ea3ac3fc0 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceivePumpEventHubTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceivePumpEventHubTest.java @@ -37,7 +37,7 @@ public class ReceivePumpEventHubTest extends ApiTestBase { @BeforeClass public static void initializeEventHub() throws EventHubException, IOException { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); } @AfterClass diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveTest.java index ef25a601ca55..2638e7b8477a 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiveTest.java @@ -43,7 +43,7 @@ public static void initialize() throws Exception { } public static void initializeEventHub(ConnectionStringBuilder connectionString) throws Exception { - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); TestBase.pushEventsToPartition(ehClient, PARTITION_ID, 25).get(); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverIdentifierTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverIdentifierTest.java index b1a40ba076ce..b781f99ce414 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverIdentifierTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverIdentifierTest.java @@ -35,7 +35,7 @@ public class ReceiverIdentifierTest extends ApiTestBase { public static void initializeEventHub() throws Exception { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); TestBase.pushEventsToPartition(ehClient, PARTITION_ID, SENT_EVENTS).get(); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverRuntimeMetricsTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverRuntimeMetricsTest.java index cb1eb8b46bc9..a791a7692484 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverRuntimeMetricsTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ReceiverRuntimeMetricsTest.java @@ -37,7 +37,7 @@ public class ReceiverRuntimeMetricsTest extends ApiTestBase { public static void initializeEventHub() throws Exception { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ReceiverOptions options = new ReceiverOptions(); options.setReceiverRuntimeMetricEnabled(true); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/RequestResponseTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/RequestResponseTest.java index 13b24831731b..f90719c4264b 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/RequestResponseTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/RequestResponseTest.java @@ -216,7 +216,7 @@ public void testGetRuntimes() throws Exception { } public void testGetRuntimeInfos(ConnectionStringBuilder connectionString) throws Exception { - EventHubClient ehc = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient ehc = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); EventHubRuntimeInformation ehInfo = ehc.getRuntimeInformation().get(); Assert.assertNotNull(ehInfo); @@ -258,7 +258,7 @@ public void testGetRuntimesWebSockets() throws Exception { @Test public void testGetRuntimeInfoCallTimesout() throws Exception { - final EventHubClientImpl eventHubClient = (EventHubClientImpl) EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + final EventHubClientImpl eventHubClient = (EventHubClientImpl) EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); // set operation timeout to 5ms - so that the actual operation doesn't event start final Field factoryField = EventHubClientImpl.class.getDeclaredField("underlyingFactory"); @@ -288,7 +288,7 @@ public void testGetRuntimesBadHub() throws EventHubException, IOException { .setEventHubName("NOHUBZZZZZ") .setSasKeyName(connectionString.getSasKeyName()) .setSasKey(connectionString.getSasKey()); - EventHubClient ehc = EventHubClient.createSync(bogusConnectionString.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient ehc = EventHubClient.createFromConnectionStringSync(bogusConnectionString.toString(), TestContext.EXECUTOR_SERVICE); try { ehc.getRuntimeInformation().get(); @@ -330,7 +330,7 @@ public void testGetRuntimesBadKeyname() throws EventHubException, IOException { .setEventHubName(connectionString.getEventHubName()) .setSasKeyName("xxxnokeyxxx") .setSasKey(connectionString.getSasKey()); - EventHubClient ehc = EventHubClient.createSync(bogusConnectionString.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient ehc = EventHubClient.createFromConnectionStringSync(bogusConnectionString.toString(), TestContext.EXECUTOR_SERVICE); try { ehc.getRuntimeInformation().get(); @@ -357,7 +357,7 @@ public void testGetRuntimesBadKeyname() throws EventHubException, IOException { @Test public void testGetRuntimesClosedClient() throws EventHubException, IOException, InterruptedException, ExecutionException { - EventHubClient ehc = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + EventHubClient ehc = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); ehc.closeSync(); try { diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SendTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SendTest.java index 3006234578c9..3ff1f229f58b 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SendTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SendTest.java @@ -46,7 +46,7 @@ public static void initialize() throws Exception { } public static void initializeEventHub(final ConnectionStringBuilder connectionString) throws Exception { - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); } @AfterClass diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SetPrefetchCountTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SetPrefetchCountTest.java index b2dce20bf93e..6f15ff6a1f6c 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SetPrefetchCountTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/SetPrefetchCountTest.java @@ -39,7 +39,7 @@ public class SetPrefetchCountTest extends ApiTestBase { @BeforeClass public static void initializeEventHub() throws Exception { final ConnectionStringBuilder connectionString = TestContext.getConnectionString(); - ehClient = EventHubClient.createSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); + ehClient = EventHubClient.createFromConnectionStringSync(connectionString.toString(), TestContext.EXECUTOR_SERVICE); TestBase.pushEventsToPartition(ehClient, PARTITION_ID, EVENT_COUNT).get(); } From 991d7106ad31930b0d40d84ee07086394325aefb Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Fri, 17 May 2019 19:28:13 -0700 Subject: [PATCH 03/22] Better null testing --- .../eventhubs/impl/EventHubClientImpl.java | 9 ++--- .../eventhubs/impl/MessagingFactory.java | 36 +++++-------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java index 68ec31a8506a..0bab13ec226b 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; @@ -64,9 +65,7 @@ public static CompletableFuture create( if (StringUtil.isNullOrWhiteSpace(connectionString)) { throw new IllegalArgumentException("Connection string cannot be null or empty"); } - if (executor == null) { - throw new IllegalArgumentException("Executor cannot be null"); - } + Objects.requireNonNull(executor, "Executor cannot be null"); final ConnectionStringBuilder connStr = new ConnectionStringBuilder(connectionString); final EventHubClientImpl eventHubClient = new EventHubClientImpl(connStr.getEventHubName(), executor); @@ -94,9 +93,7 @@ public static CompletableFuture create( if (StringUtil.isNullOrWhiteSpace(eventHubName)) { throw new IllegalArgumentException("Event hub name cannot be null or empty"); } - if (tokenProvider == null) { - throw new IllegalArgumentException("Token provider cannot be null"); - } + Objects.requireNonNull(tokenProvider, "Token provider cannot be null"); final EventHubClientImpl eventHubClient = new EventHubClientImpl(eventHubName, executor); final MessagingFactoryBuilder builder = new MessagingFactoryBuilder(endpoint.getHost(), tokenProvider, executor); diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java index 8601cdf8016d..85aea18a23a0 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java @@ -36,6 +36,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.RejectedExecutionException; @@ -86,24 +87,12 @@ public final class MessagingFactory extends ClientEntity implements AmqpConnecti if (StringUtil.isNullOrWhiteSpace(hostname)) { throw new IllegalArgumentException("Endpoint hostname cannot be null or empty"); } - if (operationTimeout == null) { - throw new IllegalArgumentException("Operation timeout cannot be null"); - } - if (transportType == null) { - throw new IllegalArgumentException("Transport type cannot be null"); - } - if (tokenProvider == null) { - throw new IllegalArgumentException("Token provider cannot be null"); - } - if (retryPolicy == null) { - throw new IllegalArgumentException("Retry policy cannot be null"); - } - if (executor == null) { - throw new IllegalArgumentException("Executor cannot be null"); - } - if (reactorFactory == null) { - throw new IllegalArgumentException("Reactor factory cannot be null"); - } + Objects.requireNonNull(operationTimeout, "Operation timeout cannot be null"); + Objects.requireNonNull(transportType, "Transport type cannot be null"); + Objects.requireNonNull(tokenProvider, "Token provider cannot be null"); + Objects.requireNonNull(retryPolicy, "Retry policy cannot be null"); + Objects.requireNonNull(executor, "Executor cannot be null"); + Objects.requireNonNull(reactorFactory, "Reactor factory cannot be null"); this.hostName = hostname; this.reactorFactory = reactorFactory; @@ -174,15 +163,8 @@ public MessagingFactoryBuilder(final String hostname, final ITokenProvider token } this.hostname = hostname; - if (tokenProvider == null) { - throw new IllegalArgumentException("Token provider cannot be null"); - } - this.tokenProvider = tokenProvider; - - if (executor == null) { - throw new IllegalArgumentException("Executor cannot be null"); - } - this.executor = executor; + this.tokenProvider = Objects.requireNonNull(tokenProvider); + this.executor = Objects.requireNonNull(executor); } public MessagingFactoryBuilder setOperationTimeout(Duration operationTimeout) { From 5767a80dcc3d07fdec45d0150fcd7b2ecf21b714 Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Tue, 21 May 2019 18:43:56 -0700 Subject: [PATCH 04/22] RBAC in EPH --- .../EventHubClientFactory.java | 113 ++++ .../EventProcessorHost.java | 515 ++++++++++-------- .../azure/eventprocessorhost/HostContext.java | 18 +- .../eventprocessorhost/PartitionManager.java | 2 +- .../eventprocessorhost/PartitionPump.java | 3 +- .../eventhubs/EventHubClientOptions.java | 9 +- 6 files changed, 410 insertions(+), 250 deletions(-) create mode 100644 eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java new file mode 100644 index 000000000000..29a9529b10a1 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.azure.eventprocessorhost; + +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; + +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; +import com.microsoft.azure.eventhubs.EventHubClient; +import com.microsoft.azure.eventhubs.EventHubClientOptions; +import com.microsoft.azure.eventhubs.EventHubException; +import com.microsoft.azure.eventhubs.ITokenProvider; +import com.microsoft.azure.eventhubs.RetryPolicy; + +public abstract class EventHubClientFactory { + protected final ScheduledExecutorService executor; + + protected final EventHubClientOptions options; + + public EventHubClientFactory(final ScheduledExecutorService executor, final RetryPolicy retryPolicy) { + this(executor, (new EventHubClientOptions()).setRetryPolicy(retryPolicy)); + } + + public EventHubClientFactory(final ScheduledExecutorService executor, + final EventHubClientOptions options) { + this.executor = executor; + this.options = options; + } + + abstract CompletableFuture createEventHubClient() throws EventHubException, IOException; + + public static class EHCFWithConnectionString extends EventHubClientFactory { + private final String eventHubConnectionString; + + public EHCFWithConnectionString(final String eventHubConnectionString, + final RetryPolicy retryPolicy, + final ScheduledExecutorService executor) { + super(executor, retryPolicy); + this.eventHubConnectionString = eventHubConnectionString; + } + + public CompletableFuture createEventHubClient() throws EventHubException, IOException { + return EventHubClient.createFromConnectionString(this.eventHubConnectionString, this.options.getRetryPolicy(), this.executor); + } + } + + public static class EHCFWithAuthCallback extends EventHubClientFactory { + private final URI endpoint; + private final String eventHubPath; + private final AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback; + private final String authority; + + public EHCFWithAuthCallback(final URI endpoint, + final String eventHubPath, + final AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback, + final String authority, + final EventHubClientOptions options, + final ScheduledExecutorService executor) { + super(executor, options); + this.endpoint = endpoint; + this.eventHubPath = eventHubPath; + this.authCallback = authCallback; + this.authority = authority; + } + + public CompletableFuture createEventHubClient() throws EventHubException, IOException { + return EventHubClient.createWithAzureActiveDirectory(this.endpoint, + this.eventHubPath, this.authCallback, this.authority, this.executor, this.options); + } + } + + public static class EHCFWithTokenProvider extends EventHubClientFactory { + private final URI endpoint; + private final String eventHubPath; + private final ITokenProvider tokenProvider; + + public EHCFWithTokenProvider(final URI endpoint, + final String eventHubPath, + final ITokenProvider tokenProvider, + final EventHubClientOptions options, + final ScheduledExecutorService executor) { + super(executor, options); + this.endpoint = endpoint; + this.eventHubPath = eventHubPath; + this.tokenProvider = tokenProvider; + } + + public CompletableFuture createEventHubClient() throws EventHubException, IOException { + return EventHubClient.createWithTokenProvider(this.endpoint, this.eventHubPath, this.tokenProvider, this.executor, this.options); + } + } + + public static class EHCFWithManagedIdentity extends EventHubClientFactory { + private final URI endpoint; + private final String eventHubPath; + + public EHCFWithManagedIdentity(final URI endpoint, + final String eventHubPath, + final EventHubClientOptions options, + final ScheduledExecutorService executor) { + super(executor, options); + this.endpoint = endpoint; + this.eventHubPath = eventHubPath; + } + + public CompletableFuture createEventHubClient() throws EventHubException, IOException { + return EventHubClient.createWithManagedIdentity(this.endpoint, this.eventHubPath, this.executor, this.options); + } + } +} diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java index c9eaf61e266d..f4ac28974034 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java @@ -3,15 +3,25 @@ package com.microsoft.azure.eventprocessorhost; +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider.AuthenticationCallback; import com.microsoft.azure.eventhubs.ConnectionStringBuilder; +import com.microsoft.azure.eventhubs.EventHubClientOptions; +import com.microsoft.azure.eventhubs.ITokenProvider; import com.microsoft.azure.eventhubs.RetryPolicy; +import com.microsoft.azure.eventhubs.TransportType; +import com.microsoft.azure.eventhubs.impl.StringUtil; import com.microsoft.azure.storage.StorageException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.URI; import java.net.URISyntaxException; import java.security.InvalidKeyException; +import java.time.Duration; import java.util.Locale; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -39,244 +49,24 @@ public final class EventProcessorHost { private PartitionManager partitionManager; private PartitionManagerOptions partitionManagerOptions = null; - /** - * Create a new host instance to process events from an Event Hub. - *

- * Since Event Hubs are generally used for scale-out, high-traffic scenarios, in most scenarios there will - * be only one host instances per process, and the processes will be run on separate machines. Besides scale, this also - * provides isolation: one process or machine crashing will not take out multiple host instances. However, it is - * supported to run multiple host instances on one machine, or even within one process, for development and testing. - *

- * The hostName parameter is a name for this event processor host, which must be unique among all event processor host instances - * receiving from this event hub+consumer group combination: the unique name is used to distinguish which event processor host - * instance owns the lease for a given partition. An easy way to generate a unique hostName which also includes - * other information is to call EventProcessorHost.createHostName("mystring"). - *

- * This overload of the constructor uses the built-in lease and checkpoint managers. The - * Azure Storage account specified by the storageConnectionString parameter is used by the built-in - * managers to record leases and checkpoints, in the specified container. - *

- * The Event Hub connection string may be conveniently constructed using the ConnectionStringBuilder class - * from the Java Event Hub client. - * - * @param hostName A name for this event processor host. See method notes. - * @param eventHubPath Specifies the Event Hub to receive events from. - * @param consumerGroupName The name of the consumer group to use when receiving from the Event Hub. - * @param eventHubConnectionString Connection string for the Event Hub to receive from. - * @param storageConnectionString Connection string for the Azure Storage account to use for persisting leases and checkpoints. - * @param storageContainerName Azure Storage container name for use by built-in lease and checkpoint manager. - */ - public EventProcessorHost( - final String hostName, - final String eventHubPath, - final String consumerGroupName, - final String eventHubConnectionString, - final String storageConnectionString, - final String storageContainerName) { - this(hostName, eventHubPath, consumerGroupName, eventHubConnectionString, storageConnectionString, storageContainerName, (ScheduledExecutorService) null); - } - - /** - * Create a new host to process events from an Event Hub. - *

- * This overload adds an argument to specify a user-provided thread pool. The number of partitions in the - * target event hub and the number of host instances should be considered when choosing the size of the thread pool: - * how many partitions is one instance expected to own under normal circumstances? One thread per partition should - * provide good performance, while being able to support more partitions adequately if a host instance fails and its - * partitions must be redistributed. - * - * @param hostName A name for this event processor host. See method notes. - * @param eventHubPath Specifies the Event Hub to receive events from. - * @param consumerGroupName The name of the consumer group to use when receiving from the Event Hub. - * @param eventHubConnectionString Connection string for the Event Hub to receive from. - * @param storageConnectionString Connection string for the Azure Storage account to use for persisting leases and checkpoints. - * @param storageContainerName Azure Storage container name for use by built-in lease and checkpoint manager. - * @param executorService User-supplied thread executor, or null to use EventProcessorHost-internal executor. - */ - public EventProcessorHost( - final String hostName, - final String eventHubPath, - final String consumerGroupName, - final String eventHubConnectionString, - final String storageConnectionString, - final String storageContainerName, - final ScheduledExecutorService executorService) { - this(hostName, eventHubPath, consumerGroupName, eventHubConnectionString, storageConnectionString, storageContainerName, (String) null, executorService); - } - - /** - * Create a new host to process events from an Event Hub. - *

- * This overload adds an argument to specify a prefix used by the built-in lease manager when naming blobs in Azure Storage. - * - * @param hostName A name for this event processor host. See method notes. - * @param eventHubPath Specifies the Event Hub to receive events from. - * @param consumerGroupName The name of the consumer group to use when receiving from the Event Hub. - * @param eventHubConnectionString Connection string for the Event Hub to receive from. - * @param storageConnectionString Connection string for the Azure Storage account to use for persisting leases and checkpoints. - * @param storageContainerName Azure Storage container name for use by built-in lease and checkpoint manager. - * @param storageBlobPrefix Prefix used when naming blobs within the storage container. - */ - public EventProcessorHost( - final String hostName, - final String eventHubPath, - final String consumerGroupName, - final String eventHubConnectionString, - final String storageConnectionString, - final String storageContainerName, - final String storageBlobPrefix) { - this(hostName, eventHubPath, consumerGroupName, eventHubConnectionString, storageConnectionString, storageContainerName, storageBlobPrefix, - (ScheduledExecutorService) null); - } - - /** - * Create a new host to process events from an Event Hub. - *

- * This overload allows the caller to specify both a user-supplied thread pool and - * a prefix used by the built-in lease manager when naming blobs in Azure Storage. - * - * @param hostName A name for this event processor host. See method notes. - * @param eventHubPath Specifies the Event Hub to receive events from. - * @param consumerGroupName The name of the consumer group to use when receiving from the Event Hub. - * @param eventHubConnectionString Connection string for the Event Hub to receive from. - * @param storageConnectionString Connection string for the Azure Storage account to use for persisting leases and checkpoints. - * @param storageContainerName Azure Storage container name for use by built-in lease and checkpoint manager. - * @param storageBlobPrefix Prefix used when naming blobs within the storage container. - * @param executorService User-supplied thread executor, or null to use EventProcessorHost-internal executor. - */ - public EventProcessorHost( - final String hostName, - final String eventHubPath, - final String consumerGroupName, - final String eventHubConnectionString, - final String storageConnectionString, - final String storageContainerName, - final String storageBlobPrefix, - final ScheduledExecutorService executorService) { - // Would like to check storageConnectionString and storageContainerName here but can't, because Java doesn't allow statements before - // calling another constructor. storageBlobPrefix is allowed to be null or empty, doesn't need checking. - this(hostName, eventHubPath, consumerGroupName, eventHubConnectionString, - new AzureStorageCheckpointLeaseManager(storageConnectionString, storageContainerName, storageBlobPrefix), executorService); - this.initializeLeaseManager = true; - this.partitionManagerOptions = new AzureStoragePartitionManagerOptions(); - } - - // Because Java won't let you do ANYTHING before calling another constructor. In particular, you can't - // new up an object and pass it as TWO parameters of the other constructor. private EventProcessorHost( final String hostName, final String eventHubPath, final String consumerGroupName, - final String eventHubConnectionString, - final AzureStorageCheckpointLeaseManager combinedManager, - final ScheduledExecutorService executorService) { - this(hostName, eventHubPath, consumerGroupName, eventHubConnectionString, combinedManager, combinedManager, executorService, null); - } - - /** - * Create a new host to process events from an Event Hub. - *

- * This overload allows the caller to provide their own lease and checkpoint managers to replace the built-in - * ones based on Azure Storage. - * - * @param hostName A name for this event processor host. See method notes. - * @param eventHubPath Specifies the Event Hub to receive events from. - * @param consumerGroupName The name of the consumer group to use when receiving from the Event Hub. - * @param eventHubConnectionString Connection string for the Event Hub to receive from. - * @param checkpointManager Implementation of ICheckpointManager, to be replacement checkpoint manager. - * @param leaseManager Implementation of ILeaseManager, to be replacement lease manager. - */ - public EventProcessorHost( - final String hostName, - final String eventHubPath, - final String consumerGroupName, - final String eventHubConnectionString, - ICheckpointManager checkpointManager, - ILeaseManager leaseManager) { - this(hostName, eventHubPath, consumerGroupName, eventHubConnectionString, checkpointManager, leaseManager, null, null); - } - - /** - * Create a new host to process events from an Event Hub. - *

- * This overload allows the caller to provide their own lease and checkpoint managers to replace the built-in - * ones based on Azure Storage, and to provide an executor service and a retry policy for communications with the event hub. - * - * @param hostName A name for this event processor host. See method notes. - * @param eventHubPath Specifies the Event Hub to receive events from. - * @param consumerGroupName The name of the consumer group to use when receiving from the Event Hub. - * @param eventHubConnectionString Connection string for the Event Hub to receive from. - * @param checkpointManager Implementation of ICheckpointManager, to be replacement checkpoint manager. - * @param leaseManager Implementation of ILeaseManager, to be replacement lease manager. - * @param executorService User-supplied thread executor, or null to use EventProcessorHost-internal executor. - * @param retryPolicy Retry policy governing communications with the event hub. - */ - public EventProcessorHost( - final String hostName, - final String eventHubPath, - final String consumerGroupName, - final String eventHubConnectionString, + final EventHubClientFactory eventHubClientFactory, ICheckpointManager checkpointManager, ILeaseManager leaseManager, - ScheduledExecutorService executorService, - RetryPolicy retryPolicy) { - if ((hostName == null) || hostName.isEmpty()) { - throw new IllegalArgumentException("hostName argument must not be null or empty string"); - } - - // eventHubPath is allowed to be null or empty if it is provided in the connection string. That will be checked later. - if ((consumerGroupName == null) || consumerGroupName.isEmpty()) { - throw new IllegalArgumentException("consumerGroupName argument must not be null or empty"); - } - - if ((eventHubConnectionString == null) || eventHubConnectionString.isEmpty()) { - throw new IllegalArgumentException("eventHubConnectionString argument must not be null or empty"); - } - - // The event hub path must appear in at least one of the eventHubPath argument or the connection string. - // If it appears in both, then it must be the same in both. If it appears in only one, populate the other. - ConnectionStringBuilder providedCSB = new ConnectionStringBuilder(eventHubConnectionString); - String extractedEntityPath = providedCSB.getEventHubName(); - String effectiveEventHubPath = eventHubPath; - String effectiveEventHubConnectionString = eventHubConnectionString; - if ((effectiveEventHubPath != null) && !effectiveEventHubPath.isEmpty()) { - if (extractedEntityPath != null) { - if (effectiveEventHubPath.compareTo(extractedEntityPath) != 0) { - throw new IllegalArgumentException("Provided EventHub path in eventHubPath parameter conflicts with the path in provided EventHub connection string"); - } - // else they are the same and that's fine - } else { - // There is no entity path in the connection string, so put it there. - ConnectionStringBuilder rebuildCSB = new ConnectionStringBuilder() - .setEndpoint(providedCSB.getEndpoint()) - .setEventHubName(effectiveEventHubPath) - .setSasKeyName(providedCSB.getSasKeyName()) - .setSasKey(providedCSB.getSasKey()); - rebuildCSB.setOperationTimeout(providedCSB.getOperationTimeout()); - effectiveEventHubConnectionString = rebuildCSB.toString(); - } + final boolean initializeLeaseManager, + ScheduledExecutorService executorService) { + this.initializeLeaseManager = initializeLeaseManager; + if (this.initializeLeaseManager) { + this.partitionManagerOptions = new AzureStoragePartitionManagerOptions(); } else { - if ((extractedEntityPath != null) && !extractedEntityPath.isEmpty()) { - effectiveEventHubPath = extractedEntityPath; - } else { - throw new IllegalArgumentException("Provide EventHub entity path in either eventHubPath argument or in eventHubConnectionString"); - } + // Using user-supplied implementation. + // Establish generic defaults in case the user doesn't provide an options object. + this.partitionManagerOptions = new PartitionManagerOptions(); } - if (checkpointManager == null) { - throw new IllegalArgumentException("Must provide an object which implements ICheckpointManager"); - } - if (leaseManager == null) { - throw new IllegalArgumentException("Must provide an object which implements ILeaseManager"); - } - - // executorService argument is allowed to be null, that is the indication to use an internal threadpool. - - // Normally will not be null because we're using the AzureStorage implementation. - // If it is null, we're using user-supplied implementation. Establish generic defaults - // in case the user doesn't provide an options object. - this.partitionManagerOptions = new PartitionManagerOptions(); - if (executorService != null) { // User has supplied an ExecutorService, so use that. this.weOwnExecutor = false; @@ -285,12 +75,12 @@ public EventProcessorHost( this.weOwnExecutor = true; this.executorService = Executors.newScheduledThreadPool( this.executorServicePoolSize, - new EventProcessorHostThreadPoolFactory(hostName, effectiveEventHubPath, consumerGroupName)); + new EventProcessorHostThreadPoolFactory(hostName, eventHubPath, consumerGroupName)); } this.hostContext = new HostContext(this.executorService, this, hostName, - effectiveEventHubPath, consumerGroupName, effectiveEventHubConnectionString, retryPolicy, + eventHubPath, consumerGroupName, eventHubClientFactory, leaseManager, checkpointManager); this.partitionManager = new PartitionManager(hostContext); @@ -571,4 +361,267 @@ public void uncaughtException(Thread t, Throwable e) { } } } + + + public static class EventProcessorHostBuilder { + public static ManagerStep newBuilder(final String hostName, final String consumerGroupName) { + return new Steps(hostName, consumerGroupName); + } + + private EventProcessorHostBuilder() { + } + + public static interface ManagerStep { + AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName); + + AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName, String StorageBlobPrefix); + + AuthStep useUserCheckpointAndLeaseManagers(ICheckpointManager checkpointManager, ILeaseManager leaseManager); + } + + public static interface AuthStep { + OptionalStep useEventHubConnectionString(String eventHubConnectionString); + + OptionalStep useEventHubConnectionString(String eventHubConnectionString, String eventHubPath); + + AADAuthStep useAADAuthentication(URI endpoint, String eventHubPath); + } + + public static interface AADAuthStep { + OptionalStep useAuthenticationCallback(AuthenticationCallback authCallback); + + OptionalStep useAuthenticationCallback(AuthenticationCallback authCallback, String authority); + + OptionalStep useManagedIdentity(); + + OptionalStep useTokenProvider(ITokenProvider tokenProvider); + } + + public static interface OptionalStep { + OptionalStep setExecutor(ScheduledExecutorService executor); + + OptionalStep setRetryPolicy(RetryPolicy retryPolicy); + + OptionalStep setTransportType(TransportType transportType); + + OptionalStep setOperationTimeout(Duration operationTimeout); + + EventProcessorHost build(); + } + + private static class Steps implements ManagerStep, AuthStep, AADAuthStep, OptionalStep { + private final String hostName; + private final String consumerGroupName; + + // OptionalStep + private ScheduledExecutorService executor = null; + private RetryPolicy retryPolicy = null; + private TransportType transportType = null; + private Duration operationTimeout = null; + + // Auth steps + private String eventHubConnectionString = null; // group 1 + private String eventHubPath = null; // optional for group 1, required for groups 2-4 + private URI endpoint = null; // groups 2-4 + private AuthenticationCallback authCallback = null; // group 2 + private String authority = null; // group 2 + private boolean useManagedIdentity = false; // group 3 + private ITokenProvider tokenProvider = null; // group 4 + + // ManagerStep + private ICheckpointManager checkpointManager; + private ILeaseManager leaseManager; + private boolean initializeManagers = false; + + + public Steps(final String hostName, final String consumerGroupName) { + if (StringUtil.isNullOrWhiteSpace(hostName) || StringUtil.isNullOrWhiteSpace(consumerGroupName)) { + throw new IllegalArgumentException("hostName and consumerGroupName cannot be null or empty"); + } + + this.hostName = hostName; + this.consumerGroupName = consumerGroupName; + } + + @Override + public OptionalStep setExecutor(final ScheduledExecutorService executor) { + Objects.requireNonNull(executor); + + this.executor = executor; + return this; + } + + @Override + public OptionalStep setRetryPolicy(final RetryPolicy retryPolicy) { + this.retryPolicy = retryPolicy; + return this; + } + + @Override + public OptionalStep setTransportType(final TransportType transportType) { + Objects.requireNonNull(transportType); + + this.transportType = transportType; + return this; + } + + @Override + public OptionalStep setOperationTimeout(final Duration operationTimeout) { + Objects.requireNonNull(operationTimeout); + + this.operationTimeout = operationTimeout; + return this; + } + + @Override + public OptionalStep useAuthenticationCallback(final AuthenticationCallback authCallback) { + return useAuthenticationCallback(authCallback, AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY); + } + + @Override + public OptionalStep useAuthenticationCallback(final AuthenticationCallback authCallback, final String authority) { + Objects.requireNonNull(authCallback); + if (StringUtil.isNullOrWhiteSpace(authority)) { + throw new IllegalArgumentException("authority cannot be null or empty"); + } + + this.authCallback = authCallback; + this.authority = authority; + return this; + } + + @Override + public OptionalStep useManagedIdentity() { + this.useManagedIdentity = true; + return this; + } + + @Override + public OptionalStep useTokenProvider(final ITokenProvider tokenProvider) { + Objects.requireNonNull(tokenProvider); + + this.tokenProvider = tokenProvider; + return this; + } + + @Override + public OptionalStep useEventHubConnectionString(final String eventHubConnectionString) { + return useEventHubConnectionString(eventHubConnectionString, null); + } + + @Override + public OptionalStep useEventHubConnectionString(final String eventHubConnectionString, final String eventHubPath) { + if (StringUtil.isNullOrWhiteSpace(eventHubConnectionString)) { + throw new IllegalArgumentException("eventHubConnectionString cannot be null or empty"); + } + if ((eventHubPath != null) && StringUtil.isNullOrWhiteSpace(eventHubPath)) { + throw new IllegalArgumentException("eventHubPath cannot be empty. Use null if the connection string already contains the path."); + } + + this.eventHubConnectionString = eventHubConnectionString; + this.eventHubPath = eventHubPath; + return this; + } + + @Override + public AADAuthStep useAADAuthentication(final URI endpoint, final String eventHubPath) { + Objects.requireNonNull(endpoint); + if (StringUtil.isNullOrWhiteSpace(eventHubPath)) { + throw new IllegalArgumentException("eventHubPath cannot be null or empty"); + } + + this.endpoint = endpoint; + this.eventHubPath = eventHubPath; + return this; + } + + @Override + public AuthStep useAzureStorageCheckpointLeaseManager(final String storageConnectionString, + final String storageContainerName) { + return useAzureStorageCheckpointLeaseManager(storageConnectionString, storageContainerName, ""); + } + + @Override + public AuthStep useAzureStorageCheckpointLeaseManager(final String storageConnectionString, + final String storageContainerName, final String storageBlobPrefix) { + AzureStorageCheckpointLeaseManager mgr = new AzureStorageCheckpointLeaseManager(storageConnectionString, storageContainerName, storageBlobPrefix); + this.initializeManagers = true; + return useUserCheckpointAndLeaseManagers(mgr, mgr); + } + + @Override + public AuthStep useUserCheckpointAndLeaseManagers(final ICheckpointManager checkpointManager, + final ILeaseManager leaseManager) { + Objects.requireNonNull(checkpointManager); + Objects.requireNonNull(leaseManager); + + this.checkpointManager = checkpointManager; + this.leaseManager = leaseManager; + return this; + } + + @Override + public EventProcessorHost build() { + // One of these conditions MUST be true. Can't get to the OptionalStep interface where build() is available + // without setting one of the auth options. + EventHubClientFactory ehcFactory = null; + if (this.eventHubConnectionString != null) { + normalizeConnectionStringAndEventHubPath(); + ehcFactory = new EventHubClientFactory.EHCFWithConnectionString(this.eventHubConnectionString, this.retryPolicy, this.executor); + } else if (this.authCallback != null) { + ehcFactory = new EventHubClientFactory.EHCFWithAuthCallback(this.endpoint, this.eventHubPath, + this.authCallback, this.authority, packOptions(), this.executor); + } else if (this.tokenProvider != null) { + ehcFactory = new EventHubClientFactory.EHCFWithTokenProvider(this.endpoint, this.eventHubPath, this.tokenProvider, packOptions(), this.executor); + } else if (this.useManagedIdentity) { + ehcFactory = new EventHubClientFactory.EHCFWithManagedIdentity(this.endpoint, this.eventHubPath, packOptions(), this.executor); + } + return new EventProcessorHost(this.hostName, + this.eventHubPath, + this.consumerGroupName, + ehcFactory, + this.checkpointManager, + this.leaseManager, + this.initializeManagers, + this.executor); + } + + private EventHubClientOptions packOptions() { + return (new EventHubClientOptions()).setOperationTimeout(this.operationTimeout).setRetryPolicy(this.retryPolicy).setTransportType(this.transportType); + } + + private void normalizeConnectionStringAndEventHubPath() { + // The event hub path must appear in at least one of the eventHubPath argument or the connection string. + // If it appears in both, then it must be the same in both. If it appears in only one, populate the other. + ConnectionStringBuilder csb = new ConnectionStringBuilder(this.eventHubConnectionString); + String extractedEntityPath = csb.getEventHubName(); + if ((this.eventHubPath != null) && !this.eventHubPath.isEmpty()) { + if (extractedEntityPath != null) { + if (this.eventHubPath.compareTo(extractedEntityPath) != 0) { + throw new IllegalArgumentException("Provided EventHub path in eventHubPath parameter conflicts with the path in provided EventHub connection string"); + } + // else they are the same and that's fine + } else { + // There is no entity path in the connection string, so put it there. + csb.setEventHubName(this.eventHubPath); + } + } else { + if ((extractedEntityPath != null) && !extractedEntityPath.isEmpty()) { + this.eventHubPath = extractedEntityPath; + } else { + throw new IllegalArgumentException("Provide EventHub entity path in either eventHubPath argument or in eventHubConnectionString"); + } + } + + if (this.transportType != null) { + csb.setTransportType(this.transportType); + } + if (this.operationTimeout != null) { + csb.setOperationTimeout(this.operationTimeout); + } + + this.eventHubConnectionString = csb.toString(); + } + } + } } diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/HostContext.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/HostContext.java index 1998a782e97a..26b7cc0285ed 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/HostContext.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/HostContext.java @@ -3,8 +3,6 @@ package com.microsoft.azure.eventprocessorhost; -import com.microsoft.azure.eventhubs.RetryPolicy; - import java.util.concurrent.ScheduledExecutorService; final class HostContext { @@ -18,8 +16,7 @@ final class HostContext { private final String eventHubPath; private final String consumerGroupName; - private final String eventHubConnectionString; - private final RetryPolicy retryPolicy; + private final EventHubClientFactory eventHubClientFactory; private final ILeaseManager leaseManager; private final ICheckpointManager checkpointManager; @@ -33,7 +30,7 @@ final class HostContext { HostContext(ScheduledExecutorService executor, EventProcessorHost host, String hostName, - String eventHubPath, String consumerGroupName, String eventHubConnectionString, RetryPolicy retryPolicy, + String eventHubPath, String consumerGroupName, EventHubClientFactory eventHubClientFactory, ILeaseManager leaseManager, ICheckpointManager checkpointManager) { this.executor = executor; @@ -42,8 +39,7 @@ final class HostContext { this.eventHubPath = eventHubPath; this.consumerGroupName = consumerGroupName; - this.eventHubConnectionString = eventHubConnectionString; - this.retryPolicy = retryPolicy; + this.eventHubClientFactory = eventHubClientFactory; this.leaseManager = leaseManager; this.checkpointManager = checkpointManager; @@ -65,12 +61,8 @@ String getConsumerGroupName() { return this.consumerGroupName; } - String getEventHubConnectionString() { - return this.eventHubConnectionString; - } - - RetryPolicy getRetryPolicy() { - return this.retryPolicy; + EventHubClientFactory getEventHubClientFactory() { + return this.eventHubClientFactory; } ILeaseManager getLeaseManager() { diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java index bb17cfeb4b3f..96d6ede65281 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionManager.java @@ -47,7 +47,7 @@ CompletableFuture cachePartitionIds() { final CompletableFuture cleanupFuture = new CompletableFuture(); // Stage 0A: get EventHubClient for the event hub - retval = EventHubClient.createFromConnectionString(this.hostContext.getEventHubConnectionString(), this.hostContext.getRetryPolicy(), this.hostContext.getExecutor()) + retval = this.hostContext.getEventHubClientFactory().createEventHubClient() // Stage 0B: set up a way to close the EventHubClient when we're done .thenApplyAsync((ehClient) -> { final EventHubClient saveForCleanupClient = ehClient; diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java index 59da3f8f07e2..ec049c63d511 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/PartitionPump.java @@ -175,8 +175,7 @@ private CompletableFuture openClients() { CompletableFuture startOpeningFuture = null; try { - startOpeningFuture = EventHubClient.createFromConnectionString(this.hostContext.getEventHubConnectionString(), - this.hostContext.getRetryPolicy(), this.hostContext.getExecutor()); + startOpeningFuture = this.hostContext.getEventHubClientFactory().createEventHubClient(); } catch (EventHubException | IOException e2) { // Marking startOpeningFuture as completed exceptionally will cause all the // following stages to fall through except stage 1 which will report the error. diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java index c7ce62faa497..e3b07179a5ff 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java @@ -13,24 +13,27 @@ public class EventHubClientOptions { public EventHubClientOptions() { } - public void setOperationTimeout(Duration operationTimeout) { + public EventHubClientOptions setOperationTimeout(Duration operationTimeout) { this.operationTimeout = operationTimeout; + return this; } public Duration getOperationTimeout() { return this.operationTimeout; } - public void setTransportType(TransportType transportType) { + public EventHubClientOptions setTransportType(TransportType transportType) { this.transportType = transportType; + return this; } public TransportType getTransportType() { return this.transportType; } - public void setRetryPolicy(RetryPolicy retryPolicy) { + public EventHubClientOptions setRetryPolicy(RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; + return this; } public RetryPolicy getRetryPolicy() { From 58cbf7ffc3abc133c82ca8f2c0ed350376ffc2ce Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Wed, 22 May 2019 13:30:28 -0700 Subject: [PATCH 05/22] Make tests compile --- .../CheckpointManagerTest.java | 8 ++-- .../eventprocessorhost/LeaseManagerTest.java | 7 ++- .../PartitionManagerTest.java | 7 ++- .../azure/eventprocessorhost/Repros.java | 43 +++++++++++-------- .../azure/eventprocessorhost/TestBase.java | 16 ++++--- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java index 838a17c5344a..7d89062a0756 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java @@ -238,9 +238,11 @@ private void setupOneManager(boolean useAzureStorage, int index, String suffix, } // Host name needs to be unique per host so use index. Event hub should be the same for all hosts in a test, so use the supplied suffix. - EventProcessorHost host = new EventProcessorHost("dummyHost" + String.valueOf(index), RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_EVENT_HUB_PATH + suffix, - EventHubClient.DEFAULT_CONSUMER_GROUP_NAME, RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_CONNECTION_STRING + suffix, checkpointMgr, leaseMgr); - + EventProcessorHost host = EventProcessorHost.EventProcessorHostBuilder.newBuilder("dummyHost" + String.valueOf(index), EventHubClient.DEFAULT_CONSUMER_GROUP_NAME) + .useUserCheckpointAndLeaseManagers(checkpointMgr, leaseMgr) + .useEventHubConnectionString(RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_CONNECTION_STRING + suffix, + RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_EVENT_HUB_PATH + suffix) + .build(); try { if (!useAzureStorage) { diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java index d424606afe7b..7d9e5878d522 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java @@ -287,8 +287,11 @@ private void setupOneManager(boolean useAzureStorage, int index, String suffix, } // Host name needs to be unique per host so use index. Event hub should be the same for all hosts in a test, so use the supplied suffix. - EventProcessorHost host = new EventProcessorHost("dummyHost" + String.valueOf(index), RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_EVENT_HUB_PATH + suffix, - EventHubClient.DEFAULT_CONSUMER_GROUP_NAME, RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_CONNECTION_STRING + suffix, checkpointMgr, leaseMgr); + EventProcessorHost host = EventProcessorHost.EventProcessorHostBuilder.newBuilder("dummyHost" + String.valueOf(index), EventHubClient.DEFAULT_CONSUMER_GROUP_NAME) + .useUserCheckpointAndLeaseManagers(checkpointMgr, leaseMgr) + .useEventHubConnectionString(RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_CONNECTION_STRING + suffix, + RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_EVENT_HUB_PATH + suffix) + .build(); try { if (!useAzureStorage) { diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PartitionManagerTest.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PartitionManagerTest.java index fcce41773e7b..42fdc30bffa1 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PartitionManagerTest.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PartitionManagerTest.java @@ -327,8 +327,11 @@ private void setup(int hostCount, int partitionCount, long latency, int threads) if (threads > 0) { threadpool = Executors.newScheduledThreadPool(threads); } - this.hosts[i] = new EventProcessorHost("dummyHost" + String.valueOf(i), "NOTREAL", EventHubClient.DEFAULT_CONSUMER_GROUP_NAME, - RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_CONNECTION_STRING, cm, lm, threadpool, null); + this.hosts[i] = EventProcessorHost.EventProcessorHostBuilder.newBuilder("dummyHost" + String.valueOf(i), EventHubClient.DEFAULT_CONSUMER_GROUP_NAME) + .useUserCheckpointAndLeaseManagers(cm, lm) + .useEventHubConnectionString(RealEventHubUtilities.SYNTACTICALLY_CORRECT_DUMMY_CONNECTION_STRING, "NOTREAL") + .setExecutor(threadpool) + .build(); lm.initialize(this.hosts[i].getHostContext()); lm.setLatency(latency); diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java index 69153562b544..a49a94165359 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java @@ -42,17 +42,19 @@ public void conflictingHosts() throws Exception { PrefabGeneralErrorHandler general1 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory1 = new PrefabProcessorFactory(telltale, doCheckpointing, doMarker); - EventProcessorHost host1 = new EventProcessorHost(conflictingName, utils.getConnectionString(true).getEventHubName(), - utils.getConsumerGroup(), utils.getConnectionString(true).toString(), - TestUtilities.getStorageConnectionString(), storageName); + EventProcessorHost host1 = EventProcessorHost.EventProcessorHostBuilder.newBuilder(conflictingName, utils.getConsumerGroup()) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) + .build(); EventProcessorOptions options1 = EventProcessorOptions.getDefaultOptions(); options1.setExceptionNotification(general1); PrefabGeneralErrorHandler general2 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory2 = new PrefabProcessorFactory(telltale, doCheckpointing, doMarker); - EventProcessorHost host2 = new EventProcessorHost(conflictingName, utils.getConnectionString(true).getEventHubName(), - utils.getConsumerGroup(), utils.getConnectionString(true).toString(), - TestUtilities.getStorageConnectionString(), storageName); + EventProcessorHost host2 = EventProcessorHost.EventProcessorHostBuilder.newBuilder(conflictingName, utils.getConsumerGroup()) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) + .build(); EventProcessorOptions options2 = EventProcessorOptions.getDefaultOptions(); options2.setExceptionNotification(general2); @@ -79,9 +81,11 @@ public void infiniteReceive() throws Exception { PrefabProcessorFactory factory = new PrefabProcessorFactory("never match", PrefabEventProcessor.CheckpointChoices.CKP_NONE, true, false); InMemoryCheckpointManager checkpointer = new InMemoryCheckpointManager(); InMemoryLeaseManager leaser = new InMemoryLeaseManager(); - EventProcessorHost host = new EventProcessorHost("infiniteReceive-1", utils.getConnectionString(true).getEventHubName(), - utils.getConsumerGroup(), utils.getConnectionString(true).toString(), - checkpointer, leaser, Executors.newScheduledThreadPool(16), null); + EventProcessorHost host = EventProcessorHost.EventProcessorHostBuilder.newBuilder("infiniteReceive-1", utils.getConsumerGroup()) + .useUserCheckpointAndLeaseManagers(checkpointer, leaser) + .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) + .setExecutor(Executors.newScheduledThreadPool(16)) + .build(); checkpointer.initialize(host.getHostContext()); leaser.initialize(host.getHostContext()); @@ -113,17 +117,19 @@ public void infiniteReceive2Hosts() throws Exception { PrefabGeneralErrorHandler general1 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory1 = new PrefabProcessorFactory("never match", PrefabEventProcessor.CheckpointChoices.CKP_NONE, true, false); - EventProcessorHost host1 = new EventProcessorHost("infiniteReceive2Hosts-1", utils.getConnectionString(true).getEventHubName(), - utils.getConsumerGroup(), utils.getConnectionString(true).toString(), - TestUtilities.getStorageConnectionString(), storageName); + EventProcessorHost host1 = EventProcessorHost.EventProcessorHostBuilder.newBuilder("infiniteReceive2Hosts-1", utils.getConsumerGroup()) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) + .build(); EventProcessorOptions options1 = EventProcessorOptions.getDefaultOptions(); options1.setExceptionNotification(general1); PrefabGeneralErrorHandler general2 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory2 = new PrefabProcessorFactory("never match", PrefabEventProcessor.CheckpointChoices.CKP_NONE, true, false); - EventProcessorHost host2 = new EventProcessorHost("infiniteReceive2Hosts-2", utils.getConnectionString(true).getEventHubName(), - utils.getConsumerGroup(), utils.getConnectionString(true).toString(), - TestUtilities.getStorageConnectionString(), storageName); + EventProcessorHost host2 = EventProcessorHost.EventProcessorHostBuilder.newBuilder("infiniteReceive2Hosts-1", utils.getConsumerGroup()) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) + .build(); EventProcessorOptions options2 = EventProcessorOptions.getDefaultOptions(); options2.setExceptionNotification(general2); @@ -173,9 +179,10 @@ public void infiniteReceive2Hosts() throws Exception { host2 = null; } else { factory2 = new PrefabProcessorFactory("never match", PrefabEventProcessor.CheckpointChoices.CKP_NONE, true, false); - host2 = new EventProcessorHost("infiniteReceive2Hosts-2", utils.getConnectionString(true).getEventHubName(), - utils.getConsumerGroup(), utils.getConnectionString(true).toString(), - TestUtilities.getStorageConnectionString(), storageName); + host2 = EventProcessorHost.EventProcessorHostBuilder.newBuilder("infiniteReceive2Hosts-1", utils.getConsumerGroup()) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) + .build(); options2 = EventProcessorOptions.getDefaultOptions(); options2.setExceptionNotification(general2); diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java index a7447e5392e8..1270c56a6bd7 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java @@ -151,15 +151,17 @@ PerTestSettings testSetup(PerTestSettings settings) throws Exception { settings.inOptions.setExceptionNotification(settings.outGeneralErrorHandler); if (settings.inoutEPHConstructorArgs.useExplicitManagers()) { - ICheckpointManager effectiveCheckpointMananger = settings.inoutEPHConstructorArgs.isFlagSet(PerTestSettings.EPHConstructorArgs.CHECKPOINT_MANAGER_OVERRIDE) + ICheckpointManager effectiveCheckpointManager = settings.inoutEPHConstructorArgs.isFlagSet(PerTestSettings.EPHConstructorArgs.CHECKPOINT_MANAGER_OVERRIDE) ? settings.inoutEPHConstructorArgs.getCheckpointMananger() : new BogusCheckpointMananger(); ILeaseManager effectiveLeaseManager = settings.inoutEPHConstructorArgs.isFlagSet(PerTestSettings.EPHConstructorArgs.LEASE_MANAGER_OVERRIDE) ? settings.inoutEPHConstructorArgs.getLeaseManager() : new BogusLeaseManager(); - settings.outHost = new EventProcessorHost(effectiveHostName, effectiveEntityPath, effectiveConsumerGroup, effectiveConnectionString, - effectiveCheckpointMananger, effectiveLeaseManager, effectiveExecutor, null); + settings.outHost = EventProcessorHost.EventProcessorHostBuilder.newBuilder(effectiveHostName, effectiveConsumerGroup) + .useUserCheckpointAndLeaseManagers(effectiveCheckpointManager, effectiveLeaseManager) + .useEventHubConnectionString(effectiveConnectionString, effectiveEntityPath) + .setExecutor(effectiveExecutor).build(); } else { String effectiveStorageConnectionString = settings.inoutEPHConstructorArgs.isFlagSet(PerTestSettings.EPHConstructorArgs.STORAGE_CONNECTION_OVERRIDE) ? settings.inoutEPHConstructorArgs.getStorageConnection() @@ -178,9 +180,11 @@ PerTestSettings testSetup(PerTestSettings settings) throws Exception { String effectiveBlobPrefix = settings.inoutEPHConstructorArgs.isFlagSet(PerTestSettings.EPHConstructorArgs.STORAGE_BLOB_PREFIX_OVERRIDE) ? settings.inoutEPHConstructorArgs.getStorageBlobPrefix() : null; - - settings.outHost = new EventProcessorHost(effectiveHostName, effectiveEntityPath, effectiveConsumerGroup, effectiveConnectionString, - effectiveStorageConnectionString, effectiveStorageContainerName, effectiveBlobPrefix, effectiveExecutor); + + settings.outHost = EventProcessorHost.EventProcessorHostBuilder.newBuilder(effectiveHostName, effectiveConsumerGroup) + .useAzureStorageCheckpointLeaseManager(effectiveStorageConnectionString, effectiveStorageContainerName, effectiveBlobPrefix) + .useEventHubConnectionString(effectiveConnectionString, effectiveEntityPath) + .setExecutor(effectiveExecutor).build(); } if (!settings.inEventHubDoesNotExist) { From fb518b70765c753d2162923b1e3b6dd06b59f16d Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Wed, 22 May 2019 13:35:05 -0700 Subject: [PATCH 06/22] Remove managed identity --- .../EventHubClientFactory.java | 18 ------------- .../EventProcessorHost.java | 17 +++---------- .../azure/eventhubs/EventHubClient.java | 21 ---------------- .../ManagedIdentityTokenProvider.java | 25 ------------------- .../eventhubs/impl/MessagingFactory.java | 4 --- 5 files changed, 3 insertions(+), 82 deletions(-) delete mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java index 29a9529b10a1..66bd4ceed2be 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java @@ -92,22 +92,4 @@ public CompletableFuture createEventHubClient() throws EventHubE return EventHubClient.createWithTokenProvider(this.endpoint, this.eventHubPath, this.tokenProvider, this.executor, this.options); } } - - public static class EHCFWithManagedIdentity extends EventHubClientFactory { - private final URI endpoint; - private final String eventHubPath; - - public EHCFWithManagedIdentity(final URI endpoint, - final String eventHubPath, - final EventHubClientOptions options, - final ScheduledExecutorService executor) { - super(executor, options); - this.endpoint = endpoint; - this.eventHubPath = eventHubPath; - } - - public CompletableFuture createEventHubClient() throws EventHubException, IOException { - return EventHubClient.createWithManagedIdentity(this.endpoint, this.eventHubPath, this.executor, this.options); - } - } } diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java index f4ac28974034..1adf065f41dc 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java @@ -392,8 +392,6 @@ public static interface AADAuthStep { OptionalStep useAuthenticationCallback(AuthenticationCallback authCallback, String authority); - OptionalStep useManagedIdentity(); - OptionalStep useTokenProvider(ITokenProvider tokenProvider); } @@ -421,12 +419,11 @@ private static class Steps implements ManagerStep, AuthStep, AADAuthStep, Option // Auth steps private String eventHubConnectionString = null; // group 1 - private String eventHubPath = null; // optional for group 1, required for groups 2-4 - private URI endpoint = null; // groups 2-4 + private String eventHubPath = null; // optional for group 1, required for groups 2-3 + private URI endpoint = null; // groups 2-3 private AuthenticationCallback authCallback = null; // group 2 private String authority = null; // group 2 - private boolean useManagedIdentity = false; // group 3 - private ITokenProvider tokenProvider = null; // group 4 + private ITokenProvider tokenProvider = null; // group 3 // ManagerStep private ICheckpointManager checkpointManager; @@ -490,12 +487,6 @@ public OptionalStep useAuthenticationCallback(final AuthenticationCallback authC return this; } - @Override - public OptionalStep useManagedIdentity() { - this.useManagedIdentity = true; - return this; - } - @Override public OptionalStep useTokenProvider(final ITokenProvider tokenProvider) { Objects.requireNonNull(tokenProvider); @@ -573,8 +564,6 @@ public EventProcessorHost build() { this.authCallback, this.authority, packOptions(), this.executor); } else if (this.tokenProvider != null) { ehcFactory = new EventHubClientFactory.EHCFWithTokenProvider(this.endpoint, this.eventHubPath, this.tokenProvider, packOptions(), this.executor); - } else if (this.useManagedIdentity) { - ehcFactory = new EventHubClientFactory.EHCFWithManagedIdentity(this.endpoint, this.eventHubPath, packOptions(), this.executor); } return new EventProcessorHost(this.hostName, this.eventHubPath, diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java index 955aba74929f..e3597fffba79 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java @@ -134,27 +134,6 @@ public static CompletableFuture createWithAzureActiveDirectory( ITokenProvider tokenProvider = new AzureActiveDirectoryTokenProvider(authCallback, authority, null); return createWithTokenProvider(endpointAddress, eventHubName, tokenProvider, executor, options); } - - /** - * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. - * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. - *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. - * - * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName - * @param eventHubName EventHub name - * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. - * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. - * @return EventHubClient which can be used to create Senders and Receivers to EventHub - * @throws EventHubException If the EventHubs service encountered problems during connection creation. - * @throws IOException If the underlying Proton-J layer encounter network errors. - */ - public static CompletableFuture createWithManagedIdentity( - final URI endpointAddress, - final String eventHubName, - final ScheduledExecutorService executor, - final EventHubClientOptions options) throws EventHubException, IOException { - return createWithTokenProvider(endpointAddress, eventHubName, new ManagedIdentityTokenProvider(), executor, options); - } /** * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java deleted file mode 100644 index 9af36d152965..000000000000 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.microsoft.azure.eventhubs; - -import java.io.IOException; -import java.text.ParseException; -import java.time.Duration; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; - -import com.microsoft.azure.credentials.MSICredentials; - -public class ManagedIdentityTokenProvider implements ITokenProvider { - @Override - public CompletableFuture getToken(final String resource, final Duration timeout) { - final MSICredentials credentials = new MSICredentials(); - - return CompletableFuture.supplyAsync(() -> { - try { - String rawToken = credentials.getToken(resource); - return new JsonSecurityToken(rawToken, resource); - } catch (IOException | ParseException e) { - throw new CompletionException(e); - } - }); - } -} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java index 85aea18a23a0..218d1c5312f6 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java @@ -4,12 +4,10 @@ package com.microsoft.azure.eventhubs.impl; -import com.google.common.base.Strings; import com.microsoft.azure.eventhubs.CommunicationException; import com.microsoft.azure.eventhubs.ConnectionStringBuilder; import com.microsoft.azure.eventhubs.EventHubException; import com.microsoft.azure.eventhubs.ITokenProvider; -import com.microsoft.azure.eventhubs.ManagedIdentityTokenProvider; import com.microsoft.azure.eventhubs.OperationCancelledException; import com.microsoft.azure.eventhubs.RetryPolicy; import com.microsoft.azure.eventhubs.TimeoutException; @@ -131,8 +129,6 @@ public static CompletableFuture createFromConnectionString( tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSharedAccessSignature()); } else if (!StringUtil.isNullOrWhiteSpace(csb.getSasKey())) { tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSasKeyName(), csb.getSasKey()); - } else if ((csb.getAuthentication() != null) && csb.getAuthentication().equalsIgnoreCase("Managed Identity")) { - tokenProvider = new ManagedIdentityTokenProvider(); } else { throw new IllegalArgumentException("Connection string must specify a Shared Access Signature, Shared Access Key, or Managed Identity"); } From 31ae8c8fec94cd2ccda14717c3c316ebb005bf1e Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Wed, 22 May 2019 14:35:10 -0700 Subject: [PATCH 07/22] Update pom dependencies --- eventhubs/data-plane/azure-eventhubs/pom.xml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs/pom.xml b/eventhubs/data-plane/azure-eventhubs/pom.xml index e53b26bd47c6..704a343aef62 100644 --- a/eventhubs/data-plane/azure-eventhubs/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs/pom.xml @@ -32,14 +32,9 @@ - com.microsoft.azure - azure-client-authentication - 1.6.7 - - - com.auth0 - java-jwt - 3.8.0 + com.nimbusds + nimbus-jose-jwt + 7.1 From bdc0191a25b08f3446824ecd0da98d78a482ed8d Mon Sep 17 00:00:00 2001 From: JamesBirdsall Date: Wed, 22 May 2019 15:43:12 -0700 Subject: [PATCH 08/22] De-public some things, add javadocs. --- .../EventHubClientFactory.java | 8 +- .../EventProcessorHost.java | 130 +++++++++++++++++- .../eventhubs/EventHubClientOptions.java | 34 +++++ .../azure/eventhubs/JsonSecurityToken.java | 10 ++ .../azure/eventhubs/SecurityToken.java | 27 ++++ 5 files changed, 199 insertions(+), 10 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java index 66bd4ceed2be..3fa4185f06e7 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java @@ -15,7 +15,7 @@ import com.microsoft.azure.eventhubs.ITokenProvider; import com.microsoft.azure.eventhubs.RetryPolicy; -public abstract class EventHubClientFactory { +abstract class EventHubClientFactory { protected final ScheduledExecutorService executor; protected final EventHubClientOptions options; @@ -32,7 +32,7 @@ public EventHubClientFactory(final ScheduledExecutorService executor, abstract CompletableFuture createEventHubClient() throws EventHubException, IOException; - public static class EHCFWithConnectionString extends EventHubClientFactory { + static class EHCFWithConnectionString extends EventHubClientFactory { private final String eventHubConnectionString; public EHCFWithConnectionString(final String eventHubConnectionString, @@ -47,7 +47,7 @@ public CompletableFuture createEventHubClient() throws EventHubE } } - public static class EHCFWithAuthCallback extends EventHubClientFactory { + static class EHCFWithAuthCallback extends EventHubClientFactory { private final URI endpoint; private final String eventHubPath; private final AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback; @@ -72,7 +72,7 @@ public CompletableFuture createEventHubClient() throws EventHubE } } - public static class EHCFWithTokenProvider extends EventHubClientFactory { + static class EHCFWithTokenProvider extends EventHubClientFactory { private final URI endpoint; private final String eventHubPath; private final ITokenProvider tokenProvider; diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java index 1adf065f41dc..d61ecadd8248 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java @@ -362,8 +362,28 @@ public void uncaughtException(Thread t, Throwable e) { } } - + /** + * Builder class to create EventProcessorHost instances. + *

+ * To use, start with: EventProcessorHost.EventProcessorHostBuilder.newBuilder(...) + * Then either use the built-in Azure Storage-based lease and checkpoint managers, or user implementations. + * Then either supply an Event Hub connection string or use Azure Active Directory (AAD) authentication. + * If using AAD auth, either provide a callback or an ITokenProvider + * Finally, set various optional values as desired, then call build() to get an EventProcessorHost instance. + */ public static class EventProcessorHostBuilder { + /** + * The process of building starts here, with arguments that are always required. + *

+ * The hostName parameter is a name for this EventProcessorHost instance, which must be unique among + * all instances consuming from the same Event Hub and consumer group. The name must be unique because + * it is used to distinguish which instance owns the lease for a given partition of the event hub. An + * easy way to generate a unique host name is to call EventProcessorHost.createHostName("mystring"). + * + * @param hostName a name for this host instance. See method notes. + * @param consumerGroupName the consumer group on the Event Hub + * @return interface for setting the lease and checkpoint managers + */ public static ManagerStep newBuilder(final String hostName, final String consumerGroupName) { return new Steps(hostName, consumerGroupName); } @@ -371,39 +391,137 @@ public static ManagerStep newBuilder(final String hostName, final String consume private EventProcessorHostBuilder() { } - public static interface ManagerStep { + static interface ManagerStep { + /** + * Use the built-in Azure Storage-based lease and checkpoint managers. + * + * @param storageConnectionString connection string for an Azure Storage account + * @param storageContainerName name for the blob container within the Storage account + * @return interface for setting the Event Hub connection info and auth + */ AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName); - AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName, String StorageBlobPrefix); + /** + * Use the built-in Azure Storage-based lease and checkpoint managers. + * + * @param storageConnectionString connection string for an Azure Storage account + * @param storageContainerName name for the blob container within the Storage account + * @param storageBlobPrefix prefix for the names of the blobs within the blob container + * @return interface for setting the Event Hub connection info and auth + */ + AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName, String storageBlobPrefix); + /** + * Use user-implemented lease and checkpoint managers. + * + * @param checkpointManager user-supplied implementation of {@link ICheckpointManager} + * @param leaseManager user-supplied implementation of {@link ILeaseManager} + * @return interface for setting the Event Hub connection info and auth + */ AuthStep useUserCheckpointAndLeaseManagers(ICheckpointManager checkpointManager, ILeaseManager leaseManager); } - public static interface AuthStep { + static interface AuthStep { + /** + * Azure Portal can provide a connection string with auth information that applies only to one + * individual Event Hub. In that case, the connection string contains the name of the Event Hub. + * + * @param eventHubConnectionString Event Hub connection string (which contains the name of the Event Hub) + * @return interface for setting optional values + */ OptionalStep useEventHubConnectionString(String eventHubConnectionString); + /** + * Azure Portal can provide a connection string with auth information that applies to the entire + * namespace instead of an individual Event Hub. Use this overload with such a connection string, + * which requires you to specify the name of the Event Hub separately. + * + * @param eventHubConnectionString Event Hub connection string (which does not contain the name of the Event Hub) + * @param eventHubPath name of the Event Hub + * @return interface for setting optional values + */ OptionalStep useEventHubConnectionString(String eventHubConnectionString, String eventHubPath); + /** + * When using AAD auth, call this method to specify the Event Hub, then add AAD-based auth information in the next step. + * + * @param endpoint URI of the Event Hub namespace + * @param eventHubPath name of the Event Hub + * @return interface for setting AAD auth info + */ AADAuthStep useAADAuthentication(URI endpoint, String eventHubPath); } - public static interface AADAuthStep { + static interface AADAuthStep { + /** + * Provide a callback which will be called when a token is needed. See {@link AzureActiveDirectoryTokenProvider} + * The callback will be called with an AAD authority string which is appropriate for public Azure. + * + * @param authCallback the callback + * @return interface for setting optional values + */ OptionalStep useAuthenticationCallback(AuthenticationCallback authCallback); + /** + * Provide a callback which will be called when a token is needed. See {@link AzureActiveDirectoryTokenProvider} + * + * @param authCallback the callback + * @param authority AAD authority string which will be passed to the callback. Used for national cloud support. + * @return interface for setting optional values + */ OptionalStep useAuthenticationCallback(AuthenticationCallback authCallback, String authority); + /** + * Provide a user-implemented token provider which will be called when a token is needed. + * + * @param tokenProvider user implementation of ITokenProvider + * @return interface for setting optional values + */ OptionalStep useTokenProvider(ITokenProvider tokenProvider); } - public static interface OptionalStep { + static interface OptionalStep { + /** + * Event Processor Host runs tasks on the supplied threadpool, or creates an internal one. + * @param executor threadpool + * @return interface for setting optional values + */ OptionalStep setExecutor(ScheduledExecutorService executor); + /** + * {@link RetryPolicy} for Event Hubs operations. Event Processor Host uses RetryPolicy.getDefault() + * if none is supplied. + * + * @param retryPolicy desired retry policy + * @return interface for setting optional values + */ OptionalStep setRetryPolicy(RetryPolicy retryPolicy); + /** + * {@link TransportType} for connections to the Event Hubs service. Defaults to TransportType.AMQP. + * The transport type can also be set in the Event Hub connection string. The value set here will + * override the value in the connection string, if any. + * + * @param transportType desired transport type + * @return interface for setting optional values + */ OptionalStep setTransportType(TransportType transportType); + /** + * The timeout for Event Hubs operations. Defaults to MessagingFactory.DefaultOperationTimeout. + * The timeout can also be set in the Event Hub connection string. The value set here will override + * the value in the connection string, if any. + * + * @param operationTimeout desired timeout + * @return interface for setting optional values + */ OptionalStep setOperationTimeout(Duration operationTimeout); + /** + * After setting all desired optional values, call this method to build an EventProcessorHost instance. + * + * @return new EventProcessorHost instance + */ EventProcessorHost build(); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java index e3b07179a5ff..562a0ae40cba 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClientOptions.java @@ -5,37 +5,71 @@ import java.time.Duration; +/** + * Convenient container for options for creating an {@link EventHubClient} + * All options default to not specified (null) + */ public class EventHubClientOptions { private Duration operationTimeout = null; private TransportType transportType = null; private RetryPolicy retryPolicy = null; + /** + * Create with all defaults + */ public EventHubClientOptions() { } + /** + * Set the operation timeout. + * @param operationTimeout new operation timeout, null to unset any previous value + * @return this options object + */ public EventHubClientOptions setOperationTimeout(Duration operationTimeout) { this.operationTimeout = operationTimeout; return this; } + /** + * Get the operation timeout. + * @return operation timeout or null if not set + */ public Duration getOperationTimeout() { return this.operationTimeout; } + /** + * Set the {@link TransportType} for the connection to the Event Hubs service + * @param transportType new transport type, null to unset any previous value + * @return this options object + */ public EventHubClientOptions setTransportType(TransportType transportType) { this.transportType = transportType; return this; } + /** + * Get the transport type + * @return {@link TransportType} or null if not set + */ public TransportType getTransportType() { return this.transportType; } + /** + * Set the {@link RetryPolicy} for operations + * @param retryPolicy new retry policy, null to unset any previous value + * @return this options object + */ public EventHubClientOptions setRetryPolicy(RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; return this; } + /** + * Get the retry policy + * @return {@link RetryPolicy} or null if not set + */ public RetryPolicy getRetryPolicy() { return this.retryPolicy; } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java index 107080c9558c..8f45de59393a 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/JsonSecurityToken.java @@ -8,7 +8,17 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTParser; +/** + * Extend SecurityToken with some specifics for a JSon Web Token + * + */ public class JsonSecurityToken extends SecurityToken { + /** + * Construct from a raw JWT. + * @param rawToken the JWT token data + * @param audience audience of the token + * @throws ParseException if the token cannot be parsed + */ public JsonSecurityToken(final String rawToken, final String audience) throws ParseException { super(rawToken, GetExpirationDateTimeUtcFromToken(rawToken), audience, ClientConstants.JWT_TOKEN_TYPE); } diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java index da977623307b..7ee3293bb4eb 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/SecurityToken.java @@ -5,12 +5,23 @@ import java.util.Date; +/** + * A generic representation of a security token. + * + */ public class SecurityToken { private final String tokenType; private final String token; private final Date validTo; private final String audience; + /** + * Constructor. + * @param token the actual token data + * @param validTo expiration date/time of the token + * @param audience audience of the token + * @param tokenType type of the token + */ public SecurityToken(final String token, final Date validTo, final String audience, final String tokenType) { this.tokenType = tokenType; this.token = token; @@ -18,18 +29,34 @@ public SecurityToken(final String token, final Date validTo, final String audien this.audience = audience; } + /** + * Get the type of the token. + * @return token type + */ public String getTokenType() { return this.tokenType; } + /** + * Get the token itself. + * @return token + */ public String getToken() { return this.token; } + /** + * Get the expiration date/time of the token. + * @return expiration + */ public Date validTo() { return this.validTo; } + /** + * Get the audience of the token. + * @return audience + */ public String getAudience() { return this.audience; } From 3c7d294e836878cea34e2d1a84d958894e363fe8 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Mon, 3 Jun 2019 10:57:52 -0700 Subject: [PATCH 09/22] Restore ManagedIdentityTokenProvider and MI support in connection string --- eventhubs/data-plane/azure-eventhubs/pom.xml | 5 ++++ .../ManagedIdentityTokenProvider.java | 25 +++++++++++++++++++ .../eventhubs/impl/MessagingFactory.java | 3 +++ 3 files changed, 33 insertions(+) create mode 100644 eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java diff --git a/eventhubs/data-plane/azure-eventhubs/pom.xml b/eventhubs/data-plane/azure-eventhubs/pom.xml index 704a343aef62..889e1ebde3e4 100644 --- a/eventhubs/data-plane/azure-eventhubs/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs/pom.xml @@ -36,6 +36,11 @@ nimbus-jose-jwt 7.1 + + com.microsoft.azure + azure-client-authentication + 1.6.7 + diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java new file mode 100644 index 000000000000..9af36d152965 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java @@ -0,0 +1,25 @@ +package com.microsoft.azure.eventhubs; + +import java.io.IOException; +import java.text.ParseException; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import com.microsoft.azure.credentials.MSICredentials; + +public class ManagedIdentityTokenProvider implements ITokenProvider { + @Override + public CompletableFuture getToken(final String resource, final Duration timeout) { + final MSICredentials credentials = new MSICredentials(); + + return CompletableFuture.supplyAsync(() -> { + try { + String rawToken = credentials.getToken(resource); + return new JsonSecurityToken(rawToken, resource); + } catch (IOException | ParseException e) { + throw new CompletionException(e); + } + }); + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java index 218d1c5312f6..77b1a045b665 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java @@ -8,6 +8,7 @@ import com.microsoft.azure.eventhubs.ConnectionStringBuilder; import com.microsoft.azure.eventhubs.EventHubException; import com.microsoft.azure.eventhubs.ITokenProvider; +import com.microsoft.azure.eventhubs.ManagedIdentityTokenProvider; import com.microsoft.azure.eventhubs.OperationCancelledException; import com.microsoft.azure.eventhubs.RetryPolicy; import com.microsoft.azure.eventhubs.TimeoutException; @@ -129,6 +130,8 @@ public static CompletableFuture createFromConnectionString( tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSharedAccessSignature()); } else if (!StringUtil.isNullOrWhiteSpace(csb.getSasKey())) { tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSasKeyName(), csb.getSasKey()); + } else if ((csb.getAuthentication() != null) && csb.getAuthentication().equalsIgnoreCase("Managed Identity")) { + tokenProvider = new ManagedIdentityTokenProvider(); } else { throw new IllegalArgumentException("Connection string must specify a Shared Access Signature, Shared Access Key, or Managed Identity"); } From 21a0a84a0d6984bcf449c9f6e51d43977f98ba3a Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Tue, 11 Jun 2019 13:42:48 -0700 Subject: [PATCH 10/22] Fix getting token in getruntime path --- .../com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java index 0bab13ec226b..511ae19728c5 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/EventHubClientImpl.java @@ -331,7 +331,7 @@ public CompletableFuture getPartitionRuntimeInforma private CompletableFuture> addManagementToken(Map request) { String audience = String.format(Locale.US, "amqp://%s/%s", this.underlyingFactory.getHostName(), this.eventHubName); return this.underlyingFactory.getTokenProvider().getToken(audience, ClientConstants.TOKEN_REFRESH_INTERVAL).thenApplyAsync((securityToken) -> { - request.put(ClientConstants.MANAGEMENT_SECURITY_TOKEN_KEY, securityToken.toString()); + request.put(ClientConstants.MANAGEMENT_SECURITY_TOKEN_KEY, securityToken.getToken()); return request; }, this.executor); } From 7f766f2db9eda8967ac3d5efd6c2e064119168f3 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Thu, 13 Jun 2019 12:22:46 -0700 Subject: [PATCH 11/22] First pass at RBAC tests --- eventhubs/data-plane/azure-eventhubs/pom.xml | 22 +- .../azure/eventhubs/sendrecv/MsalTest.java | 223 ++++++++++++++++++ 2 files changed, 240 insertions(+), 5 deletions(-) create mode 100644 eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java diff --git a/eventhubs/data-plane/azure-eventhubs/pom.xml b/eventhubs/data-plane/azure-eventhubs/pom.xml index 889e1ebde3e4..cf4da9fa2eac 100644 --- a/eventhubs/data-plane/azure-eventhubs/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs/pom.xml @@ -31,16 +31,28 @@ - - com.nimbusds - nimbus-jose-jwt - 7.1 - com.microsoft.azure azure-client-authentication 1.6.7 + + com.microsoft.azure + msal4j + 0.4.0-preview + test + + + com.microsoft.azure + adal4j + 1.6.4 + test + + + com.nimbusds + nimbus-jose-jwt + 6.0.1 + diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java new file mode 100644 index 000000000000..7d83c823e529 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java @@ -0,0 +1,223 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.azure.eventhubs.sendrecv; + +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; +import com.microsoft.aad.msal4j.ClientCredentialParameters; +import com.microsoft.aad.msal4j.ClientSecret; +import com.microsoft.aad.msal4j.ConfidentialClientApplication; +import com.microsoft.aad.msal4j.IAuthenticationResult; +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; +import com.microsoft.azure.eventhubs.ConnectionStringBuilder; +import com.microsoft.azure.eventhubs.EventData; +import com.microsoft.azure.eventhubs.EventHubClient; +import com.microsoft.azure.eventhubs.EventHubException; +import com.microsoft.azure.eventhubs.ITokenProvider; +import com.microsoft.azure.eventhubs.JsonSecurityToken; +import com.microsoft.azure.eventhubs.PartitionReceiver; +import com.microsoft.azure.eventhubs.PartitionSender; +import com.microsoft.azure.eventhubs.SecurityToken; +import com.microsoft.azure.eventhubs.impl.ClientConstants; +import com.microsoft.azure.eventhubs.impl.EventPositionImpl; +import com.microsoft.azure.eventhubs.lib.ApiTestBase; +import com.microsoft.azure.eventhubs.lib.TestContext; + +import java.net.MalformedURLException; +import java.net.URI; +import java.text.ParseException; +import java.time.Duration; +import java.util.Base64; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Supplier; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MsalTest extends ApiTestBase { + static URI endpoint; + static String eventHubName; + + final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(8); + final private String authority = "https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47"; + //final private String authority = AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY; + final private String clientId = "51ce7943-a1aa-4bb1-b708-8f66656a9242"; + final private String clientSecret = "sMm5YoVVcmb4fUtm=dQa*nY-P-YEkx35"; + + @BeforeClass + public static void initializeEventHub() throws Exception { + ConnectionStringBuilder csb = TestContext.getConnectionString(); + MsalTest.endpoint = csb.getEndpoint(); + MsalTest.eventHubName = csb.getEventHubName(); + } + + @AfterClass() + public static void cleanup() throws EventHubException { + } + + @Test + public void runSendReceiveWithAuthCallbackTest() throws Exception { + final AuthCallback callback = new AuthCallback(); + final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, + callback, this.executorService, null).get(); + + innerTest(ehc); + } + + @Test + public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { + final AuthCallback callback = new AuthCallback(); + final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, + callback, this.authority, this.executorService, null).get(); + + innerTest(ehc); + } + + private void innerTest(final EventHubClient ehc) throws EventHubException { + final PartitionReceiver pReceiver = ehc.createReceiverSync("$Default", "0", EventPositionImpl.fromEndOfStream()); + + final PartitionSender pSender = ehc.createPartitionSenderSync("0"); + final String testMessage = "somedata test"; + pSender.send(EventData.create(testMessage.getBytes())); + + int scanned = 0; + boolean found = false; + while ((scanned < 10000) && !found) { + System.out.println("Scanned " + scanned); + final Iterable events = pReceiver.receiveSync(100); + for (EventData ed : events) { + scanned++; + if ((new String(ed.getBytes())).equals(testMessage)) { + found = true; + break; + } + } + } + + pSender.closeSync(); + pReceiver.closeSync();; + ehc.closeSync(); + + Assert.assertTrue(found); + } + + @Test + public void runSendReceiveWithAADTokenProvider() throws Exception { + final AuthCallback callback = new AuthCallback(); + final AzureActiveDirectoryTokenProvider aadTokenProvider = + new AzureActiveDirectoryTokenProvider(callback, this.authority, null); + final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, aadTokenProvider, + this.executorService, null).get(); + + innerTest(ehc); + } + + @Test + public void runSendReceiveWithCustomTokenProvider() throws Exception { + final CustomTokenProvider tokenProvider = new CustomTokenProvider(); + final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, tokenProvider, + this.executorService, null).get(); + + innerTest(ehc); + } + + class CustomTokenProvider implements ITokenProvider, Supplier { + @Override + public CompletableFuture getToken(String resource, Duration timeout) { + return CompletableFuture.supplyAsync(this).thenApply((rawToken) -> { + try { + return new JsonSecurityToken(rawToken, resource); + } catch (ParseException e) { + throw new CompletionException(e); + } + }); + } + + @Override + public String get() { + String retval = ""; + + try { + retval = MsalTest.this.tokenGet(MsalTest.this.authority, MsalTest.this.clientId, MsalTest.this.clientSecret, ClientConstants.EVENTHUBS_AUDIENCE, ".default"); + } + catch (Exception e) { + throw new CompletionException(e); + } + + return retval; + } + } + + class AuthCallback implements AzureActiveDirectoryTokenProvider.AuthenticationCallback, Supplier { + private String audience; + private String callbackAuthority; + + @Override + public CompletableFuture acquireToken(String audience, String authority, Object state) { + this.audience = audience; + this.callbackAuthority = authority; + return CompletableFuture.supplyAsync(this); + } + + @Override + public String get() { + String retval = ""; + + try { + retval = MsalTest.this.tokenGet(this.callbackAuthority, MsalTest.this.clientId, MsalTest.this.clientSecret, this.audience, ".default"); + } + catch (Exception e) { + throw new CompletionException(e); + } + + return retval; + } + } + + private String tokenGet(final String authority, final String clientId, final String clientSecret, final String audience, final String extra) + throws MalformedURLException, InterruptedException, ExecutionException { + return adalGet(authority, clientId, clientSecret, audience); + //return msalGet(authority, clientId, clientSecret, audience + extra); + } + + private String adalGet(final String authority, final String clientId, final String clientSecret, final String audience) + throws MalformedURLException, InterruptedException, ExecutionException { + AuthenticationContext context = new AuthenticationContext(authority, true, MsalTest.this.executorService); + ClientCredential creds = new ClientCredential(clientId, clientSecret); + AuthenticationResult result = context.acquireToken(audience, creds, null).get(); + System.out.println(decodeToken(result.getAccessToken())); + return result.getAccessToken(); + } + + private String msalGet(final String authority, final String clientId, final String clientSecret, final String audience) + throws MalformedURLException, InterruptedException, ExecutionException { + ConfidentialClientApplication app = ConfidentialClientApplication.builder(clientId, new ClientSecret(clientSecret)) + .authority(authority) + .build(); + + ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(audience)).build(); + + IAuthenticationResult result = app.acquireToken(parameters).get(); + + System.out.println(result.accessToken()); + System.out.println(decodeToken(result.accessToken())); + return result.accessToken(); + } + + private String decodeToken(final String rawToken) { + String parts[] = rawToken.split("\\."); + String output = new String(Base64.getDecoder().decode(parts[0])); + output += '.'; + output += new String(Base64.getDecoder().decode(parts[1])); + return output; + } +} From 20538e472a2c0b530c0332629a11814709bc207b Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Thu, 13 Jun 2019 13:39:49 -0700 Subject: [PATCH 12/22] Second pass at RBAC testing --- .../azure/eventhubs/sendrecv/AadBase.java | 147 ++++++++++++++++ .../azure/eventhubs/sendrecv/AdalTest.java | 67 ++++++++ .../azure/eventhubs/sendrecv/MsalTest.java | 161 +----------------- 3 files changed, 222 insertions(+), 153 deletions(-) create mode 100644 eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java create mode 100644 eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java new file mode 100644 index 000000000000..0dd2cd5cb172 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java @@ -0,0 +1,147 @@ +package com.microsoft.azure.eventhubs.sendrecv; + +import java.net.MalformedURLException; +import java.net.URI; +import java.text.ParseException; +import java.time.Duration; +import java.util.Base64; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Supplier; + +import org.junit.Assert; +import org.junit.BeforeClass; + +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; +import com.microsoft.azure.eventhubs.ConnectionStringBuilder; +import com.microsoft.azure.eventhubs.EventData; +import com.microsoft.azure.eventhubs.EventHubClient; +import com.microsoft.azure.eventhubs.EventHubException; +import com.microsoft.azure.eventhubs.ITokenProvider; +import com.microsoft.azure.eventhubs.JsonSecurityToken; +import com.microsoft.azure.eventhubs.PartitionReceiver; +import com.microsoft.azure.eventhubs.PartitionSender; +import com.microsoft.azure.eventhubs.SecurityToken; +import com.microsoft.azure.eventhubs.impl.ClientConstants; +import com.microsoft.azure.eventhubs.impl.EventPositionImpl; +import com.microsoft.azure.eventhubs.lib.ApiTestBase; +import com.microsoft.azure.eventhubs.lib.TestContext; + +public abstract class AadBase extends ApiTestBase { + protected static URI endpoint; + protected static String eventHubName; + + protected final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(8); + + @BeforeClass + public static void initializeEventHub() throws Exception { + ConnectionStringBuilder csb = TestContext.getConnectionString(); + AadBase.endpoint = csb.getEndpoint(); + AadBase.eventHubName = csb.getEventHubName(); + } + + protected void innerTest(final EventHubClient ehc) throws EventHubException { + final PartitionReceiver pReceiver = ehc.createReceiverSync("$Default", "0", EventPositionImpl.fromEndOfStream()); + + final PartitionSender pSender = ehc.createPartitionSenderSync("0"); + final String testMessage = "somedata test"; + pSender.send(EventData.create(testMessage.getBytes())); + + int scanned = 0; + boolean found = false; + while ((scanned < 10000) && !found) { + System.out.println("Scanned " + scanned); + final Iterable events = pReceiver.receiveSync(100); + for (EventData ed : events) { + scanned++; + if ((new String(ed.getBytes())).equals(testMessage)) { + found = true; + break; + } + } + } + + pSender.closeSync(); + pReceiver.closeSync();; + ehc.closeSync(); + + Assert.assertTrue(found); + } + + abstract String tokenGet(final String authority, final String clientId, final String clientSecret, final String audience, final String extra) + throws MalformedURLException, InterruptedException, ExecutionException; + + protected class AuthCallback implements AzureActiveDirectoryTokenProvider.AuthenticationCallback { + final private String clientId; + final private String clientSecret; + + public AuthCallback(final String clientId, final String clientSecret) { + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + @Override + public CompletableFuture acquireToken(String audience, String authority, Object state) { + return CompletableFuture.supplyAsync(new TestTokenSupplier(authority, this.clientId, this.clientSecret, audience)); + } + } + + protected class CustomTokenProvider implements ITokenProvider { + final private TestTokenSupplier supplier; + + public CustomTokenProvider(final String authority, final String clientId, final String clientSecret) { + this.supplier = new TestTokenSupplier(authority, clientId, clientSecret, ClientConstants.EVENTHUBS_AUDIENCE); + } + + @Override + public CompletableFuture getToken(String resource, Duration timeout) { + return CompletableFuture.supplyAsync(this.supplier).thenApply((rawToken) -> { + try { + return new JsonSecurityToken(rawToken, resource); + } catch (ParseException e) { + throw new CompletionException(e); + } + }); + } + } + + protected class TestTokenSupplier implements Supplier { + final private String authority; + final private String clientId; + final private String clientSecret; + final private String audience; + + public TestTokenSupplier(final String authority, final String clientId, final String clientSecret, final String audience) { + this.authority = authority; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.audience = audience; + } + + @Override + public String get() { + String retval = ""; + + try { + retval = tokenGet(this.authority, this.clientId, this.clientSecret, this.audience, ".default"); + } + catch (Exception e) { + throw new CompletionException(e); + } + + return retval; + } + } + + + protected String decodeToken(final String rawToken) { + String parts[] = rawToken.split("\\."); + String output = new String(Base64.getDecoder().decode(parts[0])); + output += '.'; + output += new String(Base64.getDecoder().decode(parts[1])); + return output; + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java new file mode 100644 index 000000000000..43d6383cacff --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java @@ -0,0 +1,67 @@ +package com.microsoft.azure.eventhubs.sendrecv; + +import java.net.MalformedURLException; +import java.util.concurrent.ExecutionException; + +import org.junit.Test; + +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; +import com.microsoft.azure.eventhubs.EventHubClient; + +public class AdalTest extends AadBase { + final private String authority = "https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47"; + //final private String authority = AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY; + final private String clientId = "51ce7943-a1aa-4bb1-b708-8f66656a9242"; + final private String clientSecret = "sMm5YoVVcmb4fUtm=dQa*nY-P-YEkx35"; + + @Test + public void runSendReceiveWithAuthCallbackTest() throws Exception { + final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); + final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, + callback, this.executorService, null).get(); + + innerTest(ehc); + } + + @Test + public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { + final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); + final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, + callback, this.authority, this.executorService, null).get(); + + innerTest(ehc); + } + + @Test + public void runSendReceiveWithAADTokenProvider() throws Exception { + final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); + final AzureActiveDirectoryTokenProvider aadTokenProvider = + new AzureActiveDirectoryTokenProvider(callback, this.authority, null); + final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, aadTokenProvider, + this.executorService, null).get(); + + innerTest(ehc); + } + + @Test + public void runSendReceiveWithCustomTokenProvider() throws Exception { + final CustomTokenProvider tokenProvider = new CustomTokenProvider(this.authority, this.clientId, this.clientSecret); + final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, tokenProvider, + this.executorService, null).get(); + + innerTest(ehc); + } + + @Override + String tokenGet(final String authority, final String clientId, final String clientSecret, final String audience, final String extra) + throws MalformedURLException, InterruptedException, ExecutionException { + AuthenticationContext context = new AuthenticationContext(authority, true, this.executorService); + ClientCredential creds = new ClientCredential(clientId, clientSecret); + AuthenticationResult result = context.acquireToken(audience, creds, null).get(); + System.out.println(decodeToken(result.getAccessToken())); + return result.getAccessToken(); + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java index 7d83c823e529..b66116330f27 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java @@ -3,70 +3,28 @@ package com.microsoft.azure.eventhubs.sendrecv; -import com.microsoft.aad.adal4j.AuthenticationContext; -import com.microsoft.aad.adal4j.AuthenticationResult; -import com.microsoft.aad.adal4j.ClientCredential; import com.microsoft.aad.msal4j.ClientCredentialParameters; import com.microsoft.aad.msal4j.ClientSecret; import com.microsoft.aad.msal4j.ConfidentialClientApplication; import com.microsoft.aad.msal4j.IAuthenticationResult; import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; -import com.microsoft.azure.eventhubs.ConnectionStringBuilder; -import com.microsoft.azure.eventhubs.EventData; import com.microsoft.azure.eventhubs.EventHubClient; -import com.microsoft.azure.eventhubs.EventHubException; -import com.microsoft.azure.eventhubs.ITokenProvider; -import com.microsoft.azure.eventhubs.JsonSecurityToken; -import com.microsoft.azure.eventhubs.PartitionReceiver; -import com.microsoft.azure.eventhubs.PartitionSender; -import com.microsoft.azure.eventhubs.SecurityToken; -import com.microsoft.azure.eventhubs.impl.ClientConstants; -import com.microsoft.azure.eventhubs.impl.EventPositionImpl; -import com.microsoft.azure.eventhubs.lib.ApiTestBase; -import com.microsoft.azure.eventhubs.lib.TestContext; import java.net.MalformedURLException; -import java.net.URI; -import java.text.ParseException; -import java.time.Duration; -import java.util.Base64; import java.util.Collections; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.function.Supplier; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; import org.junit.Test; -public class MsalTest extends ApiTestBase { - static URI endpoint; - static String eventHubName; - - final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(8); +public class MsalTest extends AadBase { final private String authority = "https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47"; //final private String authority = AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY; final private String clientId = "51ce7943-a1aa-4bb1-b708-8f66656a9242"; final private String clientSecret = "sMm5YoVVcmb4fUtm=dQa*nY-P-YEkx35"; - @BeforeClass - public static void initializeEventHub() throws Exception { - ConnectionStringBuilder csb = TestContext.getConnectionString(); - MsalTest.endpoint = csb.getEndpoint(); - MsalTest.eventHubName = csb.getEventHubName(); - } - - @AfterClass() - public static void cleanup() throws EventHubException { - } - @Test public void runSendReceiveWithAuthCallbackTest() throws Exception { - final AuthCallback callback = new AuthCallback(); + final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, callback, this.executorService, null).get(); @@ -75,44 +33,16 @@ public void runSendReceiveWithAuthCallbackTest() throws Exception { @Test public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { - final AuthCallback callback = new AuthCallback(); + final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, callback, this.authority, this.executorService, null).get(); innerTest(ehc); } - private void innerTest(final EventHubClient ehc) throws EventHubException { - final PartitionReceiver pReceiver = ehc.createReceiverSync("$Default", "0", EventPositionImpl.fromEndOfStream()); - - final PartitionSender pSender = ehc.createPartitionSenderSync("0"); - final String testMessage = "somedata test"; - pSender.send(EventData.create(testMessage.getBytes())); - - int scanned = 0; - boolean found = false; - while ((scanned < 10000) && !found) { - System.out.println("Scanned " + scanned); - final Iterable events = pReceiver.receiveSync(100); - for (EventData ed : events) { - scanned++; - if ((new String(ed.getBytes())).equals(testMessage)) { - found = true; - break; - } - } - } - - pSender.closeSync(); - pReceiver.closeSync();; - ehc.closeSync(); - - Assert.assertTrue(found); - } - @Test public void runSendReceiveWithAADTokenProvider() throws Exception { - final AuthCallback callback = new AuthCallback(); + final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final AzureActiveDirectoryTokenProvider aadTokenProvider = new AzureActiveDirectoryTokenProvider(callback, this.authority, null); final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, aadTokenProvider, @@ -123,88 +53,21 @@ public void runSendReceiveWithAADTokenProvider() throws Exception { @Test public void runSendReceiveWithCustomTokenProvider() throws Exception { - final CustomTokenProvider tokenProvider = new CustomTokenProvider(); + final CustomTokenProvider tokenProvider = new CustomTokenProvider(this.authority, this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, tokenProvider, this.executorService, null).get(); innerTest(ehc); } - class CustomTokenProvider implements ITokenProvider, Supplier { - @Override - public CompletableFuture getToken(String resource, Duration timeout) { - return CompletableFuture.supplyAsync(this).thenApply((rawToken) -> { - try { - return new JsonSecurityToken(rawToken, resource); - } catch (ParseException e) { - throw new CompletionException(e); - } - }); - } - - @Override - public String get() { - String retval = ""; - - try { - retval = MsalTest.this.tokenGet(MsalTest.this.authority, MsalTest.this.clientId, MsalTest.this.clientSecret, ClientConstants.EVENTHUBS_AUDIENCE, ".default"); - } - catch (Exception e) { - throw new CompletionException(e); - } - - return retval; - } - } - - class AuthCallback implements AzureActiveDirectoryTokenProvider.AuthenticationCallback, Supplier { - private String audience; - private String callbackAuthority; - - @Override - public CompletableFuture acquireToken(String audience, String authority, Object state) { - this.audience = audience; - this.callbackAuthority = authority; - return CompletableFuture.supplyAsync(this); - } - - @Override - public String get() { - String retval = ""; - - try { - retval = MsalTest.this.tokenGet(this.callbackAuthority, MsalTest.this.clientId, MsalTest.this.clientSecret, this.audience, ".default"); - } - catch (Exception e) { - throw new CompletionException(e); - } - - return retval; - } - } - - private String tokenGet(final String authority, final String clientId, final String clientSecret, final String audience, final String extra) - throws MalformedURLException, InterruptedException, ExecutionException { - return adalGet(authority, clientId, clientSecret, audience); - //return msalGet(authority, clientId, clientSecret, audience + extra); - } - - private String adalGet(final String authority, final String clientId, final String clientSecret, final String audience) - throws MalformedURLException, InterruptedException, ExecutionException { - AuthenticationContext context = new AuthenticationContext(authority, true, MsalTest.this.executorService); - ClientCredential creds = new ClientCredential(clientId, clientSecret); - AuthenticationResult result = context.acquireToken(audience, creds, null).get(); - System.out.println(decodeToken(result.getAccessToken())); - return result.getAccessToken(); - } - - private String msalGet(final String authority, final String clientId, final String clientSecret, final String audience) + @Override + String tokenGet(final String authority, final String clientId, final String clientSecret, final String audience, final String extra) throws MalformedURLException, InterruptedException, ExecutionException { ConfidentialClientApplication app = ConfidentialClientApplication.builder(clientId, new ClientSecret(clientSecret)) .authority(authority) .build(); - ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(audience)).build(); + ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(audience + extra)).build(); IAuthenticationResult result = app.acquireToken(parameters).get(); @@ -212,12 +75,4 @@ private String msalGet(final String authority, final String clientId, final Stri System.out.println(decodeToken(result.accessToken())); return result.accessToken(); } - - private String decodeToken(final String rawToken) { - String parts[] = rawToken.split("\\."); - String output = new String(Base64.getDecoder().decode(parts[0])); - output += '.'; - output += new String(Base64.getDecoder().decode(parts[1])); - return output; - } } From 965c5f04aa6335684e3cb8d742a7486aa77efaf8 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Mon, 17 Jun 2019 17:46:11 -0700 Subject: [PATCH 13/22] Fix Managed Identity provider --- eventhubs/data-plane/azure-eventhubs/pom.xml | 1 + .../azure/eventhubs/ManagedIdentityTokenProvider.java | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs/pom.xml b/eventhubs/data-plane/azure-eventhubs/pom.xml index cf4da9fa2eac..17802877c81e 100644 --- a/eventhubs/data-plane/azure-eventhubs/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs/pom.xml @@ -35,6 +35,7 @@ com.microsoft.azure azure-client-authentication 1.6.7 + compile com.microsoft.azure diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java index 9af36d152965..c8e4b3d324d2 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ManagedIdentityTokenProvider.java @@ -7,15 +7,16 @@ import java.util.concurrent.CompletionException; import com.microsoft.azure.credentials.MSICredentials; +import com.microsoft.azure.eventhubs.impl.ClientConstants; public class ManagedIdentityTokenProvider implements ITokenProvider { + final static MSICredentials credentials = new MSICredentials(); + @Override public CompletableFuture getToken(final String resource, final Duration timeout) { - final MSICredentials credentials = new MSICredentials(); - return CompletableFuture.supplyAsync(() -> { try { - String rawToken = credentials.getToken(resource); + String rawToken = ManagedIdentityTokenProvider.credentials.getToken(ClientConstants.EVENTHUBS_AUDIENCE); return new JsonSecurityToken(rawToken, resource); } catch (IOException | ParseException e) { throw new CompletionException(e); From 40f3c73bbc96a734f1b31b432717913c370c7fe0 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Tue, 18 Jun 2019 14:36:41 -0700 Subject: [PATCH 14/22] Fix issues with Managed Identity and connection string --- .../microsoft/azure/eventhubs/ConnectionStringBuilder.java | 4 +++- .../com/microsoft/azure/eventhubs/impl/MessagingFactory.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java index cf1151f5d401..0118a4058f8f 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/ConnectionStringBuilder.java @@ -62,10 +62,12 @@ public final class ConnectionStringBuilder { static final String SHARED_ACCESS_SIGNATURE_CONFIG_NAME = "SharedAccessSignature"; static final String TRANSPORT_TYPE_CONFIG_NAME = "TransportType"; static final String AUTHENTICATION_CONFIG_NAME = "Authentication"; + + static public final String MANAGED_IDENTITY_AUTHENTICATION = "Managed Identity"; private static final String ALL_KEY_ENUMERATE_REGEX = "(" + HOST_NAME_CONFIG_NAME + "|" + ENDPOINT_CONFIG_NAME + "|" + SHARED_ACCESS_KEY_NANE_CONFIG_NAME + "|" + SHARED_ACCESS_KEY_CONFIG_NAME + "|" + SHARED_ACCESS_SIGNATURE_CONFIG_NAME + "|" + ENTITY_PATH_CONFIG_NAME + "|" + OPERATION_TIMEOUT_CONFIG_NAME - + "|" + TRANSPORT_TYPE_CONFIG_NAME + ")"; + + "|" + TRANSPORT_TYPE_CONFIG_NAME + "|" + AUTHENTICATION_CONFIG_NAME + ")"; private static final String KEYS_WITH_DELIMITERS_REGEX = KEY_VALUE_PAIR_DELIMITER + ALL_KEY_ENUMERATE_REGEX + KEY_VALUE_SEPARATOR; diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java index 77b1a045b665..74b6996e3321 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/MessagingFactory.java @@ -130,7 +130,7 @@ public static CompletableFuture createFromConnectionString( tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSharedAccessSignature()); } else if (!StringUtil.isNullOrWhiteSpace(csb.getSasKey())) { tokenProvider = new SharedAccessSignatureTokenProvider(csb.getSasKeyName(), csb.getSasKey()); - } else if ((csb.getAuthentication() != null) && csb.getAuthentication().equalsIgnoreCase("Managed Identity")) { + } else if ((csb.getAuthentication() != null) && csb.getAuthentication().equalsIgnoreCase(ConnectionStringBuilder.MANAGED_IDENTITY_AUTHENTICATION)) { tokenProvider = new ManagedIdentityTokenProvider(); } else { throw new IllegalArgumentException("Connection string must specify a Shared Access Signature, Shared Access Key, or Managed Identity"); From caf7260b90d1b3c4b6cb371f3f8466617a464b80 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Tue, 18 Jun 2019 15:22:14 -0700 Subject: [PATCH 15/22] AAD client tests finished --- .../azure/eventhubs/sendrecv/AadBase.java | 25 +++++++++- .../azure/eventhubs/sendrecv/AdalTest.java | 24 ++++++---- .../sendrecv/ManagedIdentityTest.java | 48 +++++++++++++++++++ .../azure/eventhubs/sendrecv/MsalTest.java | 27 ++++++----- 4 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ManagedIdentityTest.java diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java index 0dd2cd5cb172..241bc9469a48 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AadBase.java @@ -20,6 +20,7 @@ import com.microsoft.azure.eventhubs.EventData; import com.microsoft.azure.eventhubs.EventHubClient; import com.microsoft.azure.eventhubs.EventHubException; +import com.microsoft.azure.eventhubs.EventHubRuntimeInformation; import com.microsoft.azure.eventhubs.ITokenProvider; import com.microsoft.azure.eventhubs.JsonSecurityToken; import com.microsoft.azure.eventhubs.PartitionReceiver; @@ -30,6 +31,18 @@ import com.microsoft.azure.eventhubs.lib.ApiTestBase; import com.microsoft.azure.eventhubs.lib.TestContext; +/** + * Because of the way JUnit is structured, the individual test classes for AAD libraries have to implement + * the actual @Test methods. Each test method tests a different way of creating an EventHubClient, then calls + * innerTest() to perform a common set of data-plane operations which the token is expected to allow. + * + * Each test class implements tokenGet() to do the actual token-getting via that library's APIs. All the layers + * above that (callback, ITokenProvider, etc.) are just different plumbing to get the same token to where it + * needs to go, so those parts are implemented here in the common base class. + * + * decodeToken() provides a convenient way to get the token contents as a printable string, allowing the tester + * to verify token contents if things are not working as expected. + */ public abstract class AadBase extends ApiTestBase { protected static URI endpoint; protected static String eventHubName; @@ -43,13 +56,20 @@ public static void initializeEventHub() throws Exception { AadBase.eventHubName = csb.getEventHubName(); } - protected void innerTest(final EventHubClient ehc) throws EventHubException { + protected void innerTest(final EventHubClient ehc) throws EventHubException, InterruptedException, ExecutionException { + // Try some runtime operations. We don't care about the result, just that it doesn't throw. + EventHubRuntimeInformation ehri = ehc.getRuntimeInformation().get(); + ehc.getPartitionRuntimeInformation(ehri.getPartitionIds()[ehri.getPartitionCount() - 1]).get(); + + // Open a receiver final PartitionReceiver pReceiver = ehc.createReceiverSync("$Default", "0", EventPositionImpl.fromEndOfStream()); + // Open a sender and do a send. final PartitionSender pSender = ehc.createPartitionSenderSync("0"); final String testMessage = "somedata test"; pSender.send(EventData.create(testMessage.getBytes())); + // Do some receives. int scanned = 0; boolean found = false; while ((scanned < 10000) && !found) { @@ -64,6 +84,7 @@ protected void innerTest(final EventHubClient ehc) throws EventHubException { } } + // Close everything. pSender.closeSync(); pReceiver.closeSync();; ehc.closeSync(); @@ -85,6 +106,7 @@ public AuthCallback(final String clientId, final String clientSecret) { @Override public CompletableFuture acquireToken(String audience, String authority, Object state) { + //(new Throwable()).printStackTrace(); return CompletableFuture.supplyAsync(new TestTokenSupplier(authority, this.clientId, this.clientSecret, audience)); } } @@ -136,7 +158,6 @@ public String get() { } } - protected String decodeToken(final String rawToken) { String parts[] = rawToken.split("\\."); String output = new String(Base64.getDecoder().decode(parts[0])); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java index 43d6383cacff..d2216fd0fbfa 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java @@ -11,13 +11,20 @@ import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; import com.microsoft.azure.eventhubs.EventHubClient; +/** + * These JUnit test cases are all commented out by default because they can only be run with special setup. + * They extract the namespace (endpoint) and event hub name from the connection string in the environment variable + * which all test cases use, but they assume that the namespace (or event hub) has been set up with special permissions. + * Within the AAD directory indicated by "authority", there is a registered application with id "clientId" and a secret + * "clientSecret". This application has been granted the "Azure Event Hubs Data Owner" role on the namespace or + * event hub. + */ public class AdalTest extends AadBase { - final private String authority = "https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47"; - //final private String authority = AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY; - final private String clientId = "51ce7943-a1aa-4bb1-b708-8f66656a9242"; - final private String clientSecret = "sMm5YoVVcmb4fUtm=dQa*nY-P-YEkx35"; + final private String authority = "https://login.windows.net/replaceWithTenantIdGuid"; + final private String clientId = "replaceWithClientIdGuid"; + final private String clientSecret = "replaceWithClientSecret"; - @Test + //@Test public void runSendReceiveWithAuthCallbackTest() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, @@ -26,7 +33,7 @@ public void runSendReceiveWithAuthCallbackTest() throws Exception { innerTest(ehc); } - @Test + //@Test public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, @@ -35,7 +42,7 @@ public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { innerTest(ehc); } - @Test + //@Test public void runSendReceiveWithAADTokenProvider() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final AzureActiveDirectoryTokenProvider aadTokenProvider = @@ -46,7 +53,7 @@ public void runSendReceiveWithAADTokenProvider() throws Exception { innerTest(ehc); } - @Test + //@Test public void runSendReceiveWithCustomTokenProvider() throws Exception { final CustomTokenProvider tokenProvider = new CustomTokenProvider(this.authority, this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, tokenProvider, @@ -61,7 +68,6 @@ String tokenGet(final String authority, final String clientId, final String clie AuthenticationContext context = new AuthenticationContext(authority, true, this.executorService); ClientCredential creds = new ClientCredential(clientId, clientSecret); AuthenticationResult result = context.acquireToken(audience, creds, null).get(); - System.out.println(decodeToken(result.getAccessToken())); return result.getAccessToken(); } } diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ManagedIdentityTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ManagedIdentityTest.java new file mode 100644 index 000000000000..bc6f9204b3b9 --- /dev/null +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/ManagedIdentityTest.java @@ -0,0 +1,48 @@ +package com.microsoft.azure.eventhubs.sendrecv; + +import java.net.MalformedURLException; +import java.util.concurrent.ExecutionException; + +import org.junit.Test; + +import com.microsoft.azure.eventhubs.ConnectionStringBuilder; +import com.microsoft.azure.eventhubs.EventHubClient; +import com.microsoft.azure.eventhubs.ManagedIdentityTokenProvider; +import com.microsoft.azure.eventhubs.lib.TestContext; + +/** + * These JUnit test cases are all commented out by default because they can only be run with special setup. + * They assume that they are running in a VM which has been set up with a managed identity, and that the namespace + * (or event hub) has been set up with that managed identity granted the "Azure Event Hubs Data Owner" role on the + * namespace or event hub. + */ +public class ManagedIdentityTest extends AadBase { + //@Test + public void runSendReceiveWithMITokenProvider() throws Exception { + final ManagedIdentityTokenProvider aadTokenProvider = new ManagedIdentityTokenProvider(); + final EventHubClient ehc = EventHubClient.createWithTokenProvider(ManagedIdentityTest.endpoint, + ManagedIdentityTest.eventHubName, aadTokenProvider, + this.executorService, null).get(); + + innerTest(ehc); + } + + //@Test + public void runSendReceiveWithMIConnectionString() throws Exception { + final ConnectionStringBuilder csb = TestContext.getConnectionString(); + // Remove SAS info and replace with "Authentication=Managed Identity" + csb.setSasKey(null); + csb.setSasKeyName(null); + csb.setAuthentication(ConnectionStringBuilder.MANAGED_IDENTITY_AUTHENTICATION); + final EventHubClient ehc = EventHubClient.createFromConnectionString(csb.toString(), this.executorService).get(); + + innerTest(ehc); + } + + @Override + String tokenGet(String authority, String clientId, String clientSecret, String audience, String extra) + throws MalformedURLException, InterruptedException, ExecutionException { + // Not used for these cases but required by AadBase + return null; + } +} diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java index b66116330f27..7f219a307fc8 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java @@ -16,13 +16,20 @@ import org.junit.Test; +/** + * These JUnit test cases are all commented out by default because they can only be run with special setup. + * They extract the namespace (endpoint) and event hub name from the connection string in the environment variable + * which all test cases use, but they assume that the namespace (or event hub) has been set up with special permissions. + * Within the AAD directory indicated by "authority", there is a registered application with id "clientId" and a secret + * "clientSecret". This application has been granted the "Azure Event Hubs Data Owner" role on the namespace or + * event hub. + */ public class MsalTest extends AadBase { - final private String authority = "https://login.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47"; - //final private String authority = AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY; - final private String clientId = "51ce7943-a1aa-4bb1-b708-8f66656a9242"; - final private String clientSecret = "sMm5YoVVcmb4fUtm=dQa*nY-P-YEkx35"; + final private String authority = "https://login.windows.net/replaceWithTenantIdGuid"; + final private String clientId = "replaceWithClientIdGuid"; + final private String clientSecret = "replaceWithClientSecret"; - @Test + //@Test public void runSendReceiveWithAuthCallbackTest() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, @@ -31,7 +38,7 @@ public void runSendReceiveWithAuthCallbackTest() throws Exception { innerTest(ehc); } - @Test + //@Test public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, @@ -40,7 +47,7 @@ public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { innerTest(ehc); } - @Test + //@Test public void runSendReceiveWithAADTokenProvider() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final AzureActiveDirectoryTokenProvider aadTokenProvider = @@ -51,7 +58,7 @@ public void runSendReceiveWithAADTokenProvider() throws Exception { innerTest(ehc); } - @Test + //@Test public void runSendReceiveWithCustomTokenProvider() throws Exception { final CustomTokenProvider tokenProvider = new CustomTokenProvider(this.authority, this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithTokenProvider(MsalTest.endpoint, MsalTest.eventHubName, tokenProvider, @@ -70,9 +77,7 @@ String tokenGet(final String authority, final String clientId, final String clie ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(audience + extra)).build(); IAuthenticationResult result = app.acquireToken(parameters).get(); - - System.out.println(result.accessToken()); - System.out.println(decodeToken(result.accessToken())); + return result.accessToken(); } } From a11307680443a20739fdf106aff2c6481011da3e Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Tue, 18 Jun 2019 16:00:03 -0700 Subject: [PATCH 16/22] Removed common authority and APIs/tests using it --- .../AzureActiveDirectoryTokenProvider.java | 2 -- .../azure/eventhubs/EventHubClient.java | 25 ------------------- .../azure/eventhubs/sendrecv/AdalTest.java | 9 ------- .../azure/eventhubs/sendrecv/MsalTest.java | 9 ------- 4 files changed, 45 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java index 96e6e50c0aa5..5247aebf7cf1 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/AzureActiveDirectoryTokenProvider.java @@ -11,8 +11,6 @@ import com.microsoft.azure.eventhubs.impl.ClientConstants; public final class AzureActiveDirectoryTokenProvider implements ITokenProvider { - public final static String COMMON_AUTHORITY = "https://login.microsoftonline.com/common"; - private final AuthenticationCallback authCallback; private final String authority; private final Object authCallbackState; diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java index e3597fffba79..3d11e7ee95d3 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/EventHubClient.java @@ -84,31 +84,6 @@ static CompletableFuture createFromConnectionString( return EventHubClientImpl.create(connectionString, retryPolicy, executor); } - - - /** - * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. - * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. - *

The {@link EventHubClient} created from this method creates a Sender instance internally, which is used by the {@link #send(EventData)} methods. - * - * @param endpointAddress namespace level endpoint. This needs to be in the format of scheme://fullyQualifiedServiceBusNamespaceEndpointName - * @param eventHubName EventHub name - * @param authCallback A callback which returns a JSON Web Token obtained from AAD. - * @param executor An {@link ScheduledExecutorService} to run all tasks performed by {@link EventHubClient}. - * @param options Options {@link EventHubClientOptions} for creating the client. Uses all defaults if null. - * @return EventHubClient which can be used to create Senders and Receivers to EventHub - * @throws EventHubException If the EventHubs service encountered problems during connection creation. - * @throws IOException If the underlying Proton-J layer encounter network errors. - */ - public static CompletableFuture createWithAzureActiveDirectory( - final URI endpointAddress, - final String eventHubName, - final AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback, - final ScheduledExecutorService executor, - final EventHubClientOptions options) throws EventHubException, IOException { - return createWithAzureActiveDirectory(endpointAddress, eventHubName, authCallback, AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY, executor, options); - } - /** * Factory method to create an instance of {@link EventHubClient} using the supplied namespace endpoint address, eventhub name and authentication mechanism. * In a normal scenario (when re-direct is not enabled) - one EventHubClient instance maps to one Connection to the Azure ServiceBus EventHubs service. diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java index d2216fd0fbfa..7705a16cf917 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/AdalTest.java @@ -26,15 +26,6 @@ public class AdalTest extends AadBase { //@Test public void runSendReceiveWithAuthCallbackTest() throws Exception { - final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); - final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, - callback, this.executorService, null).get(); - - innerTest(ehc); - } - - //@Test - public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, callback, this.authority, this.executorService, null).get(); diff --git a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java index 7f219a307fc8..605e40000632 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java +++ b/eventhubs/data-plane/azure-eventhubs/src/test/java/com/microsoft/azure/eventhubs/sendrecv/MsalTest.java @@ -31,15 +31,6 @@ public class MsalTest extends AadBase { //@Test public void runSendReceiveWithAuthCallbackTest() throws Exception { - final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); - final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, - callback, this.executorService, null).get(); - - innerTest(ehc); - } - - //@Test - public void runSendReceiveWithAuthCallbackWithAuthorityTest() throws Exception { final AuthCallback callback = new AuthCallback(this.clientId, this.clientSecret); final EventHubClient ehc = EventHubClient.createWithAzureActiveDirectory(MsalTest.endpoint, MsalTest.eventHubName, callback, this.authority, this.executorService, null).get(); From a213dd3264dca34fd43ebc0bcf8c0e5798432f03 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Tue, 18 Jun 2019 16:04:29 -0700 Subject: [PATCH 17/22] Common authority was also used in EPH, remove --- .../eventprocessorhost/EventProcessorHost.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java index d61ecadd8248..7d37b12826aa 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java @@ -453,15 +453,6 @@ static interface AuthStep { } static interface AADAuthStep { - /** - * Provide a callback which will be called when a token is needed. See {@link AzureActiveDirectoryTokenProvider} - * The callback will be called with an AAD authority string which is appropriate for public Azure. - * - * @param authCallback the callback - * @return interface for setting optional values - */ - OptionalStep useAuthenticationCallback(AuthenticationCallback authCallback); - /** * Provide a callback which will be called when a token is needed. See {@link AzureActiveDirectoryTokenProvider} * @@ -588,11 +579,6 @@ public OptionalStep setOperationTimeout(final Duration operationTimeout) { return this; } - @Override - public OptionalStep useAuthenticationCallback(final AuthenticationCallback authCallback) { - return useAuthenticationCallback(authCallback, AzureActiveDirectoryTokenProvider.COMMON_AUTHORITY); - } - @Override public OptionalStep useAuthenticationCallback(final AuthenticationCallback authCallback, final String authority) { Objects.requireNonNull(authCallback); From 54f7fd8e6fcbe436823d90438c3cafe041bb55fe Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Thu, 27 Jun 2019 18:12:00 -0700 Subject: [PATCH 18/22] Take StorageCredentials to support Storage AAD for EPH --- .../AzureStorageCheckpointLeaseManager.java | 32 ++++++++++++++++--- .../EventProcessorHost.java | 22 +++++++------ .../CheckpointManagerTest.java | 2 +- .../eventprocessorhost/LeaseManagerTest.java | 2 +- .../azure/eventprocessorhost/Repros.java | 10 +++--- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/AzureStorageCheckpointLeaseManager.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/AzureStorageCheckpointLeaseManager.java index e44b6d2ccdf6..6261138e5dea 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/AzureStorageCheckpointLeaseManager.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/AzureStorageCheckpointLeaseManager.java @@ -6,6 +6,7 @@ import com.google.gson.Gson; import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.CloudStorageAccount; +import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageExtendedErrorInformation; @@ -46,6 +47,7 @@ class AzureStorageCheckpointLeaseManager implements ICheckpointManager, ILeaseMa private static final String METADATA_OWNER_NAME = "OWNINGHOST"; private final String storageConnectionString; + private final StorageCredentials storageCredentials; private final String storageBlobPrefix; private final BlobRequestOptions leaseOperationOptions = new BlobRequestOptions(); private final BlobRequestOptions checkpointOperationOptions = new BlobRequestOptions(); @@ -59,16 +61,30 @@ class AzureStorageCheckpointLeaseManager implements ICheckpointManager, ILeaseMa private Hashtable latestCheckpoint = new Hashtable(); - AzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName) { - this(storageConnectionString, storageContainerName, ""); - } - AzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName, String storageBlobPrefix) { if ((storageConnectionString == null) || storageConnectionString.trim().isEmpty()) { throw new IllegalArgumentException("Provide valid Azure Storage connection string when using Azure Storage"); } this.storageConnectionString = storageConnectionString; + this.storageCredentials = null; + + if ((storageContainerName != null) && storageContainerName.trim().isEmpty()) { + throw new IllegalArgumentException("Azure Storage container name must be a valid container name or null to use the default"); + } + this.storageContainerName = storageContainerName; + // Convert all-whitespace prefix to empty string. Convert null prefix to empty string. + // Then the rest of the code only has one case to worry about. + this.storageBlobPrefix = (storageBlobPrefix != null) ? storageBlobPrefix.trim() : ""; + } + + AzureStorageCheckpointLeaseManager(StorageCredentials storageCredentials, String storageContainerName, String storageBlobPrefix) { + if (storageCredentials == null) { + throw new IllegalArgumentException("Provide valid Azure Storage credentials when using Azure Storage"); + } + this.storageConnectionString = null; + this.storageCredentials = storageCredentials; + if ((storageContainerName != null) && storageContainerName.trim().isEmpty()) { throw new IllegalArgumentException("Azure Storage container name must be a valid container name or null to use the default"); } @@ -102,7 +118,13 @@ void initialize(HostContext hostContext) throws InvalidKeyException, URISyntaxEx + "Must be from 3 to 63 characters long."); } - this.storageClient = CloudStorageAccount.parse(this.storageConnectionString).createCloudBlobClient(); + CloudStorageAccount storageAccount = null; + if (this.storageConnectionString != null) { + storageAccount = CloudStorageAccount.parse(this.storageConnectionString); + } else { + storageAccount = new CloudStorageAccount(this.storageCredentials); + } + this.storageClient = storageAccount.createCloudBlobClient(); this.eventHubContainer = this.storageClient.getContainerReference(this.storageContainerName); diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java index 7d37b12826aa..326789203a04 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java @@ -11,6 +11,7 @@ import com.microsoft.azure.eventhubs.RetryPolicy; import com.microsoft.azure.eventhubs.TransportType; import com.microsoft.azure.eventhubs.impl.StringUtil; +import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageException; import org.slf4j.Logger; @@ -397,19 +398,20 @@ static interface ManagerStep { * * @param storageConnectionString connection string for an Azure Storage account * @param storageContainerName name for the blob container within the Storage account + * @param storageBlobPrefix prefix for the names of the blobs within the blob container, can be empty or null * @return interface for setting the Event Hub connection info and auth */ - AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName); + AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName, String storageBlobPrefix); /** * Use the built-in Azure Storage-based lease and checkpoint managers. * - * @param storageConnectionString connection string for an Azure Storage account + * @param storageCredentials credentials for an Azure Storage account, such as an AAD token * @param storageContainerName name for the blob container within the Storage account - * @param storageBlobPrefix prefix for the names of the blobs within the blob container + * @param storageBlobPrefix prefix for the names of the blobs within the blob container, can be empty or null * @return interface for setting the Event Hub connection info and auth */ - AuthStep useAzureStorageCheckpointLeaseManager(String storageConnectionString, String storageContainerName, String storageBlobPrefix); + AuthStep useAzureStorageCheckpointLeaseManager(StorageCredentials storageCredentials, String storageContainerName, String storageBlobPrefix); /** * Use user-implemented lease and checkpoint managers. @@ -632,14 +634,16 @@ public AADAuthStep useAADAuthentication(final URI endpoint, final String eventHu @Override public AuthStep useAzureStorageCheckpointLeaseManager(final String storageConnectionString, - final String storageContainerName) { - return useAzureStorageCheckpointLeaseManager(storageConnectionString, storageContainerName, ""); + final String storageContainerName, final String storageBlobPrefix) { + AzureStorageCheckpointLeaseManager mgr = new AzureStorageCheckpointLeaseManager(storageConnectionString, storageContainerName, storageBlobPrefix); + this.initializeManagers = true; + return useUserCheckpointAndLeaseManagers(mgr, mgr); } - + @Override - public AuthStep useAzureStorageCheckpointLeaseManager(final String storageConnectionString, + public AuthStep useAzureStorageCheckpointLeaseManager(final StorageCredentials storageCredentials, final String storageContainerName, final String storageBlobPrefix) { - AzureStorageCheckpointLeaseManager mgr = new AzureStorageCheckpointLeaseManager(storageConnectionString, storageContainerName, storageBlobPrefix); + AzureStorageCheckpointLeaseManager mgr = new AzureStorageCheckpointLeaseManager(storageCredentials, storageContainerName, storageBlobPrefix); this.initializeManagers = true; return useUserCheckpointAndLeaseManagers(mgr, mgr); } diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java index 7d89062a0756..669aebb50c04 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/CheckpointManagerTest.java @@ -232,7 +232,7 @@ private void setupOneManager(boolean useAzureStorage, int index, String suffix, } else { TestBase.logInfo("Container name: " + containerName); String azureStorageConnectionString = TestUtilities.getStorageConnectionString(); - AzureStorageCheckpointLeaseManager azMgr = new AzureStorageCheckpointLeaseManager(azureStorageConnectionString, containerName); + AzureStorageCheckpointLeaseManager azMgr = new AzureStorageCheckpointLeaseManager(azureStorageConnectionString, containerName, null); leaseMgr = azMgr; checkpointMgr = azMgr; } diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java index 7d9e5878d522..c819b0fee2cb 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/LeaseManagerTest.java @@ -281,7 +281,7 @@ private void setupOneManager(boolean useAzureStorage, int index, String suffix, } else { TestBase.logInfo("Container name: " + containerName); String azureStorageConnectionString = TestUtilities.getStorageConnectionString(); - AzureStorageCheckpointLeaseManager azMgr = new AzureStorageCheckpointLeaseManager(azureStorageConnectionString, containerName); + AzureStorageCheckpointLeaseManager azMgr = new AzureStorageCheckpointLeaseManager(azureStorageConnectionString, containerName, null); leaseMgr = azMgr; checkpointMgr = azMgr; } diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java index a49a94165359..9c072cd18d4d 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/Repros.java @@ -43,7 +43,7 @@ public void conflictingHosts() throws Exception { PrefabGeneralErrorHandler general1 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory1 = new PrefabProcessorFactory(telltale, doCheckpointing, doMarker); EventProcessorHost host1 = EventProcessorHost.EventProcessorHostBuilder.newBuilder(conflictingName, utils.getConsumerGroup()) - .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName, null) .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) .build(); EventProcessorOptions options1 = EventProcessorOptions.getDefaultOptions(); @@ -52,7 +52,7 @@ public void conflictingHosts() throws Exception { PrefabGeneralErrorHandler general2 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory2 = new PrefabProcessorFactory(telltale, doCheckpointing, doMarker); EventProcessorHost host2 = EventProcessorHost.EventProcessorHostBuilder.newBuilder(conflictingName, utils.getConsumerGroup()) - .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName, null) .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) .build(); EventProcessorOptions options2 = EventProcessorOptions.getDefaultOptions(); @@ -118,7 +118,7 @@ public void infiniteReceive2Hosts() throws Exception { PrefabGeneralErrorHandler general1 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory1 = new PrefabProcessorFactory("never match", PrefabEventProcessor.CheckpointChoices.CKP_NONE, true, false); EventProcessorHost host1 = EventProcessorHost.EventProcessorHostBuilder.newBuilder("infiniteReceive2Hosts-1", utils.getConsumerGroup()) - .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName, null) .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) .build(); EventProcessorOptions options1 = EventProcessorOptions.getDefaultOptions(); @@ -127,7 +127,7 @@ public void infiniteReceive2Hosts() throws Exception { PrefabGeneralErrorHandler general2 = new PrefabGeneralErrorHandler(); PrefabProcessorFactory factory2 = new PrefabProcessorFactory("never match", PrefabEventProcessor.CheckpointChoices.CKP_NONE, true, false); EventProcessorHost host2 = EventProcessorHost.EventProcessorHostBuilder.newBuilder("infiniteReceive2Hosts-1", utils.getConsumerGroup()) - .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName, null) .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) .build(); EventProcessorOptions options2 = EventProcessorOptions.getDefaultOptions(); @@ -180,7 +180,7 @@ public void infiniteReceive2Hosts() throws Exception { } else { factory2 = new PrefabProcessorFactory("never match", PrefabEventProcessor.CheckpointChoices.CKP_NONE, true, false); host2 = EventProcessorHost.EventProcessorHostBuilder.newBuilder("infiniteReceive2Hosts-1", utils.getConsumerGroup()) - .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName) + .useAzureStorageCheckpointLeaseManager(TestUtilities.getStorageConnectionString(), storageName, null) .useEventHubConnectionString(utils.getConnectionString(true).toString(), utils.getConnectionString(true).getEventHubName()) .build(); options2 = EventProcessorOptions.getDefaultOptions(); From 64519e1630d5b4c7572ce6e8cdc5ecdca36a6bf2 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Thu, 27 Jun 2019 18:20:09 -0700 Subject: [PATCH 19/22] Update version --- eventhubs/data-plane/azure-eventhubs-eph/pom.xml | 4 ++-- eventhubs/data-plane/azure-eventhubs-extensions/pom.xml | 2 +- eventhubs/data-plane/azure-eventhubs/pom.xml | 2 +- .../com/microsoft/azure/eventhubs/impl/ClientConstants.java | 2 +- eventhubs/data-plane/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/pom.xml b/eventhubs/data-plane/azure-eventhubs-eph/pom.xml index 0a3bd414a75e..a60fcba7009e 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs-eph/pom.xml @@ -7,14 +7,14 @@ com.microsoft.azure azure-eventhubs-clients - 2.3.1 + 3.0.0 ../pom.xml 4.0.0 com.microsoft.azure azure-eventhubs-eph - 2.5.1 + 3.0.0 Microsoft Azure SDK for Event Hubs Event Processor Host(EPH) EPH is built on top of the Azure Event Hubs Client and provides a number of features not present in that lower layer diff --git a/eventhubs/data-plane/azure-eventhubs-extensions/pom.xml b/eventhubs/data-plane/azure-eventhubs-extensions/pom.xml index a2a85d70883b..4b64c5b7380e 100644 --- a/eventhubs/data-plane/azure-eventhubs-extensions/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs-extensions/pom.xml @@ -7,7 +7,7 @@ com.microsoft.azure azure-eventhubs-clients - 2.3.1 + 3.0.0 ../pom.xml diff --git a/eventhubs/data-plane/azure-eventhubs/pom.xml b/eventhubs/data-plane/azure-eventhubs/pom.xml index 17802877c81e..80ed8c76e51a 100644 --- a/eventhubs/data-plane/azure-eventhubs/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs/pom.xml @@ -7,7 +7,7 @@ com.microsoft.azure azure-eventhubs-clients - 2.3.1 + 3.0.0 ../pom.xml diff --git a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java index d21cef26be60..497d72f0cb05 100644 --- a/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java +++ b/eventhubs/data-plane/azure-eventhubs/src/main/java/com/microsoft/azure/eventhubs/impl/ClientConstants.java @@ -37,7 +37,7 @@ public final class ClientConstants { public static final String NO_RETRY = "NoRetry"; public static final String DEFAULT_RETRY = "Default"; public static final String PRODUCT_NAME = "MSJavaClient"; - public static final String CURRENT_JAVACLIENT_VERSION = "2.3.1"; + public static final String CURRENT_JAVACLIENT_VERSION = "3.0.0"; public static final String PLATFORM_INFO = getPlatformInfo(); public static final String FRAMEWORK_INFO = getFrameworkInfo(); public static final String CBS_ADDRESS = "$cbs"; diff --git a/eventhubs/data-plane/pom.xml b/eventhubs/data-plane/pom.xml index 3c1235e0732c..db12f95d4e9f 100644 --- a/eventhubs/data-plane/pom.xml +++ b/eventhubs/data-plane/pom.xml @@ -15,7 +15,7 @@ com.microsoft.azure azure-eventhubs-clients pom - 2.3.1 + 3.0.0 Microsoft Azure Event Hubs SDK Parent Java libraries for talking to Windows Azure Event Hubs From 6065b145dfd1a3480fcfd27a88fb3a69ff83fc67 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Fri, 28 Jun 2019 15:55:30 -0700 Subject: [PATCH 20/22] Executor not handled properly during EPH build/init --- .../EventHubClientFactory.java | 29 +++++++++---------- .../EventProcessorHost.java | 12 ++++---- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java index 3fa4185f06e7..e91a5d13dc96 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventHubClientFactory.java @@ -16,29 +16,30 @@ import com.microsoft.azure.eventhubs.RetryPolicy; abstract class EventHubClientFactory { - protected final ScheduledExecutorService executor; + protected ScheduledExecutorService executor; protected final EventHubClientOptions options; - public EventHubClientFactory(final ScheduledExecutorService executor, final RetryPolicy retryPolicy) { - this(executor, (new EventHubClientOptions()).setRetryPolicy(retryPolicy)); + public EventHubClientFactory(final RetryPolicy retryPolicy) { + this((new EventHubClientOptions()).setRetryPolicy(retryPolicy)); } - public EventHubClientFactory(final ScheduledExecutorService executor, - final EventHubClientOptions options) { - this.executor = executor; + public EventHubClientFactory(final EventHubClientOptions options) { this.options = options; } + public void setExecutor(ScheduledExecutorService executor) { + this.executor = executor; + } + abstract CompletableFuture createEventHubClient() throws EventHubException, IOException; static class EHCFWithConnectionString extends EventHubClientFactory { private final String eventHubConnectionString; public EHCFWithConnectionString(final String eventHubConnectionString, - final RetryPolicy retryPolicy, - final ScheduledExecutorService executor) { - super(executor, retryPolicy); + final RetryPolicy retryPolicy) { + super(retryPolicy); this.eventHubConnectionString = eventHubConnectionString; } @@ -57,9 +58,8 @@ public EHCFWithAuthCallback(final URI endpoint, final String eventHubPath, final AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback, final String authority, - final EventHubClientOptions options, - final ScheduledExecutorService executor) { - super(executor, options); + final EventHubClientOptions options) { + super(options); this.endpoint = endpoint; this.eventHubPath = eventHubPath; this.authCallback = authCallback; @@ -80,9 +80,8 @@ static class EHCFWithTokenProvider extends EventHubClientFactory { public EHCFWithTokenProvider(final URI endpoint, final String eventHubPath, final ITokenProvider tokenProvider, - final EventHubClientOptions options, - final ScheduledExecutorService executor) { - super(executor, options); + final EventHubClientOptions options) { + super(options); this.endpoint = endpoint; this.eventHubPath = eventHubPath; this.tokenProvider = tokenProvider; diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java index 326789203a04..d6d4356d0de3 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java @@ -78,6 +78,7 @@ private EventProcessorHost( this.executorServicePoolSize, new EventProcessorHostThreadPoolFactory(hostName, eventHubPath, consumerGroupName)); } + eventHubClientFactory.setExecutor(this.executorService); this.hostContext = new HostContext(this.executorService, this, hostName, @@ -476,7 +477,7 @@ static interface AADAuthStep { static interface OptionalStep { /** * Event Processor Host runs tasks on the supplied threadpool, or creates an internal one. - * @param executor threadpool + * @param executor threadpool, or null to use an internal one * @return interface for setting optional values */ OptionalStep setExecutor(ScheduledExecutorService executor); @@ -553,8 +554,7 @@ public Steps(final String hostName, final String consumerGroupName) { @Override public OptionalStep setExecutor(final ScheduledExecutorService executor) { - Objects.requireNonNull(executor); - + // executor is allowed to be null, causes EPH to create and use an internal one this.executor = executor; return this; } @@ -666,12 +666,12 @@ public EventProcessorHost build() { EventHubClientFactory ehcFactory = null; if (this.eventHubConnectionString != null) { normalizeConnectionStringAndEventHubPath(); - ehcFactory = new EventHubClientFactory.EHCFWithConnectionString(this.eventHubConnectionString, this.retryPolicy, this.executor); + ehcFactory = new EventHubClientFactory.EHCFWithConnectionString(this.eventHubConnectionString, this.retryPolicy); } else if (this.authCallback != null) { ehcFactory = new EventHubClientFactory.EHCFWithAuthCallback(this.endpoint, this.eventHubPath, - this.authCallback, this.authority, packOptions(), this.executor); + this.authCallback, this.authority, packOptions()); } else if (this.tokenProvider != null) { - ehcFactory = new EventHubClientFactory.EHCFWithTokenProvider(this.endpoint, this.eventHubPath, this.tokenProvider, packOptions(), this.executor); + ehcFactory = new EventHubClientFactory.EHCFWithTokenProvider(this.endpoint, this.eventHubPath, this.tokenProvider, packOptions()); } return new EventProcessorHost(this.hostName, this.eventHubPath, From 149665f4c6fb9c80cefee3d9fa722362838dd36a Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Fri, 28 Jun 2019 17:19:18 -0700 Subject: [PATCH 21/22] EPH AAD test case --- .../data-plane/azure-eventhubs-eph/pom.xml | 6 +++ .../eventprocessorhost/PerTestSettings.java | 22 ++++++++ .../azure/eventprocessorhost/SmokeTest.java | 54 +++++++++++++++++++ .../azure/eventprocessorhost/TestBase.java | 19 +++++-- 4 files changed, 96 insertions(+), 5 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/pom.xml b/eventhubs/data-plane/azure-eventhubs-eph/pom.xml index a60fcba7009e..271053a5cf05 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/pom.xml +++ b/eventhubs/data-plane/azure-eventhubs-eph/pom.xml @@ -45,6 +45,12 @@ com.google.code.gson gson + + com.microsoft.azure + msal4j + 0.4.0-preview + test + diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PerTestSettings.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PerTestSettings.java index 1ad68b3950f5..7172e8a8e4a5 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PerTestSettings.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/PerTestSettings.java @@ -6,6 +6,8 @@ import java.util.ArrayList; import java.util.concurrent.ScheduledExecutorService; +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; + public class PerTestSettings { // In-out properties: may be set before test setup and then changed by setup. final EPHConstructorArgs inoutEPHConstructorArgs; @@ -60,6 +62,7 @@ class EPHConstructorArgs { static final int LEASE_MANAGER_OVERRIDE = 0x0800; static final int EXPLICIT_MANAGER = CHECKPOINT_MANAGER_OVERRIDE | LEASE_MANAGER_OVERRIDE; static final int TELLTALE_ON_TIMEOUT = 0x1000; + static final int AUTH_CALLBACK = 0x2000; private int flags; @@ -67,6 +70,8 @@ class EPHConstructorArgs { private String ehPath; private String consumerGroupName; private String ehConnection; + private AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback; + private String authAuthority; private String storageConnection; private String storageContainerName; private String storageBlobPrefix; @@ -81,6 +86,8 @@ class EPHConstructorArgs { this.ehPath = null; this.consumerGroupName = null; this.ehConnection = null; + this.authCallback = null; + this.authAuthority = null; this.storageConnection = null; this.storageContainerName = null; this.storageBlobPrefix = null; @@ -136,6 +143,21 @@ void setEHConnection(String ehConnection) { this.ehConnection = ehConnection; this.flags |= EH_CONNECTION_OVERRIDE; } + + AzureActiveDirectoryTokenProvider.AuthenticationCallback getAuthCallback() { + return this.authCallback; + } + + String getAuthAuthority() { + return this.authAuthority; + } + + void setAuthCallback(AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback, String authAuthority) + { + this.authCallback = authCallback; + this.authAuthority = authAuthority; + this.flags |= AUTH_CALLBACK; + } String getStorageConnection() { return this.storageConnection; diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/SmokeTest.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/SmokeTest.java index 1fc9b86b2eed..704b920b51ca 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/SmokeTest.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/SmokeTest.java @@ -3,11 +3,20 @@ package com.microsoft.azure.eventprocessorhost; +import com.microsoft.aad.msal4j.ClientCredentialParameters; +import com.microsoft.aad.msal4j.ClientSecret; +import com.microsoft.aad.msal4j.ConfidentialClientApplication; +import com.microsoft.aad.msal4j.IAuthenticationResult; +import com.microsoft.azure.eventhubs.AzureActiveDirectoryTokenProvider; import com.microsoft.azure.eventhubs.EventPosition; + import org.junit.Assert; import org.junit.Test; import java.time.Instant; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.Executors; @@ -22,6 +31,51 @@ public void sendRecv1MsgTest() throws Exception { testFinish(settings, SmokeTest.ANY_NONZERO_COUNT); } + + /** + * This JUnit test case is all commented out by default because it can only be run with special setup. + * It extracts the namespace (endpoint) and event hub name from the connection string in the environment variable + * which all test cases use, but it assumes that the namespace (or event hub) has been set up with special permissions. + * Within the AAD directory indicated by "authority", there is a registered application with id "clientId" and a secret + * "clientSecret". This application has been granted the "Azure Event Hubs Data Owner" role on the namespace or + * event hub. + */ + //@Test + public void sendRecv1MsgAADTest() throws Exception { + PerTestSettings settings = new PerTestSettings("SendRecv1MsgAAD"); + AzureActiveDirectoryTokenProvider.AuthenticationCallback authCallback = new MsalAuthCallback(); + String authAuthority = "https://login.windows.net/replaceWithTenantIdGuid"; + settings.inoutEPHConstructorArgs.setAuthCallback(authCallback, authAuthority); + settings = testSetup(settings); + + settings.outUtils.sendToAny(settings.outTelltale); + waitForTelltale(settings); + + testFinish(settings, SmokeTest.ANY_NONZERO_COUNT); + } + + private class MsalAuthCallback implements AzureActiveDirectoryTokenProvider.AuthenticationCallback { + final private String clientId = "replaceWithClientIdGuid"; + final private String clientSecret = "replaceWithClientSecret"; + + @Override + public CompletableFuture acquireToken(String audience, String authority, Object state) { + try { + ConfidentialClientApplication app = ConfidentialClientApplication.builder(this.clientId, new ClientSecret(this.clientSecret)) + .authority(authority) + .build(); + + ClientCredentialParameters parameters = ClientCredentialParameters.builder(Collections.singleton(audience + ".default")).build(); + + IAuthenticationResult result = app.acquireToken(parameters).get(); + + return CompletableFuture.completedFuture(result.accessToken()); + } + catch (Exception e) { + throw new CompletionException(e); + } + } + } @Test public void receiverRuntimeMetricsTest() throws Exception { diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java index 1270c56a6bd7..69451871e266 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/test/java/com/microsoft/azure/eventprocessorhost/TestBase.java @@ -6,6 +6,8 @@ import com.microsoft.azure.eventhubs.ConnectionStringBuilder; import com.microsoft.azure.eventhubs.EventHubClient; import com.microsoft.azure.eventhubs.EventHubException; +import com.microsoft.azure.eventprocessorhost.EventProcessorHost.EventProcessorHostBuilder.AuthStep; +import com.microsoft.azure.eventprocessorhost.EventProcessorHost.EventProcessorHostBuilder.OptionalStep; import org.junit.After; import org.junit.AfterClass; @@ -180,11 +182,18 @@ PerTestSettings testSetup(PerTestSettings settings) throws Exception { String effectiveBlobPrefix = settings.inoutEPHConstructorArgs.isFlagSet(PerTestSettings.EPHConstructorArgs.STORAGE_BLOB_PREFIX_OVERRIDE) ? settings.inoutEPHConstructorArgs.getStorageBlobPrefix() : null; - - settings.outHost = EventProcessorHost.EventProcessorHostBuilder.newBuilder(effectiveHostName, effectiveConsumerGroup) - .useAzureStorageCheckpointLeaseManager(effectiveStorageConnectionString, effectiveStorageContainerName, effectiveBlobPrefix) - .useEventHubConnectionString(effectiveConnectionString, effectiveEntityPath) - .setExecutor(effectiveExecutor).build(); + + AuthStep intermediate = EventProcessorHost.EventProcessorHostBuilder.newBuilder(effectiveHostName, effectiveConsumerGroup) + .useAzureStorageCheckpointLeaseManager(effectiveStorageConnectionString, effectiveStorageContainerName, effectiveBlobPrefix); + OptionalStep almostDone = null; + if (settings.inoutEPHConstructorArgs.isFlagSet(PerTestSettings.EPHConstructorArgs.AUTH_CALLBACK)) { + ConnectionStringBuilder csb = new ConnectionStringBuilder(effectiveConnectionString); + almostDone = intermediate.useAADAuthentication(csb.getEndpoint(), effectiveEntityPath) + .useAuthenticationCallback(settings.inoutEPHConstructorArgs.getAuthCallback(), settings.inoutEPHConstructorArgs.getAuthAuthority()); + } else { + almostDone = intermediate.useEventHubConnectionString(effectiveConnectionString, effectiveEntityPath); + } + settings.outHost = almostDone.setExecutor(effectiveExecutor).build(); } if (!settings.inEventHubDoesNotExist) { From 2580f9f0f5a879ef238d77b6842d888ee2f051f7 Mon Sep 17 00:00:00 2001 From: James Birdsall Date: Mon, 1 Jul 2019 14:03:22 -0700 Subject: [PATCH 22/22] Make builder interfaces public --- .../azure/eventprocessorhost/EventProcessorHost.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java index d6d4356d0de3..5fc9acbd1e71 100644 --- a/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java +++ b/eventhubs/data-plane/azure-eventhubs-eph/src/main/java/com/microsoft/azure/eventprocessorhost/EventProcessorHost.java @@ -393,7 +393,7 @@ public static ManagerStep newBuilder(final String hostName, final String consume private EventProcessorHostBuilder() { } - static interface ManagerStep { + public static interface ManagerStep { /** * Use the built-in Azure Storage-based lease and checkpoint managers. * @@ -424,7 +424,7 @@ static interface ManagerStep { AuthStep useUserCheckpointAndLeaseManagers(ICheckpointManager checkpointManager, ILeaseManager leaseManager); } - static interface AuthStep { + public static interface AuthStep { /** * Azure Portal can provide a connection string with auth information that applies only to one * individual Event Hub. In that case, the connection string contains the name of the Event Hub. @@ -455,7 +455,7 @@ static interface AuthStep { AADAuthStep useAADAuthentication(URI endpoint, String eventHubPath); } - static interface AADAuthStep { + public static interface AADAuthStep { /** * Provide a callback which will be called when a token is needed. See {@link AzureActiveDirectoryTokenProvider} * @@ -474,7 +474,7 @@ static interface AADAuthStep { OptionalStep useTokenProvider(ITokenProvider tokenProvider); } - static interface OptionalStep { + public static interface OptionalStep { /** * Event Processor Host runs tasks on the supplied threadpool, or creates an internal one. * @param executor threadpool, or null to use an internal one