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

com.azure.messaging.eventhubs.EventData Maven / Gradle / Ivy

There is a newer version: 5.19.2
Show newest version
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.messaging.eventhubs;

import com.azure.core.amqp.models.AmqpAnnotatedMessage;
import com.azure.core.amqp.models.AmqpMessageBody;
import com.azure.core.amqp.models.AmqpMessageHeader;
import com.azure.core.amqp.models.AmqpMessageId;
import com.azure.core.amqp.models.AmqpMessageProperties;
import com.azure.core.models.MessageContent;
import com.azure.core.util.BinaryData;
import com.azure.core.util.Context;
import com.azure.core.util.FluxUtil;
import com.azure.core.util.logging.ClientLogger;

import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

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.PARTITION_KEY_ANNOTATION_NAME;
import static com.azure.core.amqp.AmqpMessageConstant.PUBLISHER_ANNOTATION_NAME;
import static com.azure.core.amqp.AmqpMessageConstant.SEQUENCE_NUMBER_ANNOTATION_NAME;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * 

The data structure encapsulating the event being sent-to and received-from Event Hubs. Each Event Hub partition * can be visualized as a stream of {@link EventData}. This class is not thread-safe.

* * @see EventDataBatch * @see EventHubProducerClient * @see EventHubProducerAsyncClient * * @see AMQP 1.0 specification */ public class EventData extends MessageContent { /* * These are properties owned by the service and set when a message is received. */ static final Set RESERVED_SYSTEM_PROPERTIES; private static final ClientLogger LOGGER = new ClientLogger(EventData.class); private final Map properties; private final SystemProperties systemProperties; private AmqpAnnotatedMessage annotatedMessage; private Context context; static { final Set properties = new HashSet<>(); properties.add(OFFSET_ANNOTATION_NAME.getValue()); properties.add(PARTITION_KEY_ANNOTATION_NAME.getValue()); properties.add(SEQUENCE_NUMBER_ANNOTATION_NAME.getValue()); properties.add(ENQUEUED_TIME_UTC_ANNOTATION_NAME.getValue()); properties.add(PUBLISHER_ANNOTATION_NAME.getValue()); RESERVED_SYSTEM_PROPERTIES = Collections.unmodifiableSet(properties); } /** * Creates an event with an empty body. */ public EventData() { this.context = Context.NONE; this.annotatedMessage = new AmqpAnnotatedMessage(AmqpMessageBody.fromData(new byte[0])); this.properties = annotatedMessage.getApplicationProperties(); this.systemProperties = new SystemProperties(); } /** * Creates an event containing the {@code body}. * * @param body The data to set for this event. * * @throws NullPointerException if {@code body} is {@code null}. */ public EventData(byte[] body) { this.context = Context.NONE; final AmqpMessageBody messageBody = AmqpMessageBody.fromData( Objects.requireNonNull(body, "'body' cannot be null.")); this.annotatedMessage = new AmqpAnnotatedMessage(messageBody); this.properties = annotatedMessage.getApplicationProperties(); this.systemProperties = new SystemProperties(); } /** * Creates an event containing the {@code body}. * * @param body The data to set for this event. * * @throws NullPointerException if {@code body} is {@code null}. */ public EventData(ByteBuffer body) { // Extract the ByteBuffer as it isn't guaranteed that the ByteBuffer will be a HeapByteBuffer and using // .array() on a DirectByteBuffer or read-only ByteBuffer will throw an exception. Additionally, even if the // ByteBuffer was a HeapByteBuffer the entire backing array may not have been written. // // Duplicate the ByteBuffer so the original body won't have its read position mutated. this(FluxUtil.byteBufferToArray(Objects.requireNonNull(body, "'body' cannot be null.").duplicate())); } /** * Creates an event by encoding the {@code body} using UTF-8 charset. * * @param body The string that will be UTF-8 encoded to create an event. * * @throws NullPointerException if {@code body} is {@code null}. */ public EventData(String body) { this(Objects.requireNonNull(body, "'body' cannot be null.").getBytes(UTF_8)); } /** * Creates an event with the provided {@link BinaryData} as payload. * * @param body The {@link BinaryData} payload for this event. */ public EventData(BinaryData body) { this(Objects.requireNonNull(body, "'body' cannot be null.").toBytes()); } /** * Creates an event with the given {@code body}, system properties and context. Used in the case where a message * is received from the service. * * @param context A specified key-value pair of type {@link Context}. * @param amqpAnnotatedMessage Backing annotated message. * * @throws NullPointerException if {@code amqpAnnotatedMessage} or {@code context} is {@code null}. * @throws IllegalArgumentException if {@code amqpAnnotatedMessage}'s body type is unknown. */ EventData(AmqpAnnotatedMessage amqpAnnotatedMessage, SystemProperties systemProperties, Context context) { this.context = Objects.requireNonNull(context, "'context' cannot be null."); this.properties = Collections.unmodifiableMap(amqpAnnotatedMessage.getApplicationProperties()); this.annotatedMessage = Objects.requireNonNull(amqpAnnotatedMessage, "'amqpAnnotatedMessage' cannot be null."); this.systemProperties = systemProperties; switch (annotatedMessage.getBody().getBodyType()) { case DATA: break; case SEQUENCE: case VALUE: LOGGER.warning("Message body type '{}' is not supported in EH. " + " Getting contents of body may throw.", annotatedMessage.getBody().getBodyType()); break; default: throw LOGGER.logExceptionAsError(new IllegalArgumentException( "Body type not valid " + annotatedMessage.getBody().getBodyType())); } } /** * Gets the set of free-form event properties which may be used for passing metadata associated with the event with * the event body during Event Hubs operations. A common use-case for {@code properties()} is to associate * serialization hints for the {@link #getBody()} as an aid to consumers who wish to deserialize the binary data. * *

Adding serialization hint using {@code getProperties()}

*

In the sample, the type of telemetry is indicated by adding an application property with key "eventType".

* * *
     * TelemetryEvent telemetry = new TelemetryEvent("temperature", "37");
     * byte[] serializedTelemetryData = telemetry.toString().getBytes(UTF_8);
     *
     * EventData eventData = new EventData(serializedTelemetryData);
     * eventData.getProperties().put("eventType", TelemetryEvent.class.getName());
     * 
* * *

* The following types are supported: *

    *
  • {@link Character}
  • *
  • {@link java.util.Date}
  • *
  • {@link Double}
  • *
  • {@link Float}
  • *
  • {@link Integer}
  • *
  • {@link Long}
  • *
  • {@link Short}
  • *
  • {@link String}
  • *
* * @return Application properties associated with this {@link EventData}. For received {@link EventData}, the map is * a read-only view. */ public Map getProperties() { return properties; } /** * Properties that are populated by Event Hubs service. As these are populated by the Event Hubs service, they are * only present on a received {@link EventData}. Provides an abstraction on top of properties exposed by * {@link #getRawAmqpMessage()}. These properties are read-only and can be modified via * {@link #getRawAmqpMessage()}. * * @return An encapsulation of all system properties appended by EventHubs service into {@link EventData}. If the * {@link EventData} is not received from the Event Hubs service, the values returned are {@code null}. */ public Map getSystemProperties() { return systemProperties; } /** * Gets the actual payload/data wrapped by EventData. * *

* If the means for deserializing the raw data is not apparent to consumers, a common technique is to make use of * {@link #getProperties()} when creating the event, to associate serialization hints as an aid to consumers who * wish to deserialize the binary data. *

* * @return A byte array representing the data. */ public byte[] getBody() { return annotatedMessage.getBody().getFirstData(); } /** * Returns event data as UTF-8 decoded string. * * @return UTF-8 decoded string representation of the event data. */ public String getBodyAsString() { return new String(annotatedMessage.getBody().getFirstData(), UTF_8); } /** * Returns the {@link BinaryData} payload associated with this event. * * @return the {@link BinaryData} payload associated with this event. */ @Override public BinaryData getBodyAsBinaryData() { return BinaryData.fromBytes(annotatedMessage.getBody().getFirstData()); } /** * Sets a new binary body and corresponding {@link AmqpAnnotatedMessage} on the event. Contents from * {@link #getRawAmqpMessage()} are shallow copied to the new underlying message. */ @Override public EventData setBodyAsBinaryData(BinaryData binaryData) { final AmqpAnnotatedMessage current = this.annotatedMessage; this.annotatedMessage = new AmqpAnnotatedMessage(AmqpMessageBody.fromData(binaryData.toBytes())); if (current == null) { return this; } this.annotatedMessage.getApplicationProperties().putAll(current.getApplicationProperties()); this.annotatedMessage.getDeliveryAnnotations().putAll(current.getDeliveryAnnotations()); this.annotatedMessage.getFooter().putAll(current.getFooter()); this.annotatedMessage.getMessageAnnotations().putAll(current.getMessageAnnotations()); final AmqpMessageHeader header = this.annotatedMessage.getHeader(); header.setDeliveryCount(current.getHeader().getDeliveryCount()) .setDurable(current.getHeader().isDurable()) .setFirstAcquirer(current.getHeader().isFirstAcquirer()) .setPriority(current.getHeader().getPriority()) .setTimeToLive(current.getHeader().getTimeToLive()); final AmqpMessageProperties props = this.annotatedMessage.getProperties(); props.setAbsoluteExpiryTime(current.getProperties().getAbsoluteExpiryTime()) .setContentEncoding(current.getProperties().getContentEncoding()) .setContentType(current.getProperties().getContentType()) .setCorrelationId(current.getProperties().getCorrelationId()) .setCreationTime(current.getProperties().getCreationTime()) .setGroupId(current.getProperties().getGroupId()) .setGroupSequence(current.getProperties().getGroupSequence()) .setMessageId(current.getProperties().getMessageId()) .setReplyTo(current.getProperties().getReplyTo()) .setReplyToGroupId(current.getProperties().getReplyToGroupId()) .setSubject(current.getProperties().getSubject()) .setTo(current.getProperties().getTo()) .setUserId(current.getProperties().getUserId()); return this; } /** * Gets the offset of the event when it was received from the associated Event Hub partition. This is only present * on a received {@link EventData}. * * @return The offset within the Event Hub partition of the received event. {@code null} if the {@link EventData} * was not received from Event Hubs service. */ public Long getOffset() { return systemProperties.getOffset(); } /** * Gets the partition hashing key if it was set when originally publishing the event. If it exists, this value was * used to compute a hash to select a partition to send the message to. This is only present on a received * {@link EventData}. * * @return A partition key for this Event Data. {@code null} if the {@link EventData} was not received from Event * Hubs service or there was no partition key set when the event was sent to the Event Hub. */ public String getPartitionKey() { return systemProperties.getPartitionKey(); } /** * Gets the instant, in UTC, of when the event was enqueued in the Event Hub partition. This is only present on a * received {@link EventData}. * * @return The instant, in UTC, this was enqueued in the Event Hub partition. {@code null} if the {@link EventData} * was not received from Event Hubs service. */ public Instant getEnqueuedTime() { return systemProperties.getEnqueuedTime(); } /** * Gets the sequence number assigned to the event when it was enqueued in the associated Event Hub partition. This * is unique for every message received in the Event Hub partition. This is only present on a received {@link * EventData}. * * @return The sequence number for this event. {@code null} if the {@link EventData} was not received from Event * Hubs service. */ public Long getSequenceNumber() { return systemProperties.getSequenceNumber(); } /** * Gets the underlying AMQP message. * * @return The underlying AMQP message. */ public AmqpAnnotatedMessage getRawAmqpMessage() { return annotatedMessage; } /** * Gets the MIME type describing the data contained in the {@link #getBody()}, intended to allow consumers to make * informed decisions for inspecting and processing the event. * * @return The content type. */ public String getContentType() { return annotatedMessage.getProperties().getContentType(); } /** * Sets the MIME type describing the data contained in the {@link #getBody()}, intended to allow consumers to make * informed decisions for inspecting and processing the event. * * @param contentType The content type. * * @return The updated {@link EventData}. */ public EventData setContentType(String contentType) { annotatedMessage.getProperties().setContentType(contentType); return this; } /** * Gets an application-defined value that represents the context to use for correlation across one or more * operations. The identifier is a free-form value and may reflect a unique identity or a shared data element with * significance to the application. * * @return The correlation id. {@code null} if there is none set. */ public String getCorrelationId() { final AmqpMessageId messageId = annotatedMessage.getProperties().getCorrelationId(); return messageId != null ? messageId.toString() : null; } /** * Sets an application-defined value that represents the context to use for correlation across one or more * operations. The identifier is a free-form value and may reflect a unique identity or a shared data element with * significance to the application. * * @param correlationId The correlation id. * * @return The updated {@link EventData}. */ public EventData setCorrelationId(String correlationId) { final AmqpMessageId id = correlationId != null ? new AmqpMessageId(correlationId) : null; annotatedMessage.getProperties().setCorrelationId(id); return this; } /** * Gets an application-defined value that uniquely identifies the event. The identifier is a free-form value and * can reflect a GUID or an identifier derived from the application context. * * @return The message id. {@code null} if there is none set. */ public String getMessageId() { final AmqpMessageId messageId = annotatedMessage.getProperties().getMessageId(); return messageId != null ? messageId.toString() : null; } /** * Sets an application-defined value that uniquely identifies the event. The identifier is a free-form value and * can reflect a GUID or an identifier derived from the application context. * * @param messageId The message id. * * @return The updated {@link EventData}. */ public EventData setMessageId(String messageId) { final AmqpMessageId id = messageId != null ? new AmqpMessageId(messageId) : null; annotatedMessage.getProperties().setMessageId(id); return this; } /** * True if the object is an {@link EventData} and the binary contents of {@link #getBody()} are equal. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } EventData eventData = (EventData) o; return Arrays.equals(annotatedMessage.getBody().getFirstData(), eventData.annotatedMessage.getBody().getFirstData()); } /** * Gets a hash of the binary contents in {@link #getBody()}. */ @Override public int hashCode() { return Arrays.hashCode(annotatedMessage.getBody().getFirstData()); } /** * A specified key-value pair of type {@link Context} to set additional information on the event. * * @return the {@link Context} object set on the event */ Context getContext() { return context; } /** * Adds a new key value pair to the existing context on Event Data. * * @param key The key for this context object * @param value The value for this context object. * * @return The updated {@link EventData}. * * @throws NullPointerException if {@code key} or {@code value} is null. */ public EventData addContext(String key, Object value) { Objects.requireNonNull(key, "The 'key' parameter cannot be null."); Objects.requireNonNull(value, "The 'value' parameter cannot be null."); this.context = context.addData(key, value); return this; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy