PageRenderTime 1109ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ChangeStreamEvent.java

http://github.com/SpringSource/spring-data-mongodb
Java | 223 lines | 110 code | 38 blank | 75 comment | 22 complexity | 46e3337a5cb6fecb1eea3a9faae7ac91 MD5 | raw file
  1. /*
  2. * Copyright 2018-2021 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.data.mongodb.core;
  17. import java.time.Instant;
  18. import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
  19. import org.bson.BsonTimestamp;
  20. import org.bson.BsonValue;
  21. import org.bson.Document;
  22. import org.springframework.data.mongodb.core.convert.MongoConverter;
  23. import org.springframework.data.mongodb.core.messaging.Message;
  24. import org.springframework.lang.Nullable;
  25. import org.springframework.util.ClassUtils;
  26. import org.springframework.util.ObjectUtils;
  27. import com.mongodb.client.model.changestream.ChangeStreamDocument;
  28. import com.mongodb.client.model.changestream.OperationType;
  29. /**
  30. * {@link Message} implementation specific to MongoDB <a href="https://docs.mongodb.com/manual/changeStreams/">Change
  31. * Streams</a>.
  32. *
  33. * @author Christoph Strobl
  34. * @author Mark Paluch
  35. * @since 2.1
  36. */
  37. public class ChangeStreamEvent<T> {
  38. @SuppressWarnings("rawtypes") //
  39. private static final AtomicReferenceFieldUpdater<ChangeStreamEvent, Object> CONVERTED_UPDATER = AtomicReferenceFieldUpdater
  40. .newUpdater(ChangeStreamEvent.class, Object.class, "converted");
  41. private final @Nullable ChangeStreamDocument<Document> raw;
  42. private final Class<T> targetType;
  43. private final MongoConverter converter;
  44. // accessed through CONVERTED_UPDATER.
  45. private volatile @Nullable T converted;
  46. /**
  47. * @param raw can be {@literal null}.
  48. * @param targetType must not be {@literal null}.
  49. * @param converter must not be {@literal null}.
  50. */
  51. public ChangeStreamEvent(@Nullable ChangeStreamDocument<Document> raw, Class<T> targetType,
  52. MongoConverter converter) {
  53. this.raw = raw;
  54. this.targetType = targetType;
  55. this.converter = converter;
  56. }
  57. /**
  58. * Get the raw {@link ChangeStreamDocument} as emitted by the driver.
  59. *
  60. * @return can be {@literal null}.
  61. */
  62. @Nullable
  63. public ChangeStreamDocument<Document> getRaw() {
  64. return raw;
  65. }
  66. /**
  67. * Get the {@link ChangeStreamDocument#getClusterTime() cluster time} as {@link Instant} the event was emitted at.
  68. *
  69. * @return can be {@literal null}.
  70. */
  71. @Nullable
  72. public Instant getTimestamp() {
  73. return getBsonTimestamp() != null ? converter.getConversionService().convert(raw.getClusterTime(), Instant.class)
  74. : null;
  75. }
  76. /**
  77. * Get the {@link ChangeStreamDocument#getClusterTime() cluster time}.
  78. *
  79. * @return can be {@literal null}.
  80. * @since 2.2
  81. */
  82. @Nullable
  83. public BsonTimestamp getBsonTimestamp() {
  84. return raw != null ? raw.getClusterTime() : null;
  85. }
  86. /**
  87. * Get the {@link ChangeStreamDocument#getResumeToken() resume token} for this event.
  88. *
  89. * @return can be {@literal null}.
  90. */
  91. @Nullable
  92. public BsonValue getResumeToken() {
  93. return raw != null ? raw.getResumeToken() : null;
  94. }
  95. /**
  96. * Get the {@link ChangeStreamDocument#getOperationType() operation type} for this event.
  97. *
  98. * @return can be {@literal null}.
  99. */
  100. @Nullable
  101. public OperationType getOperationType() {
  102. return raw != null ? raw.getOperationType() : null;
  103. }
  104. /**
  105. * Get the database name the event was originated at.
  106. *
  107. * @return can be {@literal null}.
  108. */
  109. @Nullable
  110. public String getDatabaseName() {
  111. return raw != null ? raw.getNamespace().getDatabaseName() : null;
  112. }
  113. /**
  114. * Get the collection name the event was originated at.
  115. *
  116. * @return can be {@literal null}.
  117. */
  118. @Nullable
  119. public String getCollectionName() {
  120. return raw != null ? raw.getNamespace().getCollectionName() : null;
  121. }
  122. /**
  123. * Get the potentially converted {@link ChangeStreamDocument#getFullDocument()}.
  124. *
  125. * @return {@literal null} when {@link #getRaw()} or {@link ChangeStreamDocument#getFullDocument()} is
  126. * {@literal null}.
  127. */
  128. @Nullable
  129. public T getBody() {
  130. if (raw == null) {
  131. return null;
  132. }
  133. Document fullDocument = raw.getFullDocument();
  134. if (fullDocument == null) {
  135. return targetType.cast(fullDocument);
  136. }
  137. return getConverted(fullDocument);
  138. }
  139. @SuppressWarnings("unchecked")
  140. private T getConverted(Document fullDocument) {
  141. return (T) doGetConverted(fullDocument);
  142. }
  143. private Object doGetConverted(Document fullDocument) {
  144. Object result = CONVERTED_UPDATER.get(this);
  145. if (result != null) {
  146. return result;
  147. }
  148. if (ClassUtils.isAssignable(Document.class, fullDocument.getClass())) {
  149. result = converter.read(targetType, fullDocument);
  150. return CONVERTED_UPDATER.compareAndSet(this, null, result) ? result : CONVERTED_UPDATER.get(this);
  151. }
  152. if (converter.getConversionService().canConvert(fullDocument.getClass(), targetType)) {
  153. result = converter.getConversionService().convert(fullDocument, targetType);
  154. return CONVERTED_UPDATER.compareAndSet(this, null, result) ? result : CONVERTED_UPDATER.get(this);
  155. }
  156. throw new IllegalArgumentException(
  157. String.format("No converter found capable of converting %s to %s", fullDocument.getClass(), targetType));
  158. }
  159. /*
  160. * (non-Javadoc)
  161. * @see java.lang.Object#toString()
  162. */
  163. @Override
  164. public String toString() {
  165. return "ChangeStreamEvent {" + "raw=" + raw + ", targetType=" + targetType + '}';
  166. }
  167. @Override
  168. public boolean equals(Object o) {
  169. if (this == o)
  170. return true;
  171. if (o == null || getClass() != o.getClass())
  172. return false;
  173. ChangeStreamEvent<?> that = (ChangeStreamEvent<?>) o;
  174. if (!ObjectUtils.nullSafeEquals(this.raw, that.raw)) {
  175. return false;
  176. }
  177. return ObjectUtils.nullSafeEquals(this.targetType, that.targetType);
  178. }
  179. @Override
  180. public int hashCode() {
  181. int result = raw != null ? raw.hashCode() : 0;
  182. result = 31 * result + ObjectUtils.nullSafeHashCode(targetType);
  183. return result;
  184. }
  185. }