All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.azure.eventhubs.EventHubClient Maven / Gradle / Ivy

There is a newer version: 3.3.0
Show newest version
/*
 * Copyright (c) Microsoft. All rights reserved.
 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
 */
package com.microsoft.azure.eventhubs;

import java.io.IOException;
import java.nio.channels.UnresolvedAddressException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

import com.microsoft.azure.servicebus.ClientConstants;
import com.microsoft.azure.servicebus.ClientEntity;
import com.microsoft.azure.servicebus.ConnectionStringBuilder;
import com.microsoft.azure.servicebus.IllegalEntityException;
import com.microsoft.azure.servicebus.IteratorUtil;
import com.microsoft.azure.servicebus.MessageSender;
import com.microsoft.azure.servicebus.MessagingFactory;
import com.microsoft.azure.servicebus.RetryPolicy;
import com.microsoft.azure.servicebus.PayloadSizeExceededException;
import com.microsoft.azure.servicebus.ReceiverDisconnectedException;
import com.microsoft.azure.servicebus.ServiceBusException;
import com.microsoft.azure.servicebus.StringUtil;
import com.microsoft.azure.servicebus.Timer;
import com.microsoft.azure.servicebus.TimerType;

/**
 * Anchor class - all EventHub client operations STARTS here.
 *
 * @see EventHubClient#createFromConnectionString(String)
 */
public class EventHubClient extends ClientEntity implements IEventHubClient {
    public static final String DEFAULT_CONSUMER_GROUP_NAME = "$Default";

    private final String eventHubName;
    private final Object senderCreateSync;

    private MessagingFactory underlyingFactory;
    private MessageSender sender;
    private boolean isSenderCreateStarted;
    private CompletableFuture createSender;

    private EventHubClient(final ConnectionStringBuilder connectionString) throws IOException, IllegalEntityException {
        super(StringUtil.getRandomString(), null);

        this.eventHubName = connectionString.getEntityPath();
        this.senderCreateSync = new Object();
    }

    /**
     * Synchronous version of {@link #createFromConnectionString(String)}.
     *
     * @param connectionString The connection string to be used. See {@link ConnectionStringBuilder} to construct a connectionString.
     * @return EventHubClient which can be used to create Senders and Receivers to EventHub
     * @throws ServiceBusException If Service Bus service encountered problems during connection creation.
     * @throws IOException         If the underlying Proton-J layer encounter network errors.
     */
    public static EventHubClient createFromConnectionStringSync(final String connectionString)
            throws ServiceBusException, IOException {
        return createFromConnectionStringSync(connectionString, null);
    }

    /**
     * Synchronous version of {@link #createFromConnectionString(String)}.
     *
     * @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.
     * @return EventHubClient which can be used to create Senders and Receivers to EventHub
     * @throws ServiceBusException If Service Bus service encountered problems during connection creation.
     * @throws IOException         If the underlying Proton-J layer encounter network errors.
     */
    public static EventHubClient createFromConnectionStringSync(final String connectionString, final RetryPolicy retryPolicy)
            throws ServiceBusException, IOException {
        try {
            return createFromConnectionString(connectionString, retryPolicy).get();
        } catch (InterruptedException | ExecutionException exception) {
            if (exception instanceof InterruptedException) {
                // Re-assert the thread's interrupted status
                Thread.currentThread().interrupt();
            }

            Throwable throwable = exception.getCause();
            if (throwable != null) {
                if (throwable instanceof RuntimeException) {
                    throw (RuntimeException) throwable;
                }

                if (throwable instanceof ServiceBusException) {
                    throw (ServiceBusException) throwable;
                }

                throw new ServiceBusException(true, throwable);
            }
        }

        return null;
    }

