/** * Initialize a DomainEventMessage originating from an aggregate. * * @param trackingToken Tracking token of the event * @param delegate Delegate domain event containing other event data */ public GenericTrackedDomainEventMessage(TrackingToken trackingToken, DomainEventMessage<T> delegate) { this(trackingToken, delegate.getType(), delegate.getAggregateIdentifier(), delegate.getSequenceNumber(), delegate, delegate.getTimestamp()); }
/** * Returns a {@link Predicate} for a {@link ConflictResolver} that responds affirmative if the payload of any event * in a list of unseen events is of given {@code payloadType} and matches the given {@code messageFilter}. If the * returned predicate matches an unseen event the ConflictResolver will throw an exception. * * @param payloadType the type of event payload to filter for * @param payloadFilter predicate for the payload of a single event * @return a Predicate to detect conflicts in unseen aggregate events */ @SuppressWarnings("unchecked") public static <T> Predicate<List<DomainEventMessage<?>>> payloadMatching(Class<T> payloadType, Predicate<? super T> payloadFilter) { return events -> events.stream().filter(event -> payloadType.isAssignableFrom(event.getPayloadType())) .map(event -> (T) event.getPayload()).anyMatch(payloadFilter::test); }
/** * Construct a new event entry from a published domain event message to enable storing the event or sending it to a * remote location. * <p> * The given {@code serializer} will be used to serialize the payload and metadata in the given {@code eventMessage}. * The type of the serialized data will be the same as the given {@code contentType}. * * @param eventMessage The event message to convert to a serialized event entry * @param serializer The serializer to convert the event * @param contentType The data type of the payload and metadata after serialization */ public AbstractDomainEventEntry(DomainEventMessage<?> eventMessage, Serializer serializer, Class<T> contentType) { super(eventMessage, serializer, contentType); type = eventMessage.getType(); aggregateIdentifier = eventMessage.getAggregateIdentifier(); sequenceNumber = eventMessage.getSequenceNumber(); }
@Override public DomainEventStream readEvents(String aggregateIdentifier, long firstSequenceNumber) { AtomicReference<Long> sequenceNumber = new AtomicReference<>(); Stream<? extends DomainEventMessage<?>> stream = events.values().stream().filter(event -> event instanceof DomainEventMessage<?>) .map(event -> (DomainEventMessage<?>) event) .filter(event -> aggregateIdentifier.equals(event.getAggregateIdentifier()) && event.getSequenceNumber() >= firstSequenceNumber) .peek(event -> sequenceNumber.set(event.getSequenceNumber())); return DomainEventStream.of(stream, sequenceNumber::get); }
@Override public Long resolveParameterValue(Message message) { if (message instanceof DomainEventMessage) { return ((DomainEventMessage) message).getSequenceNumber(); } return null; }
@Override public Object getSequenceIdentifierFor(EventMessage event) { if (event instanceof DomainEventMessage) { return ((DomainEventMessage) event).getAggregateIdentifier(); } return null; } }
/** * Returns a {@link Predicate} for a {@link ConflictResolver} that responds affirmative if the payload type of any * event in a list of unseen events is assignable to given {@code payloadType}. If the returned predicate matches an * unseen event the ConflictResolver will throw an exception. * * @param payloadType the type of event payload to filter for * @return a Predicate to detect conflicts in unseen aggregate events */ public static <T> Predicate<List<DomainEventMessage<?>>> payloadTypeOf(Class<T> payloadType) { return eventMatching(event -> payloadType.isAssignableFrom(event.getPayloadType())); }
/** * Construct a new event entry from a published domain event message to enable storing the event or sending it to a * remote location. * <p> * The given {@code serializer} will be used to serialize the payload and metadata in the given {@code * eventMessage}. The type of the serialized data will be the same as the given {@code contentType}. * * @param eventMessage The event message to convert to a serialized event entry * @param serializer The serializer to convert the event * @param contentType The data type of the payload and metadata after serialization */ public AbstractSnapshotEventEntry(DomainEventMessage<?> eventMessage, Serializer serializer, Class<T> contentType) { super(eventMessage, serializer, contentType); type = eventMessage.getType(); aggregateIdentifier = eventMessage.getAggregateIdentifier(); sequenceNumber = eventMessage.getSequenceNumber(); }
@Override protected void storeSnapshot(DomainEventMessage<?> snapshot, Serializer serializer) { try { entityManager().merge(createSnapshotEntity(snapshot, serializer)); deleteSnapshots(snapshot.getAggregateIdentifier(), snapshot.getSequenceNumber()); if (explicitFlush) { entityManager().flush(); } } catch (Exception e) { handlePersistenceException(e, snapshot); } }
@Override public Long getLastSequenceNumber() { return event.getSequenceNumber(); } };
@Override public void storeSnapshot(DomainEventMessage<?> snapshot) { snapshots.put(snapshot.getAggregateIdentifier(), snapshot); }
/** * Constructor used to create a new event entry to store in Mongo. * * @param serializer Serializer to use for the event to store * @param events The events contained in this commit */ public CommitEntry(List<? extends DomainEventMessage<?>> events, Serializer serializer) { DomainEventMessage firstEvent = events.get(0); DomainEventMessage lastEvent = events.get(events.size() - 1); firstSequenceNumber = firstEvent.getSequenceNumber(); firstTimestamp = formatInstant(firstEvent.getTimestamp()); lastTimestamp = formatInstant(lastEvent.getTimestamp()); lastSequenceNumber = lastEvent.getSequenceNumber(); aggregateIdentifier = lastEvent.getAggregateIdentifier(); lastEventIdentifier = lastEvent.getIdentifier(); aggregateType = lastEvent.getType(); eventEntries = new EventEntry[events.size()]; for (int i = 0, eventsLength = events.size(); i < eventsLength; i++) { DomainEventMessage event = events.get(i); eventEntries[i] = new EventEntry(event, serializer); } }
public static ProducerRecord<String, byte[]> toRecord(final EventMessage<?> message, final Serializer serializer, final String eventStorage) { final SerializedObject<byte[]> serializedObject = serializePayload(message, serializer, byte[].class); final Map<String, Object> headers = new HashMap<>(); message.getMetaData().forEach((k, v) -> headers.put("axon-metadata-" + k, v)); headers.put("axon-message-id", message.getIdentifier()); headers.put("axon-message-type", serializedObject.getType().getName()); headers.put("axon-message-revision", serializedObject.getType().getRevision()); headers.put("axon-message-timestamp", message.getTimestamp().toString()); if (message instanceof DomainEventMessage<?>) { headers.put("axon-message-aggregate-id", ((DomainEventMessage<?>) message).getAggregateIdentifier()); headers.put("axon-message-aggregate-seq", ((DomainEventMessage<?>) message).getSequenceNumber()); headers.put("axon-message-aggregate-type", ((DomainEventMessage<?>) message).getType()); } final KafkaPayload payload = new KafkaPayload(headers, serializedObject.getData()); final SerializedObject<byte[]> serializedKafkaPayload = serializer.serialize(payload, byte[].class); return new ProducerRecord<>(eventStorage, serializedKafkaPayload.getData()); }
/** * Invoke when an Exception is raised while persisting an Event or Snapshot. * * @param exception The exception raised while persisting an Event * @param failedEvent The EventMessage that could not be persisted */ protected void handlePersistenceException(Exception exception, EventMessage<?> failedEvent) { String eventDescription; if (failedEvent instanceof DomainEventMessage<?>) { DomainEventMessage<?> failedDomainEvent = (DomainEventMessage<?>) failedEvent; eventDescription = format("An event for aggregate [%s] at sequence [%d]", failedDomainEvent.getAggregateIdentifier(), failedDomainEvent.getSequenceNumber()); } else { eventDescription = format("An event with identifier [%s]", failedEvent.getIdentifier()); } if (persistenceExceptionResolver != null && persistenceExceptionResolver.isDuplicateKeyViolation(exception)) { throw new ConcurrencyException(eventDescription + " was already inserted", exception); } else { throw new EventStoreException(eventDescription + " could not be persisted", exception); } }
private DomainEventMessage<?> readNext() { DomainEventMessage<?> next = iterator.next(); this.sequenceNumber = next.getSequenceNumber(); return next; }
protected void scheduleSnapshot(DomainEventMessage msg) { snapshotter.scheduleSnapshot(aggregateType, msg.getAggregateIdentifier()); counter = 0; }
@SuppressWarnings("unchecked") @Override public final T createAggregateRoot(String aggregateIdentifier, DomainEventMessage<?> firstEvent) { T aggregate; if (aggregateBaseType.isAssignableFrom(firstEvent.getPayloadType())) { aggregate = (T) firstEvent.getPayload(); } else { aggregate = doCreateAggregate(aggregateIdentifier, firstEvent); } return postProcessInstance(aggregate); }
/** * Constructor used to create a new event entry to store in Mongo. * * @param event The actual DomainEvent to store * @param serializer Serializer to use for the event to store */ public EventEntry(DomainEventMessage<?> event, Serializer serializer) { aggregateIdentifier = event.getAggregateIdentifier(); aggregateType = event.getType(); sequenceNumber = event.getSequenceNumber(); eventIdentifier = event.getIdentifier(); Class<?> serializationTarget = String.class; if (serializer.canSerializeTo(DBObject.class)) { serializationTarget = DBObject.class; } SerializedObject<?> serializedPayloadObject = serializePayload(event, serializer, serializationTarget); SerializedObject<?> serializedMetaDataObject = serializeMetaData(event, serializer, serializationTarget); serializedPayload = serializedPayloadObject.getData(); payloadType = serializedPayloadObject.getType().getName(); payloadRevision = serializedPayloadObject.getType().getRevision(); serializedMetaData = serializedMetaDataObject.getData(); timestamp = formatInstant(event.getTimestamp()); }
/** * Generate defaults headers to recognise an event message. * * @param message event message. * @param serializedObject payload. * @return headers */ public static Map<String, Object> defaultHeaders(EventMessage<?> message, SerializedObject<?> serializedObject) { Assert.notNull(message, () -> "Event message cannot be null"); Assert.notNull(serializedObject, () -> "Serialized Object cannot be null"); Assert.notNull(serializedObject.getType(), () -> "SerializedObject Type cannot be null"); HashMap<String, Object> headers = new HashMap<>(); headers.put(MESSAGE_ID, message.getIdentifier()); headers.put(MESSAGE_TYPE, serializedObject.getType().getName()); headers.put(MESSAGE_REVISION, serializedObject.getType().getRevision()); headers.put(MESSAGE_TIMESTAMP, message.getTimestamp()); if (message instanceof DomainEventMessage) { headers.put(AGGREGATE_ID, ((DomainEventMessage<?>) message).getAggregateIdentifier()); headers.put(AGGREGATE_SEQ, ((DomainEventMessage<?>) message).getSequenceNumber()); headers.put(AGGREGATE_TYPE, ((DomainEventMessage<?>) message).getType()); } return Collections.unmodifiableMap(headers); }
@Override protected void storeSnapshot(DomainEventMessage<?> snapshot, Serializer serializer) { try { storageStrategy.appendSnapshot(template.snapshotCollection(), snapshot, serializer); storageStrategy.deleteSnapshots( template.snapshotCollection(), snapshot.getAggregateIdentifier(), snapshot.getSequenceNumber() ); } catch (Exception e) { handlePersistenceException(e, snapshot); } }