Message.java

/*
 * Copyright 2018-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.mongodb.core.messaging;

import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 * General message abstraction for any type of Event / Message published by MongoDB server to the client. This might be
 * <a href="https://docs.mongodb.com/manual/reference/change-events/">Change Stream Events</a>, or
 * {@link org.bson.Document Documents} published by a
 * <a href="https://docs.mongodb.com/manual/core/tailable-cursors/">tailable cursor</a>. The original message received
 * is preserved in the raw parameter. Additional information about the origin of the {@link Message} is contained in
 * {@link MessageProperties}. <br />
 * For convenience the {@link #getBody()} of the message gets lazily converted into the target domain type if necessary
 * using the mapping infrastructure.
 *
 * @author Christoph Strobl
 * @author Mark Paluch
 * @author Myroslav Kosinskyi
 * @see MessageProperties
 * @since 2.1
 */
public interface Message<S, T> {

	/**
	 * The raw message source as emitted by the origin.
	 *
	 * @return can be {@literal null}.
	 */
	@Nullable
	S getRaw();

	/**
	 * The converted message body if available.
	 *
	 * @return can be {@literal null}.
	 */
	@Nullable
	T getBody();

	/**
	 * The converted message body before change if available.
	 *
	 * @return can be {@literal null}.
	 * @since 4.0
	 */
	default @Nullable T getBodyBeforeChange() {
		return null;
	}

	/**
	 * {@link MessageProperties} containing information about the {@link Message} origin and other metadata.
	 *
	 * @return never {@literal null}.
	 */
	MessageProperties getProperties();

	/**
	 * @author Christoph Strobl
	 * @since 2.1
	 */
	class MessageProperties {

		private static final MessageProperties EMPTY = new MessageProperties();

		private @Nullable String databaseName;
		private @Nullable String collectionName;

		/**
		 * The database name the message originates from.
		 *
		 * @return can be {@literal null}.
		 */
		public @Nullable String getDatabaseName() {
			return databaseName;
		}

		/**
		 * The collection name the message originates from.
		 *
		 * @return can be {@literal null}.
		 */
		public @Nullable String getCollectionName() {
			return collectionName;
		}

		/**
		 * @return empty {@link MessageProperties}.
		 */
		public static MessageProperties empty() {
			return EMPTY;
		}

		/**
		 * Obtain a shiny new {@link MessagePropertiesBuilder} and start defining options in this fancy fluent way. Just
		 * don't forget to call {@link MessagePropertiesBuilder#build() build()} when done.
		 *
		 * @return new instance of {@link MessagePropertiesBuilder}.
		 */
		public static MessagePropertiesBuilder builder() {
			return new MessagePropertiesBuilder();
		}

		@Override
		public boolean equals(@Nullable Object o) {
			if (this == o)
				return true;
			if (o == null || getClass() != o.getClass())
				return false;

			MessageProperties that = (MessageProperties) o;

			if (!ObjectUtils.nullSafeEquals(this.databaseName, that.databaseName)) {
				return false;
			}

			return ObjectUtils.nullSafeEquals(this.collectionName, that.collectionName);
		}

		@Override
		public int hashCode() {
			int result = ObjectUtils.nullSafeHashCode(databaseName);
			result = 31 * result + ObjectUtils.nullSafeHashCode(collectionName);
			return result;
		}

		public String toString() {
			return "Message.MessageProperties(databaseName=" + this.getDatabaseName() + ", collectionName="
					+ this.getCollectionName() + ")";
		}

		/**
		 * Builder for {@link MessageProperties}.
		 *
		 * @author Christoph Strobl
		 * @since 2.1
		 */
		public static class MessagePropertiesBuilder {

			private @Nullable String databaseName;
			private @Nullable String collectionName;

			/**
			 * @param dbName must not be {@literal null}.
			 * @return this.
			 */
			@Contract("_ -> this")
			public MessagePropertiesBuilder databaseName(String dbName) {

				Assert.notNull(dbName, "Database name must not be null");

				this.databaseName = dbName;
				return this;
			}

			/**
			 * @param collectionName must not be {@literal null}.
			 * @return this
			 */
			@Contract("_ -> this")
			public MessagePropertiesBuilder collectionName(String collectionName) {

				Assert.notNull(collectionName, "Collection name must not be null");

				this.collectionName = collectionName;
				return this;
			}

			/**
			 * @return the built {@link MessageProperties}.
			 */
			@Contract("-> new")
			public MessageProperties build() {

				MessageProperties properties = new MessageProperties();

				properties.collectionName = collectionName;
				properties.databaseName = databaseName;

				return properties;
			}
		}
	}
}