    /**
     * 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. * @return EventHubClient which can be used to create Senders and Receivers to EventHub * @throws ServiceBusException If Service Bus service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ public static CompletableFuture createFromConnectionString(final String connectionString) throws ServiceBusException, IOException { return createFromConnectionString(connectionString, null); } /** * 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. * @return EventHubClient which can be used to create Senders and Receivers to EventHub * @throws ServiceBusException If Service Bus service encountered problems during connection creation. * @throws IOException If the underlying Proton-J layer encounter network errors. */ public static CompletableFuture createFromConnectionString(final String connectionString, final RetryPolicy retryPolicy) throws ServiceBusException, IOException { final ConnectionStringBuilder connStr = new ConnectionStringBuilder(connectionString); final EventHubClient eventHubClient = new EventHubClient(connStr); return MessagingFactory.createFromConnectionString(connectionString.toString(), retryPolicy) .thenApply(new Function() { @Override public EventHubClient apply(MessagingFactory factory) { eventHubClient.underlyingFactory = factory; return eventHubClient; } }); } /** * Synchronous version of {@link #send(EventData)}. * * @param data the {@link EventData} to be sent. * @throws PayloadSizeExceededException if the total size of the {@link EventData} exceeds a predefined limit set by the service. Default is 256k bytes. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @throws UnresolvedAddressException if there are Client to Service network connectivity issues, if the Azure DNS resolution of the ServiceBus Namespace fails (ex: namespace deleted etc.) */ @Override public final void sendSync(final EventData data) throws ServiceBusException { try { this.send(data).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } } /** * Send {@link EventData} to EventHub. The sent {@link EventData} will land on any arbitrarily chosen EventHubs partition. *

There are 3 ways to send to EventHubs, each exposed as a method (along with its sendBatch overload): *

    *
  • {@link #send(EventData)} or {@link #send(Iterable)} *
  • {@link #send(EventData, String)} or {@link #send(Iterable, String)} *
  • {@link PartitionSender#send(EventData)} or {@link PartitionSender#send(Iterable)} *
*

Use this method to Send, if: *

     * a)  the send({@link EventData}) operation should be highly available and
     * b)  the data needs to be evenly distributed among all partitions; exception being, when a subset of partitions are unavailable
     * 
*

* {@link #send(EventData)} send's the {@link EventData} to a Service Gateway, which in-turn will forward the {@link EventData} to one of the EventHubs' partitions. Here's the message forwarding algorithm: *

     * i.  Forward the {@link EventData}'s to EventHub partitions, by equally distributing the data among all partitions (ex: Round-robin the {@link EventData}'s to all EventHubs' partitions)
     * ii. If one of the EventHub partitions is unavailable for a moment, the Service Gateway will automatically detect it and forward the message to another available partition - making the Send operation highly-available.
     * 
* * @param data the {@link EventData} to be sent. * @return a CompletableFuture that can be completed when the send operations is done.. * @see #send(EventData, String) * @see PartitionSender#send(EventData) */ @Override public final 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 EventHubClient.this.sender.send(data.toAmqpMessage()); } }); } /** * Synchronous version of {@link #send(Iterable)}. * * @param eventDatas batch of events to send to EventHub * @throws PayloadSizeExceededException if the total size of the {@link EventData} exceeds a pre-defined limit set by the service. Default is 256k bytes. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @throws UnresolvedAddressException if there are Client to Service network connectivity issues, if the Azure DNS resolution of the ServiceBus Namespace fails (ex: namespace deleted etc.) */ @Override public final void sendSync(final Iterable eventDatas) throws ServiceBusException { try { this.send(eventDatas).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } } /** * Send a batch of {@link EventData} to EventHub. The sent {@link EventData} will land on any arbitrarily chosen EventHubs partition. * This is the most recommended way to Send to EventHubs. *

There are 3 ways to send to EventHubs, to understand this particular type of Send refer to the overload {@link #send(EventData)}, which is used to send single {@link EventData}. * Use this overload versus {@link #send(EventData)}, if you need to send a batch of {@link EventData}. *

Sending a batch of {@link EventData}'s is useful in the following cases: *

     * i.	Efficient send - sending a batch of {@link EventData} maximizes the overall throughput by optimally using the number of sessions created to EventHubs' service.
     * ii.	Send multiple {@link EventData}'s in a Transaction. To achieve ACID properties, the Gateway Service will forward all {@link EventData}'s in the batch to a single EventHubs' partition.
     * 
*

* Sample code (sample uses sync version of the api but concept are identical): *

     * Gson gson = new GsonBuilder().create();
     * EventHubClient client = EventHubClient.createFromConnectionStringSync("__connection__");
     *
     * while (true)
     * {
     *     LinkedList{@literal<}EventData{@literal>} events = new LinkedList{@literal<}EventData{@literal>}();}
     *     for (int count = 1; count {@literal<} 11; count++)
     *     {
     *         PayloadEvent payload = new PayloadEvent(count);
     *         byte[] payloadBytes = gson.toJson(payload).getBytes(Charset.defaultCharset());
     *         EventData sendEvent = new EventData(payloadBytes);
     *         Map{@literal<}String, String{@literal>} applicationProperties = new HashMap{@literal<}String, String{@literal>}();
     *         applicationProperties.put("from", "javaClient");
     *         sendEvent.setProperties(applicationProperties);
     *         events.add(sendEvent);
     *     }
     *
     *     client.sendSync(events);
     *     System.out.println(String.format("Sent Batch... Size: %s", events.size()));
     * }
     * 
*

for Exceptions refer to {@link #sendSync(Iterable)} * * @param eventDatas batch of events to send to EventHub * @return a CompletableFuture that can be completed when the send operations is done.. * @see #send(EventData, String) * @see PartitionSender#send(EventData) */ @Override public final 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 EventHubClient.this.sender.send(EventDataUtil.toAmqpMessages(eventDatas)); } }); } /** * Synchronous version of {@link #send(EventData, String)}. * * @param eventData the {@link EventData} to be sent. * @param partitionKey the partitionKey will be hash'ed to determine the partitionId to send the eventData to. On the Received message this can be accessed at {@link EventData.SystemProperties#getPartitionKey()} * @throws PayloadSizeExceededException if the total size of the {@link EventData} exceeds a pre-defined limit set by the service. Default is 256k bytes. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final void sendSync(final EventData eventData, final String partitionKey) throws ServiceBusException { try { this.send(eventData, partitionKey).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } } /** * Send an '{@link EventData} with a partitionKey' to EventHub. All {@link EventData}'s with a partitionKey are guaranteed to land on the same partition. * This send pattern emphasize data correlation over general availability and latency. *

* There are 3 ways to send to EventHubs, each exposed as a method (along with its sendBatch overload): *

     * i.   {@link #send(EventData)} or {@link #send(Iterable)}
     * ii.  {@link #send(EventData, String)} or {@link #send(Iterable, String)}
     * iii. {@link PartitionSender#send(EventData)} or {@link PartitionSender#send(Iterable)}
     * 
*

* Use this type of Send, if: *

     * i.  There is a need for correlation of events based on Sender instance; The sender can generate a UniqueId and set it as partitionKey - which on the received Message can be used for correlation
     * ii. The client wants to take control of distribution of data across partitions.
     * 
*

* Multiple PartitionKey's could be mapped to one Partition. EventHubs service uses a proprietary Hash algorithm to map the PartitionKey to a PartitionId. * Using this type of Send (Sending using a specific partitionKey), could sometimes result in partitions which are not evenly distributed. * * @param eventData the {@link EventData} to be sent. * @param partitionKey the partitionKey will be hash'ed to determine the partitionId to send the eventData to. On the Received message this can be accessed at {@link EventData.SystemProperties#getPartitionKey()} * @return a CompletableFuture that can be completed when the send operations is done.. * @see #send(EventData) * @see PartitionSender#send(EventData) */ @Override public final 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 EventHubClient.this.sender.send(eventData.toAmqpMessage(partitionKey)); } }); } /** * Synchronous version of {@link #send(Iterable, String)}. * * @param eventDatas the batch of events to send to EventHub * @param partitionKey the partitionKey will be hash'ed to determine the partitionId to send the eventData to. On the Received message this can be accessed at {@link EventData.SystemProperties#getPartitionKey()} * @throws PayloadSizeExceededException if the total size of the {@link EventData} exceeds a pre-defined limit set by the service. Default is 256k bytes. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @throws UnresolvedAddressException if there are Client to Service network connectivity issues, if the Azure DNS resolution of the ServiceBus Namespace fails (ex: namespace deleted etc.) */ @Override public final void sendSync(final Iterable eventDatas, final String partitionKey) throws ServiceBusException { try { this.send(eventDatas, partitionKey).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } } /** * Send a 'batch of {@link EventData} with the same partitionKey' to EventHub. All {@link EventData}'s with a partitionKey are guaranteed to land on the same partition. * Multiple PartitionKey's will be mapped to one Partition. *

There are 3 ways to send to EventHubs, to understand this particular type of Send refer to the overload {@link #send(EventData, String)}, which is the same type of Send and is used to send single {@link EventData}. *

Sending a batch of {@link EventData}'s is useful in the following cases: *

     * i.	Efficient send - sending a batch of {@link EventData} maximizes the overall throughput by optimally using the number of sessions created to EventHubs service.
     * ii.	Send multiple events in One Transaction. This is the reason why all events sent in a batch needs to have same partitionKey (so that they are sent to one partition only).
     * 
* * @param eventDatas the batch of events to send to EventHub * @param partitionKey the partitionKey will be hash'ed to determine the partitionId to send the eventData to. On the Received message this can be accessed at {@link EventData.SystemProperties#getPartitionKey()} * @return a CompletableFuture that can be completed when the send operations is done.. * @see #send(EventData) * @see PartitionSender#send(EventData) */ @Override public final 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: {0}", ClientConstants.MAX_PARTITION_KEY_LENGTH)); } return this.createInternalSender().thenCompose(new Function>() { @Override public CompletableFuture apply(Void voidArg) { return EventHubClient.this.sender.send(EventDataUtil.toAmqpMessages(eventDatas, partitionKey)); } }); } /** * Synchronous version of {@link #createPartitionSender(String)}. * * @param partitionId partitionId of EventHub to send the {@link EventData}'s to * @return PartitionSender which can be used to send events to a specific partition. * @throws ServiceBusException if Service Bus service encountered problems during connection creation. */ @Override public final PartitionSender createPartitionSenderSync(final String partitionId) throws ServiceBusException, IllegalArgumentException { try { return this.createPartitionSender(partitionId).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create a {@link PartitionSender} which can publish {@link EventData}'s directly to a specific EventHub partition (sender type iii. in the below list). *

