com.azure.messaging.eventhubs.implementation.EventHubReactorSession Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of azure-messaging-eventhubs Show documentation
Show all versions of azure-messaging-eventhubs Show documentation
Libraries built on Microsoft Azure Event Hubs
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.messaging.eventhubs.implementation;
import com.azure.core.amqp.AmqpConnection;
import com.azure.core.amqp.AmqpRetryOptions;
import com.azure.core.amqp.AmqpRetryPolicy;
import com.azure.core.amqp.ClaimsBasedSecurityNode;
import com.azure.core.amqp.implementation.AmqpConstants;
import com.azure.core.amqp.implementation.AmqpLinkProvider;
import com.azure.core.amqp.implementation.AmqpReceiveLink;
import com.azure.core.amqp.implementation.AmqpSendLink;
import com.azure.core.amqp.implementation.ConsumerFactory;
import com.azure.core.amqp.implementation.MessageSerializer;
import com.azure.core.amqp.implementation.ProtonSessionWrapper;
import com.azure.core.amqp.implementation.ReactorHandlerProvider;
import com.azure.core.amqp.implementation.ReactorSession;
import com.azure.core.amqp.implementation.TokenManager;
import com.azure.core.amqp.implementation.TokenManagerProvider;
import com.azure.core.amqp.implementation.handler.DeliverySettleMode;
import com.azure.core.util.logging.ClientLogger;
import com.azure.messaging.eventhubs.models.EventPosition;
import com.azure.messaging.eventhubs.models.ReceiveOptions;
import org.apache.qpid.proton.amqp.Symbol;
import org.apache.qpid.proton.amqp.UnknownDescribedType;
import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode;
import org.apache.qpid.proton.amqp.transport.SenderSettleMode;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import static com.azure.core.amqp.AmqpMessageConstant.ENQUEUED_TIME_UTC_ANNOTATION_NAME;
import static com.azure.core.amqp.AmqpMessageConstant.OFFSET_ANNOTATION_NAME;
import static com.azure.core.amqp.AmqpMessageConstant.SEQUENCE_NUMBER_ANNOTATION_NAME;
import static com.azure.core.amqp.implementation.AmqpConstants.CLIENT_IDENTIFIER;
import static com.azure.core.amqp.implementation.AmqpConstants.CLIENT_RECEIVER_IDENTIFIER;
import static com.azure.core.amqp.implementation.AmqpConstants.VENDOR;
/**
* An AMQP session for Event Hubs.
*/
class EventHubReactorSession extends ReactorSession implements EventHubSession {
private static final Symbol EPOCH = Symbol.valueOf(VENDOR + ":epoch");
private static final Symbol ENABLE_RECEIVER_RUNTIME_METRIC_NAME =
Symbol.valueOf(VENDOR + ":enable-receiver-runtime-metric");
private static final ClientLogger LOGGER = new ClientLogger(EventHubReactorSession.class);
private final boolean isV2;
/**
* Creates a new AMQP session using proton-j.
*
* @param session Proton-j session for this AMQP session.
* @param handlerProvider Providers reactor handlers for listening to proton-j reactor events.
* @param linkProvider Provides amqp links for send and receive.
* @param cbsNodeSupplier Mono that returns a reference to the {@link ClaimsBasedSecurityNode}.
* @param tokenManagerProvider Provides {@link TokenManager} that authorizes the client when performing
* operations on the message broker.
* @param retryOptions to be used for this session.
* @param messageSerializer to be used.
*/
EventHubReactorSession(AmqpConnection amqpConnection, ProtonSessionWrapper session,
ReactorHandlerProvider handlerProvider, AmqpLinkProvider linkProvider,
Mono cbsNodeSupplier, TokenManagerProvider tokenManagerProvider,
AmqpRetryOptions retryOptions, MessageSerializer messageSerializer, boolean isV2) {
super(amqpConnection, session, handlerProvider, linkProvider, cbsNodeSupplier, tokenManagerProvider,
messageSerializer, retryOptions);
this.isV2 = isV2;
}
@Override
public Mono createProducer(String linkName, String entityPath, Duration timeout,
AmqpRetryPolicy retryPolicy, String clientIdentifier) {
Objects.requireNonNull(linkName, "'linkName' cannot be null.");
Objects.requireNonNull(entityPath, "'entityPath' cannot be null.");
Objects.requireNonNull(timeout, "'timeout' cannot be null.");
Objects.requireNonNull(clientIdentifier, "'clientIdentifier' cannot be null.");
final Map properties = new HashMap<>();
properties.put(CLIENT_IDENTIFIER, clientIdentifier);
return createProducer(linkName, entityPath, timeout, retryPolicy, properties).cast(AmqpSendLink.class);
}
/**
* {@inheritDoc}
*/
@Override
public Mono createConsumer(String linkName, String entityPath, Duration timeout,
AmqpRetryPolicy retry, EventPosition eventPosition, ReceiveOptions options, String clientIdentifier) {
Objects.requireNonNull(linkName, "'linkName' cannot be null.");
Objects.requireNonNull(entityPath, "'entityPath' cannot be null.");
Objects.requireNonNull(timeout, "'timeout' cannot be null.");
Objects.requireNonNull(retry, "'retry' cannot be null.");
Objects.requireNonNull(eventPosition, "'eventPosition' cannot be null.");
Objects.requireNonNull(options, "'options' cannot be null.");
Objects.requireNonNull(clientIdentifier, "'clientIdentifier' cannot be null.");
final String eventPositionExpression = getExpression(eventPosition);
final Map filter = new HashMap<>();
filter.put(AmqpConstants.STRING_FILTER, new UnknownDescribedType(AmqpConstants.STRING_FILTER,
eventPositionExpression));
final Map properties = new HashMap<>();
if (options.getOwnerLevel() != null) {
properties.put(EPOCH, options.getOwnerLevel());
}
properties.put(CLIENT_RECEIVER_IDENTIFIER, clientIdentifier);
final Symbol[] desiredCapabilities = options.getTrackLastEnqueuedEventProperties()
? new Symbol[]{ENABLE_RECEIVER_RUNTIME_METRIC_NAME}
: null;
final ConsumerFactory consumerFactory;
if (isV2) {
consumerFactory = new ConsumerFactory(DeliverySettleMode.ACCEPT_AND_SETTLE_ON_DELIVERY, false);
} else {
consumerFactory = new ConsumerFactory();
}
// Use explicit settlement via dispositions (not pre-settled)
return createConsumer(linkName, entityPath, timeout, retry, filter, properties, desiredCapabilities,
SenderSettleMode.UNSETTLED, ReceiverSettleMode.SECOND, consumerFactory);
}
private String getExpression(EventPosition eventPosition) {
final String isInclusiveFlag = eventPosition.isInclusive() ? "=" : "";
// order of preference
if (eventPosition.getOffset() != null) {
return String.format(
AmqpConstants.AMQP_ANNOTATION_FORMAT, OFFSET_ANNOTATION_NAME.getValue(),
isInclusiveFlag,
eventPosition.getOffset());
}
if (eventPosition.getSequenceNumber() != null) {
return String.format(
AmqpConstants.AMQP_ANNOTATION_FORMAT,
SEQUENCE_NUMBER_ANNOTATION_NAME.getValue(),
isInclusiveFlag,
eventPosition.getSequenceNumber());
}
if (eventPosition.getEnqueuedDateTime() != null) {
String ms;
try {
ms = Long.toString(eventPosition.getEnqueuedDateTime().toEpochMilli());
} catch (ArithmeticException ex) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(String.format(Locale.ROOT,
"Event position for enqueued DateTime could not be parsed. Value: '%s'",
eventPosition.getEnqueuedDateTime()), ex));
}
return String.format(AmqpConstants.AMQP_ANNOTATION_FORMAT,
ENQUEUED_TIME_UTC_ANNOTATION_NAME.getValue(), isInclusiveFlag, ms);
}
throw LOGGER.logExceptionAsError(new IllegalArgumentException("No starting position was set."));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy