com.microsoft.azure.eventhubs.impl.EventHubClientImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of azure-eventhubs Show documentation
Show all versions of azure-eventhubs Show documentation
Please note, a newer package azure-messaging-eventhubs for Azure Event Hubs is available at https://search.maven.org/artifact/com.azure/azure-messaging-eventhubs as of February 2020. While this package will continue to receive critical bug fixes, we strongly encourage you to upgrade. Read the migration guide at https://aka.ms/azsdk/java/migrate/eh for more details.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.azure.eventhubs.impl;
import com.microsoft.azure.eventhubs.BatchOptions;
import com.microsoft.azure.eventhubs.ConnectionStringBuilder;
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.ProxyConfiguration;
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.time.Duration;
import java.util.Date;
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;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Function;
public final class EventHubClientImpl extends ClientEntity implements EventHubClient {
/**
* It will be truncated to 128 characters
*/
public static String USER_AGENT = null;
private final String eventHubName;
private final Object senderCreateSync;
private volatile boolean isSenderCreateStarted;
private volatile MessagingFactory underlyingFactory;
private volatile MessageSender sender;
private volatile Timer timer;
private CompletableFuture createSender;
private EventHubClientImpl(final String eventHubName, final ScheduledExecutorService executor) {
super(StringUtil.getRandomString("EC"), null, executor);
this.eventHubName = eventHubName;
this.senderCreateSync = new Object();
}
public static CompletableFuture create(
final String connectionString, final RetryPolicy retryPolicy, final ScheduledExecutorService executor,
final ProxyConfiguration proxyConfiguration)
throws IOException {
return create(connectionString, retryPolicy, executor, proxyConfiguration, EventHubClientOptions.SILENT_OFF);
}
public static CompletableFuture create(
final String connectionString,
final RetryPolicy retryPolicy,
final ScheduledExecutorService executor,
final ProxyConfiguration proxyConfiguration,
final Duration watchdogTriggerTime)
throws IOException {
if (StringUtil.isNullOrWhiteSpace(connectionString)) {
throw new IllegalArgumentException("Connection string cannot be null or empty");
}
Objects.requireNonNull(executor, "Executor cannot be null.");
final ConnectionStringBuilder connStr = new ConnectionStringBuilder(connectionString);
final EventHubClientImpl eventHubClient = new EventHubClientImpl(connStr.getEventHubName(), executor);
return MessagingFactory.createFromConnectionString(connectionString, retryPolicy, executor, proxyConfiguration, watchdogTriggerTime)
.thenApplyAsync(new Function() {
@Override
public EventHubClient apply(MessagingFactory factory) {
eventHubClient.underlyingFactory = factory;
eventHubClient.timer = new Timer(factory);
return eventHubClient;
}
}, 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");
}
Objects.requireNonNull(tokenProvider, "Token provider cannot be null.");
final MessagingFactoryBuilder builder = new MessagingFactoryBuilder(endpoint.getHost(), tokenProvider, executor);
if (options != null) {
builder.setOperationTimeout(options.getOperationTimeout())
.setTransportType(options.getTransportType())
.setRetryPolicy(options.getRetryPolicy())
.setProxyConfiguration(options.getProxyConfiguration())
.setWatchdogTriggerTime(options.getMaximumSilentTime());
}
return create(eventHubName, executor, builder.build());
}
/**
* Package private method for creating an EventHub client with the given messaging factory.
*
* @param eventHubName Name of the Event Hub to connect to.
* @param executor Thread pool to run on.
* @param messagingFactory A future that completes with the messaging factory.
* @return A new EventHubClient.
*/
static CompletableFuture create(
final String eventHubName,
final ScheduledExecutorService executor,
final CompletableFuture messagingFactory) {
if (StringUtil.isNullOrWhiteSpace(eventHubName)) {
throw new IllegalArgumentException("Event hub name cannot be null or empty");
}
return messagingFactory.thenApplyAsync(factory -> {
final EventHubClientImpl eventHubClient = new EventHubClientImpl(eventHubName, executor);
eventHubClient.underlyingFactory = factory;
eventHubClient.timer = new Timer(factory);
return eventHubClient;
}, executor);
}
public String getEventHubName() {
return eventHubName;
}
public EventDataBatch createBatch(BatchOptions options) throws EventHubException {
return ExceptionUtil.sync(() -> {
int maxSize = this.createInternalSender().thenApplyAsync(
(aVoid) -> this.sender.getMaxMessageSize(),
this.executor).get();
if (options.maxMessageSize == null) {
return new EventDataBatchImpl(maxSize, options.partitionKey);
}
if (options.maxMessageSize > maxSize) {
throw new IllegalArgumentException("The maxMessageSize set in BatchOptions is too large. You set a maxMessageSize of "
+ options.maxMessageSize + ". The maximum allowed size is " + maxSize + ".");
}
return new EventDataBatchImpl(options.maxMessageSize, options.partitionKey);
}
);
}
@Override
public CompletableFuture send(final EventData data) {
if (data == null) {
throw new IllegalArgumentException("EventData cannot be empty.");
}
return this.createInternalSender().thenCompose(new Function>() {
@Override
public CompletableFuture apply(Void voidArg) {
return EventHubClientImpl.this.sender.send(((EventDataImpl) data).toAmqpMessage());
}
});
}
@Override
public CompletableFuture send(final Iterable eventDatas) {
if (eventDatas == null || IteratorUtil.sizeEquals(eventDatas, 0)) {
throw new IllegalArgumentException("Empty batch of EventData cannot be sent.");
}
return this.createInternalSender().thenCompose(new Function>() {
@Override
public CompletableFuture apply(Void voidArg) {
return EventHubClientImpl.this.sender.send(EventDataUtil.toAmqpMessages(eventDatas));
}
});
}
@Override
public CompletableFuture send(final EventDataBatch eventDatas) {
if (eventDatas == null || Integer.compare(eventDatas.getSize(), 0) == 0) {
throw new IllegalArgumentException("Empty batch of EventData cannot be sent.");
}
final EventDataBatchImpl eventDataBatch = (EventDataBatchImpl) eventDatas;
return eventDataBatch.getPartitionKey() != null
? this.send(eventDataBatch.getInternalIterable(), eventDataBatch.getPartitionKey())
: this.send(eventDataBatch.getInternalIterable());
}
@Override
public CompletableFuture send(final EventData eventData, final String partitionKey) {
if (eventData == null) {
throw new IllegalArgumentException("EventData cannot be null.");
}
if (partitionKey == null) {
throw new IllegalArgumentException("partitionKey cannot be null.");
}
return this.createInternalSender().thenCompose(new Function>() {
@Override
public CompletableFuture apply(Void voidArg) {
return EventHubClientImpl.this.sender.send(((EventDataImpl) eventData).toAmqpMessage(partitionKey));
}
});
}
@Override
public CompletableFuture send(final Iterable eventDatas, final String partitionKey) {
if (eventDatas == null || IteratorUtil.sizeEquals(eventDatas, 0)) {
throw new IllegalArgumentException("Empty batch of EventData cannot be sent.");
}
if (partitionKey == null) {
throw new IllegalArgumentException("partitionKey cannot be null.");
}
if (partitionKey.length() > ClientConstants.MAX_PARTITION_KEY_LENGTH) {
throw new IllegalArgumentException(
String.format(Locale.US, "PartitionKey exceeds the maximum allowed length of partitionKey: %s", ClientConstants.MAX_PARTITION_KEY_LENGTH));
}
return this.createInternalSender().thenCompose(new Function>() {
@Override
public CompletableFuture apply(Void voidArg) {
return EventHubClientImpl.this.sender.send(EventDataUtil.toAmqpMessages(eventDatas, partitionKey));
}
});
}
@Override
public CompletableFuture createPartitionSender(final String partitionId)
throws EventHubException {
return PartitionSenderImpl.create(this.underlyingFactory, this.eventHubName, partitionId, this.executor);
}
@Override
public CompletableFuture createReceiver(final String consumerGroupName, final String partitionId,
final EventPosition eventPosition)
throws EventHubException {
return this.createReceiver(consumerGroupName, partitionId, eventPosition, null);
}
@Override
public CompletableFuture createReceiver(final String consumerGroupName, final String partitionId,
final EventPosition eventPosition, final ReceiverOptions receiverOptions)
throws EventHubException {
return PartitionReceiverImpl.create(this.underlyingFactory, this.eventHubName, consumerGroupName, partitionId, eventPosition, PartitionReceiverImpl.NULL_EPOCH, false, receiverOptions, this.executor);
}
@Override
public CompletableFuture createEpochReceiver(final String consumerGroupName,
final String partitionId, final EventPosition eventPosition, final long epoch)
throws EventHubException {
return this.createEpochReceiver(consumerGroupName, partitionId, eventPosition, epoch, null);
}
@Override
public CompletableFuture createEpochReceiver(final String consumerGroupName,
final String partitionId, final EventPosition eventPosition, final long epoch,
final ReceiverOptions receiverOptions)
throws EventHubException {
return PartitionReceiverImpl.create(this.underlyingFactory, this.eventHubName, consumerGroupName, partitionId, eventPosition, epoch, true, receiverOptions, this.executor);
}
@Override
public CompletableFuture onClose() {
if (this.underlyingFactory != null) {
synchronized (this.senderCreateSync) {
final CompletableFuture internalSenderClose = this.sender != null
? this.sender.close().thenCompose(new Function>() {
@Override
public CompletableFuture apply(Void voidArg) {
return EventHubClientImpl.this.underlyingFactory.close();
}
})
: this.underlyingFactory.close();
return internalSenderClose;
}
}
return CompletableFuture.completedFuture(null);
}
private CompletableFuture createInternalSender() {
if (!this.isSenderCreateStarted) {
synchronized (this.senderCreateSync) {
if (!this.isSenderCreateStarted) {
String senderName = StringUtil.getRandomString("EC").concat(StringUtil.SEPARATOR + this.underlyingFactory.getClientId()).concat("-InternalSender");
this.createSender = MessageSender.create(this.underlyingFactory, senderName, this.eventHubName)
.thenAcceptAsync(new Consumer() {
public void accept(MessageSender a) {
EventHubClientImpl.this.sender = a;
}
}, this.executor);
this.isSenderCreateStarted = true;
}
}
}
return this.createSender;
}
@Override
public CompletableFuture getRuntimeInformation() {
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);
return addManagementToken(request).thenCompose((requestWithToken) -> managementWithRetry(requestWithToken)).
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));
}, this.executor);
}
@Override
public CompletableFuture getPartitionRuntimeInformation(String partitionId) {
throwIfClosed();
Map request = new HashMap();
request.put(ClientConstants.MANAGEMENT_ENTITY_TYPE_KEY, ClientConstants.MANAGEMENT_PARTITION_ENTITY_TYPE);
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);
return addManagementToken(request).thenCompose((requestWithToken) -> managementWithRetry(requestWithToken)).
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