* There are 3 patterns/ways to send to EventHubs: *

     * i.   {@link #send(EventData)} or {@link #send(Iterable)}
     * ii.  {@link #send(EventData, String)} or {@link #send(Iterable, String)}
     * iii. {@link PartitionSender#send(EventData)} or {@link PartitionSender#send(Iterable)}
     * 
* * @param partitionId partitionId of EventHub to send the {@link EventData}'s to * @return a CompletableFuture that would result in a PartitionSender when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during connection creation. * @see PartitionSender */ @Override public final CompletableFuture createPartitionSender(final String partitionId) throws ServiceBusException { return PartitionSender.Create(this.underlyingFactory, this.eventHubName, partitionId); } /** * Synchronous version of {@link #createReceiver(String, String, String)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset) throws ServiceBusException { try { return this.createReceiver(consumerGroupName, partitionId, startingOffset).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * The receiver is created for a specific EventHub partition from the specific consumer group. *

NOTE: There can be a maximum number of receivers that can run in parallel per ConsumerGroup per Partition. * The limit is enforced by the Event Hub service - current limit is 5 receivers in parallel. Having multiple receivers * reading from offsets that are far apart on the same consumer group / partition combo will have significant performance Impact. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @return a CompletableFuture that would result in a PartitionReceiver instance when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver */ @Override public final CompletableFuture createReceiver(final String consumerGroupName, final String partitionId, final String startingOffset) throws ServiceBusException { return this.createReceiver(consumerGroupName, partitionId, startingOffset, false); } /** * Synchronous version of {@link #createReceiver(String, String, String, boolean)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive) throws ServiceBusException { try { return this.createReceiver(consumerGroupName, partitionId, startingOffset, offsetInclusive).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create the EventHub receiver with given partition id and start receiving from the specified starting offset. * The receiver is created for a specific EventHub Partition from the specific consumer group. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @return a CompletableFuture that would result in a PartitionReceiver instance when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver */ @Override public final CompletableFuture createReceiver(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive) throws ServiceBusException { return this.createReceiver(consumerGroupName, partitionId, startingOffset, offsetInclusive, null); } /** * Synchronous version of {@link #createReceiver(String, String, Instant)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createReceiverSync(final String consumerGroupName, final String partitionId, final Instant dateTime) throws ServiceBusException { try { return this.createReceiver(consumerGroupName, partitionId, dateTime).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create the EventHub receiver with given partition id and start receiving from the specified starting offset. * The receiver is created for a specific EventHub Partition from the specific consumer group. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver */ @Override public final CompletableFuture createReceiver(final String consumerGroupName, final String partitionId, final Instant dateTime) throws ServiceBusException { return this.createReceiver(consumerGroupName, partitionId, dateTime, null); } /** * Synchronous version of {@link #createReceiver(String, String, String)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param receiverOptions the set of options to enable on the event hubs receiver * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset, final ReceiverOptions receiverOptions) throws ServiceBusException { try { return this.createReceiver(consumerGroupName, partitionId, startingOffset, receiverOptions).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * The receiver is created for a specific EventHub partition from the specific consumer group. *

NOTE: There can be a maximum number of receivers that can run in parallel per ConsumerGroup per Partition. * The limit is enforced by the Event Hub service - current limit is 5 receivers in parallel. Having multiple receivers * reading from offsets that are far apart on the same consumer group / partition combo will have significant performance Impact. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param receiverOptions the set of options to enable on the event hubs receiver * @return a CompletableFuture that would result in a PartitionReceiver instance when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver */ @Override public final CompletableFuture createReceiver(final String consumerGroupName, final String partitionId, final String startingOffset, final ReceiverOptions receiverOptions) throws ServiceBusException { return this.createReceiver(consumerGroupName, partitionId, startingOffset, false, receiverOptions); } /** * Synchronous version of {@link #createReceiver(String, String, String, boolean)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @param receiverOptions the set of options to enable on the event hubs receiver * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive, final ReceiverOptions receiverOptions) throws ServiceBusException { try { return this.createReceiver(consumerGroupName, partitionId, startingOffset, offsetInclusive, receiverOptions).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create the EventHub receiver with given partition id and start receiving from the specified starting offset. * The receiver is created for a specific EventHub Partition from the specific consumer group. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @param receiverOptions the set of options to enable on the event hubs receiver * @return a CompletableFuture that would result in a PartitionReceiver instance when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver */ @Override public final CompletableFuture createReceiver(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive, final ReceiverOptions receiverOptions) throws ServiceBusException { return PartitionReceiver.create(this.underlyingFactory, this.eventHubName, consumerGroupName, partitionId, startingOffset, offsetInclusive, null, PartitionReceiver.NULL_EPOCH, false, receiverOptions); } /** * Synchronous version of {@link #createReceiver(String, String, Instant)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @param receiverOptions the set of options to enable on the event hubs receiver * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createReceiverSync(final String consumerGroupName, final String partitionId, final Instant dateTime, final ReceiverOptions receiverOptions) throws ServiceBusException { try { return this.createReceiver(consumerGroupName, partitionId, dateTime, receiverOptions).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create the EventHub receiver with given partition id and start receiving from the specified starting offset. * The receiver is created for a specific EventHub Partition from the specific consumer group. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @param receiverOptions the set of options to enable on the event hubs receiver * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver */ @Override public final CompletableFuture createReceiver(final String consumerGroupName, final String partitionId, final Instant dateTime, final ReceiverOptions receiverOptions) throws ServiceBusException { return PartitionReceiver.create(this.underlyingFactory, this.eventHubName, consumerGroupName, partitionId, null, false, dateTime, PartitionReceiver.NULL_EPOCH, false, receiverOptions); } /** * Synchronous version of {@link #createEpochReceiver(String, String, String, long)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createEpochReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset, final long epoch) throws ServiceBusException { try { return this.createEpochReceiver(consumerGroupName, partitionId, startingOffset, epoch).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create a Epoch based EventHub receiver with given partition id and start receiving from the beginning of the partition stream. * The receiver is created for a specific EventHub Partition from the specific consumer group. *

* It is important to pay attention to the following when creating epoch based receiver: *

    *
  • Ownership enforcement - Once you created an epoch based receiver, you cannot create a non-epoch receiver to the same consumerGroup-Partition combo until all receivers to the combo are closed. *
  • Ownership stealing - If a receiver with higher epoch value is created for a consumerGroup-Partition combo, any older epoch receiver to that combo will be force closed. *
  • Any receiver closed due to lost of ownership to a consumerGroup-Partition combo will get ReceiverDisconnectedException for all operations from that receiver. *
* * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver * @see ReceiverDisconnectedException */ @Override public final CompletableFuture createEpochReceiver(final String consumerGroupName, final String partitionId, final String startingOffset, final long epoch) throws ServiceBusException { return this.createEpochReceiver(consumerGroupName, partitionId, startingOffset, false, epoch); } /** * Synchronous version of {@link #createEpochReceiver(String, String, String, boolean, long)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createEpochReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive, final long epoch) throws ServiceBusException { try { return this.createEpochReceiver(consumerGroupName, partitionId, startingOffset, offsetInclusive, epoch).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create a Epoch based EventHub receiver with given partition id and start receiving from the beginning of the partition stream. * The receiver is created for a specific EventHub Partition from the specific consumer group. *

* It is important to pay attention to the following when creating epoch based receiver: *

    *
  • Ownership enforcement - Once you created an epoch based receiver, you cannot create a non-epoch receiver to the same consumerGroup-Partition combo until all receivers to the combo are closed. *
  • Ownership stealing - If a receiver with higher epoch value is created for a consumerGroup-Partition combo, any older epoch receiver to that combo will be force closed. *
  • Any receiver closed due to lost of ownership to a consumerGroup-Partition combo will get ReceiverDisconnectedException for all operations from that receiver. *
* * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver * @see ReceiverDisconnectedException */ @Override public final CompletableFuture createEpochReceiver(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive, final long epoch) throws ServiceBusException { return this.createEpochReceiver(consumerGroupName, partitionId, startingOffset, offsetInclusive, epoch, null); } /** * Synchronous version of {@link #createEpochReceiver(String, String, Instant, long)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createEpochReceiverSync(final String consumerGroupName, final String partitionId, final Instant dateTime, final long epoch) throws ServiceBusException { try { return this.createEpochReceiver(consumerGroupName, partitionId, dateTime, epoch).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create a Epoch based EventHub receiver with given partition id and start receiving from the beginning of the partition stream. * The receiver is created for a specific EventHub Partition from the specific consumer group. *

* It is important to pay attention to the following when creating epoch based receiver: *

    *
  • Ownership enforcement - Once you created an epoch based receiver, you cannot create a non-epoch receiver to the same consumerGroup-Partition combo until all receivers to the combo are closed. *
  • Ownership stealing - If a receiver with higher epoch value is created for a consumerGroup-Partition combo, any older epoch receiver to that combo will be force closed. *
  • Any receiver closed due to lost of ownership to a consumerGroup-Partition combo will get ReceiverDisconnectedException for all operations from that receiver. *
* * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @param epoch a unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver * @see ReceiverDisconnectedException */ @Override public final CompletableFuture createEpochReceiver(final String consumerGroupName, final String partitionId, final Instant dateTime, final long epoch) throws ServiceBusException { return this.createEpochReceiver(consumerGroupName, partitionId, dateTime, epoch, null); } /** * Synchronous version of {@link #createEpochReceiver(String, String, String, long)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @param receiverOptions the set of options to enable on the event hubs receiver * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createEpochReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset, final long epoch, final ReceiverOptions receiverOptions) throws ServiceBusException { try { return this.createEpochReceiver(consumerGroupName, partitionId, startingOffset, epoch, receiverOptions).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create a Epoch based EventHub receiver with given partition id and start receiving from the beginning of the partition stream. * The receiver is created for a specific EventHub Partition from the specific consumer group. *

* It is important to pay attention to the following when creating epoch based receiver: *

    *
  • Ownership enforcement - Once you created an epoch based receiver, you cannot create a non-epoch receiver to the same consumerGroup-Partition combo until all receivers to the combo are closed. *
  • Ownership stealing - If a receiver with higher epoch value is created for a consumerGroup-Partition combo, any older epoch receiver to that combo will be force closed. *
  • Any receiver closed due to lost of ownership to a consumerGroup-Partition combo will get ReceiverDisconnectedException for all operations from that receiver. *
* * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @param receiverOptions the set of options to enable on the event hubs receiver * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver * @see ReceiverDisconnectedException */ @Override public final CompletableFuture createEpochReceiver(final String consumerGroupName, final String partitionId, final String startingOffset, final long epoch, final ReceiverOptions receiverOptions) throws ServiceBusException { return this.createEpochReceiver(consumerGroupName, partitionId, startingOffset, false, epoch, receiverOptions); } /** * Synchronous version of {@link #createEpochReceiver(String, String, String, boolean, long)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @param receiverOptions the set of options to enable on the event hubs receiver * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createEpochReceiverSync(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive, final long epoch, final ReceiverOptions receiverOptions) throws ServiceBusException { try { return this.createEpochReceiver(consumerGroupName, partitionId, startingOffset, offsetInclusive, epoch, receiverOptions).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create a Epoch based EventHub receiver with given partition id and start receiving from the beginning of the partition stream. * The receiver is created for a specific EventHub Partition from the specific consumer group. *

* It is important to pay attention to the following when creating epoch based receiver: *

    *
  • Ownership enforcement - Once you created an epoch based receiver, you cannot create a non-epoch receiver to the same consumerGroup-Partition combo until all receivers to the combo are closed. *
  • Ownership stealing - If a receiver with higher epoch value is created for a consumerGroup-Partition combo, any older epoch receiver to that combo will be force closed. *
  • Any receiver closed due to lost of ownership to a consumerGroup-Partition combo will get ReceiverDisconnectedException for all operations from that receiver. *
* * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param startingOffset the offset to start receiving the events from. To receive from start of the stream use: {@link PartitionReceiver#START_OF_STREAM} * @param offsetInclusive if set to true, the startingOffset is treated as an inclusive offset - meaning the first event returned is the one that has the starting offset. Normally first event returned is the event after the starting offset. * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @param receiverOptions the set of options to enable on the event hubs receiver * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver * @see ReceiverDisconnectedException */ @Override public final CompletableFuture createEpochReceiver(final String consumerGroupName, final String partitionId, final String startingOffset, boolean offsetInclusive, final long epoch, final ReceiverOptions receiverOptions) throws ServiceBusException { return PartitionReceiver.create(this.underlyingFactory, this.eventHubName, consumerGroupName, partitionId, startingOffset, offsetInclusive, null, epoch, true, receiverOptions); } /** * Synchronous version of {@link #createEpochReceiver(String, String, Instant, long)}. * * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @param epoch an unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @param receiverOptions the set of options to enable on the event hubs receiver * @return PartitionReceiver instance which can be used for receiving {@link EventData}. * @throws ServiceBusException if Service Bus service encountered problems during the operation. */ @Override public final PartitionReceiver createEpochReceiverSync(final String consumerGroupName, final String partitionId, final Instant dateTime, final long epoch, final ReceiverOptions receiverOptions) throws ServiceBusException { try { return this.createEpochReceiver(consumerGroupName, partitionId, dateTime, epoch, receiverOptions).get(); } catch (InterruptedException | ExecutionException exception) { if (exception instanceof InterruptedException) { // Re-assert the thread's interrupted status Thread.currentThread().interrupt(); } Throwable throwable = exception.getCause(); if (throwable != null) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } if (throwable instanceof ServiceBusException) { throw (ServiceBusException) throwable; } throw new ServiceBusException(true, throwable); } } return null; } /** * Create a Epoch based EventHub receiver with given partition id and start receiving from the beginning of the partition stream. * The receiver is created for a specific EventHub Partition from the specific consumer group. *

* It is important to pay attention to the following when creating epoch based receiver: *

    *
  • Ownership enforcement - Once you created an epoch based receiver, you cannot create a non-epoch receiver to the same consumerGroup-Partition combo until all receivers to the combo are closed. *
  • Ownership stealing - If a receiver with higher epoch value is created for a consumerGroup-Partition combo, any older epoch receiver to that combo will be force closed. *
  • Any receiver closed due to lost of ownership to a consumerGroup-Partition combo will get ReceiverDisconnectedException for all operations from that receiver. *
* * @param consumerGroupName the consumer group name that this receiver should be grouped under. * @param partitionId the partition Id that the receiver belongs to. All data received will be from this partition only. * @param dateTime the date time instant that receive operations will start receive events from. Events received will have {@link EventData.SystemProperties#getEnqueuedTime()} later than this Instant. * @param epoch a unique identifier (epoch value) that the service uses, to enforce partition/lease ownership. * @param receiverOptions the set of options to enable on the event hubs receiver * @return a CompletableFuture that would result in a PartitionReceiver when it is completed. * @throws ServiceBusException if Service Bus service encountered problems during the operation. * @see PartitionReceiver * @see ReceiverDisconnectedException */ @Override public final CompletableFuture createEpochReceiver(final String consumerGroupName, final String partitionId, final Instant dateTime, final long epoch, final ReceiverOptions receiverOptions) throws ServiceBusException { return PartitionReceiver.create(this.underlyingFactory, this.eventHubName, consumerGroupName, partitionId, null, false, dateTime, epoch, true, receiverOptions); } @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 EventHubClient.this.underlyingFactory.close(); } }) : this.underlyingFactory.close(); return internalSenderClose; } } return CompletableFuture.completedFuture(null); } private CompletableFuture createInternalSender() { if (!this.isSenderCreateStarted) { synchronized (this.senderCreateSync) { if (!this.isSenderCreateStarted) { this.createSender = MessageSender.create(this.underlyingFactory, StringUtil.getRandomString(), this.eventHubName) .thenAccept(new Consumer() { public void accept(MessageSender a) { EventHubClient.this.sender = a; } }); this.isSenderCreateStarted = true; } } } return this.createSender; } /** * Retrieves general information about an event hub (see {@link EventHubRuntimeInformation} for details). * Retries until it reaches the operation timeout, then either rethrows the last error if available or * returns null to indicate timeout. * * @return CompletableFuture which returns an EventHubRuntimeInformation on success, or null on timeout. */ @Override public CompletableFuture getRuntimeInformation() { CompletableFuture future1 = null; 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).thenCompose(new Function, CompletableFuture>() { @Override public CompletableFuture apply(Map rawdata) { CompletableFuture future2 = new CompletableFuture(); future2.complete(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; } }); } return future1; } /** * Retrieves dynamic information about a partition of an event hub (see {@link EventHubPartitionRuntimeInformation} for * details. Retries until it reaches the operation timeout, then either rethrows the last error if available or * returns null to indicate timeout. * * @param partitionId Partition to get information about. Must be one of the partition ids returned by getRuntimeInformation. * @return CompletableFuture which returns an EventHubPartitionRuntimeInformation on success, or null on timeout. */ @Override public CompletableFuture getPartitionRuntimeInformation(String partitionId) { CompletableFuture future1 = null; 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); future1 = this.addManagementToken(request); if (future1 == null) { future1 = managementWithRetry(request).thenCompose(new Function, CompletableFuture>() { @Override public CompletableFuture apply(Map rawdata) { CompletableFuture future2 = new CompletableFuture(); future2.complete(new EventHubPartitionRuntimeInformation( (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())); return future2; } }); } return future1; } private CompletableFuture addManagementToken(Map request) { CompletableFuture retval = null; try { String audience = String.format("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> managementWithRetry(Map request) { Instant endTime = Instant.now().plus(this.underlyingFactory.getOperationTimeout()); CompletableFuture> rawdataFuture = new CompletableFuture>(); ManagementRetry retrier = new ManagementRetry(rawdataFuture, endTime, this.underlyingFactory, request); Timer.schedule(retrier, Duration.ZERO, TimerType.OneTimeRun); return rawdataFuture; } private class ManagementRetry implements Runnable { private final CompletableFuture> finalFuture; private final Instant endTime; private final MessagingFactory mf; private final Map request; public ManagementRetry(CompletableFuture> future, Instant endTime, MessagingFactory mf, Map request) { this.finalFuture = future; this.endTime = endTime; this.mf = mf; this.request = request; } @Override public void run() { CompletableFuture> intermediateFuture = this.mf.getManagementChannel().request(this.mf.getReactorScheduler(), request); intermediateFuture.whenComplete(new BiConsumer, Throwable>() { @Override public void accept(Map result, Throwable error) { if ((result != null) && (error == null)) { // Success! ManagementRetry.this.finalFuture.complete(result); } else { Duration remainingTime = Duration.between(Instant.now(), ManagementRetry.this.endTime); Exception lastException = null; Throwable completeWith = error; if (error == null) { // Timeout, so fake up an exception to keep getNextRetryInternal happy. // It has to be a ServiceBusException that is set to retryable or getNextRetryInterval will halt the retries. lastException = new ServiceBusException(true, "timed out"); completeWith = null; } else if (error instanceof Exception) { if ((error instanceof ExecutionException) && (error.getCause() != null) && (error.getCause() instanceof Exception)) { lastException = (Exception)error.getCause(); completeWith = error.getCause(); } else { lastException = (Exception)error; } } else { lastException = new Exception("got a throwable: " + error.toString()); } Duration waitTime = ManagementRetry.this.mf.getRetryPolicy().getNextRetryInterval(ManagementRetry.this.mf.getClientId(), lastException, remainingTime); if (waitTime == null) { // Do not retry again, give up and report error. if (completeWith == null) { ManagementRetry.this.finalFuture.complete(null); } else { ManagementRetry.this.finalFuture.completeExceptionally(completeWith); } } else { // The only thing needed here is to schedule a new attempt. Even if the RequestResponseChannel has croaked, // ManagementChannel uses FaultTolerantObject, so the underlying RequestResponseChannel will be recreated // the next time it is needed. ManagementRetry retrier = new ManagementRetry(ManagementRetry.this.finalFuture, ManagementRetry.this.endTime, ManagementRetry.this.mf, ManagementRetry.this.request); Timer.schedule(retrier, waitTime, TimerType.OneTimeRun); } } } }); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy