@Override protected void publish(PublishableEvents publishableEvents) { String aggregateType = publishableEvents.getAggregateType(); String aggregateId = publishableEvents.getEntityId(); List<EventIdTypeAndData> eventsWithIds = publishableEvents.getEventsWithIds(); List<Subscription> subscriptions; synchronized (aggregateTypeToSubscription) { List<Subscription> x = aggregateTypeToSubscription.get(aggregateType); subscriptions = x == null ? null : new ArrayList<>(x); } if (subscriptions != null) for (Subscription subscription : subscriptions) { for (EventIdTypeAndData event : eventsWithIds) { if (subscription.isInterestedIn(aggregateType, event.getEventType())) subscription.handler.apply(new SerializedEvent(event.getId(), aggregateId, aggregateType, event.getEventData(), event.getEventType(), aggregateId.hashCode() % 8, eventOffset.getAndIncrement(), new EventContext(event.getId().asString()), event.getMetadata())); } } }
public static List<EventIdTypeAndData> toSerializedEventsWithIds(List<EventTypeAndData> serializedEvents, List<Int128> eventIds) { return IntStream.range(0, serializedEvents.size()).boxed().map(idx -> new EventIdTypeAndData(eventIds.get(idx), serializedEvents.get(idx).getEventType(), serializedEvents.get(idx).getEventData(), serializedEvents.get(idx).getMetadata())).collect(Collectors.toList()); }
public static Event toEvent(EventIdTypeAndData eventIdTypeAndData) { try { return JSonMapper.fromJson(eventIdTypeAndData.getEventData(), (Class<Event>) Class.forName(eventIdTypeAndData.getEventType())); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } }
public static EventWithMetadata toEventWithMetadata(EventIdTypeAndData eventIdTypeAndData) { Optional<String> metadata = eventIdTypeAndData.getMetadata(); return new EventWithMetadata(toEvent(eventIdTypeAndData), eventIdTypeAndData.getId(), metadata == null ? Optional.empty() : metadata.map(md -> JSonMapper.fromJson(md, Map.class))); }
@Override public <T extends Aggregate<T>> EntityWithMetadata<T> find(Class<T> clasz, String entityId, Optional<FindOptions> findOptions) { try { LoadedEvents le = aggregateCrud.find(clasz.getName(), entityId, toAggregateCrudFindOptions(findOptions)); if (activityLogger.isDebugEnabled()) activityLogger.debug("Loaded entity: {} {} {}", clasz.getName(), entityId, le.getEvents()); List<EventWithMetadata> eventsWithIds = le.getEvents().stream().map(AggregateCrudMapping::toEventWithMetadata).collect(Collectors.toList()); List<Event> events = eventsWithIds.stream().map(EventWithMetadata::getEvent).collect(Collectors.toList()); return new EntityWithMetadata<T>( new EntityIdAndVersion(entityId, le.getEvents().isEmpty() ? le.getSnapshot().get().getEntityVersion() : le.getEvents().get(le.getEvents().size() - 1).getId()), le.getSnapshot().map(SerializedSnapshotWithVersion::getEntityVersion), eventsWithIds, le.getSnapshot().map(ss -> Aggregates.applyEventsToMutableAggregate((T) snapshotManager.recreateFromSnapshot(clasz, AggregateCrudMapping.toSnapshot(ss.getSerializedSnapshot()), missingApplyEventMethodStrategy), events, missingApplyEventMethodStrategy)) .orElseGet(() -> Aggregates.recreateAggregate(clasz, events, missingApplyEventMethodStrategy))); } catch (RuntimeException e) { if (activityLogger.isDebugEnabled()) activityLogger.trace(String.format("Find entity failed: %s %s", clasz.getName(), entityId), e); throw e; } }
@Test public void shouldSaveAndLoadEventMetadata() throws InterruptedException { String saveMetadata = "MyMetaData"; String updateMetadata = "MyMetaDataForUpdate"; LinkedBlockingQueue<SerializedEvent> events = new LinkedBlockingQueue<>(); aggregateEvents.subscribe("shouldSaveAndLoadEventMetadata", singletonMap(aggregateType, singleton("MyEventType")), new SubscriberOptions(), se -> { events.add(se); return new CompletableFuture<Object>(); }); EntityIdVersionAndEventIds eidv = eventStore.save(aggregateType, singletonList(new EventTypeAndData("MyEventType", "{}", Optional.of(saveMetadata))), Optional.empty()); LoadedEvents findResult = eventStore.find(aggregateType, eidv.getEntityId(), Optional.of(new AggregateCrudFindOptions())); assertEquals(Optional.of(saveMetadata), findResult.getEvents().get(0).getMetadata()); EntityIdVersionAndEventIds updateResult = eventStore.update( new EntityIdAndType(eidv.getEntityId(), aggregateType), eidv.getEntityVersion(), singletonList(new EventTypeAndData("MyEventType", "{}", Optional.of(updateMetadata))), Optional.empty()); LoadedEvents findResult2 = eventStore.find(aggregateType, eidv.getEntityId(), Optional.of(new AggregateCrudFindOptions())); assertEquals(Optional.of(saveMetadata), findResult2.getEvents().get(0).getMetadata()); assertEquals(Optional.of(updateMetadata), findResult2.getEvents().get(1).getMetadata()); assertContainsEventWithMetadata(eidv.getEventIds().get(0), saveMetadata, events); assertContainsEventWithMetadata(updateResult.getEventIds().get(0), updateMetadata, events); }
public static EventWithMetadata toEventWithMetadata(EventIdTypeAndData eventIdTypeAndData) { Optional<String> metadata = eventIdTypeAndData.getMetadata(); return new EventWithMetadata(toEvent(eventIdTypeAndData), eventIdTypeAndData.getId(), metadata == null ? Optional.empty() : metadata.map(md -> JSonMapper.fromJson(md, Map.class))); }
public static Event toEvent(EventIdTypeAndData eventIdTypeAndData) { try { return JSonMapper.fromJson(eventIdTypeAndData.getEventData(), (Class<Event>) Class.forName(eventIdTypeAndData.getEventType())); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } }
@Override public <T extends Aggregate<T>> EntityWithMetadata<T> find(Class<T> clasz, String entityId, Optional<FindOptions> findOptions) { try { LoadedEvents le = aggregateCrud.find(clasz.getName(), entityId, toAggregateCrudFindOptions(findOptions)); if (activityLogger.isDebugEnabled()) activityLogger.debug("Loaded entity: {} {} {}", clasz.getName(), entityId, le.getEvents()); List<EventWithMetadata> eventsWithIds = le.getEvents().stream().map(AggregateCrudMapping::toEventWithMetadata).collect(Collectors.toList()); List<Event> events = eventsWithIds.stream().map(EventWithMetadata::getEvent).collect(Collectors.toList()); return new EntityWithMetadata<T>( new EntityIdAndVersion(entityId, le.getEvents().isEmpty() ? le.getSnapshot().get().getEntityVersion() : le.getEvents().get(le.getEvents().size() - 1).getId()), le.getSnapshot().map(SerializedSnapshotWithVersion::getEntityVersion), eventsWithIds, le.getSnapshot().map(ss -> Aggregates.applyEventsToMutableAggregate((T) snapshotManager.recreateFromSnapshot(clasz, AggregateCrudMapping.toSnapshot(ss.getSerializedSnapshot()), missingApplyEventMethodStrategy), events, missingApplyEventMethodStrategy)) .orElseGet(() -> Aggregates.recreateAggregate(clasz, events, missingApplyEventMethodStrategy))); } catch (RuntimeException e) { if (activityLogger.isDebugEnabled()) activityLogger.trace(String.format("Find entity failed: %s %s", clasz.getName(), entityId), e); throw e; } }
@Override protected void publish(PublishableEvents publishableEvents) { String aggregateType = publishableEvents.getAggregateType(); String aggregateId = publishableEvents.getEntityId(); List<EventIdTypeAndData> eventsWithIds = publishableEvents.getEventsWithIds(); List<Subscription> subscriptions; synchronized (aggregateTypeToSubscription) { List<Subscription> x = aggregateTypeToSubscription.get(aggregateType); subscriptions = x == null ? null : new ArrayList<>(x); } if (subscriptions != null) for (Subscription subscription : subscriptions) { for (EventIdTypeAndData event : eventsWithIds) { if (subscription.isInterestedIn(aggregateType, event.getEventType())) subscription.handler.apply(new SerializedEvent(event.getId(), aggregateId, aggregateType, event.getEventData(), event.getEventType(), aggregateId.hashCode() % 8, eventOffset.getAndIncrement(), new EventContext(event.getId().asString()), event.getMetadata())); } } }
@Override public <T extends Aggregate<T>> CompletableFuture<EntityWithMetadata<T>> find(Class<T> clasz, String entityId, Optional<FindOptions> findOptions) { CompletableFuture<LoadedEvents> outcome = aggregateCrud.find(clasz.getName(), entityId, AggregateCrudMapping.toAggregateCrudFindOptions(findOptions)); CompletableFuture<LoadedEvents> tappedOutcome; if (activityLogger.isDebugEnabled()) tappedOutcome = CompletableFutureUtil.tap(outcome, (result, throwable) -> { if (throwable == null) activityLogger.debug("Loaded entity: {} {} {}", clasz.getName(), entityId, result.getEvents()); else { if (throwable instanceof EventuateException) activityLogger.trace(String.format("Find entity failed: %s %s %s", clasz.getName(), entityId, throwable.getClass().getName())); else activityLogger.trace(String.format("Find entity failed: %s %s", clasz.getName(), entityId), throwable); } }); else tappedOutcome = outcome; return tappedOutcome.thenApply(le -> { List<EventWithMetadata> eventsWithIds = eventSchemaMetadataManager.upcastEvents(clasz, le.getEvents()).stream().map(AggregateCrudMapping::toEventWithMetadata).collect(Collectors.toList()); List<Event> events = eventsWithIds.stream().map(EventWithMetadata::getEvent).collect(Collectors.toList()); return new EntityWithMetadata<T>( new EntityIdAndVersion(entityId, le.getEvents().isEmpty() ? le.getSnapshot().get().getEntityVersion() : le.getEvents().get(le.getEvents().size() - 1).getId()), le.getSnapshot().map(SerializedSnapshotWithVersion::getEntityVersion), eventsWithIds, le.getSnapshot().map(ss -> Aggregates.applyEventsToMutableAggregate((T) snapshotManager.recreateFromSnapshot(clasz, AggregateCrudMapping.toSnapshot(ss.getSerializedSnapshot()), missingApplyEventMethodStrategy), events, missingApplyEventMethodStrategy)) .orElseGet(() -> Aggregates.recreateAggregate(clasz, events, missingApplyEventMethodStrategy))); }); }
public static List<EventIdTypeAndData> toSerializedEventsWithIds(List<EventTypeAndData> serializedEvents, List<Int128> eventIds) { return IntStream.range(0, serializedEvents.size()).boxed().map(idx -> new EventIdTypeAndData(eventIds.get(idx), serializedEvents.get(idx).getEventType(), serializedEvents.get(idx).getEventData(), serializedEvents.get(idx).getMetadata())).collect(Collectors.toList()); }
@Override @Transactional public SaveUpdateResult save(String aggregateType, List<EventTypeAndData> events, Optional<AggregateCrudSaveOptions> saveOptions) { List<EventIdTypeAndData> eventsWithIds = events.stream().map(this::toEventWithId).collect(Collectors.toList()); String entityId = saveOptions.flatMap(AggregateCrudSaveOptions::getEntityId).orElse(idGenerator.genId().asString()); Int128 entityVersion = last(eventsWithIds).getId(); try { jdbcTemplate.update(String.format("INSERT INTO %s (entity_type, entity_id, entity_version) VALUES (?, ?, ?)", entityTable), aggregateType, entityId, entityVersion.asString()); } catch (DuplicateKeyException e) { throw new EntityAlreadyExistsException(); } for (EventIdTypeAndData event : eventsWithIds) jdbcTemplate.update(String.format("INSERT INTO %s (event_id, event_type, event_data, entity_type, entity_id, triggering_event, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)", eventTable), event.getId().asString(), event.getEventType(), event.getEventData(), aggregateType, entityId, saveOptions.flatMap(AggregateCrudSaveOptions::getTriggeringEvent).map(EventContext::getEventToken).orElse(null), event.getMetadata().orElse(null) ); return new SaveUpdateResult(new EntityIdVersionAndEventIds(entityId, entityVersion, eventsWithIds.stream().map(EventIdTypeAndData::getId).collect(Collectors.toList())), new PublishableEvents(aggregateType, entityId, eventsWithIds)); }
@Override public <T extends Aggregate<T>> CompletableFuture<EntityWithMetadata<T>> find(Class<T> clasz, String entityId, Optional<FindOptions> findOptions) { CompletableFuture<LoadedEvents> outcome = aggregateCrud.find(clasz.getName(), entityId, AggregateCrudMapping.toAggregateCrudFindOptions(findOptions)); CompletableFuture<LoadedEvents> tappedOutcome; if (activityLogger.isDebugEnabled()) tappedOutcome = CompletableFutureUtil.tap(outcome, (result, throwable) -> { if (throwable == null) activityLogger.debug("Loaded entity: {} {} {}", clasz.getName(), entityId, result.getEvents()); else { if (throwable instanceof EventuateException) activityLogger.trace(String.format("Find entity failed: %s %s %s", clasz.getName(), entityId, throwable.getClass().getName())); else activityLogger.trace(String.format("Find entity failed: %s %s", clasz.getName(), entityId), throwable); } }); else tappedOutcome = outcome; return tappedOutcome.thenApply(le -> { List<EventWithMetadata> eventsWithIds = eventSchemaMetadataManager.upcastEvents(clasz, le.getEvents()).stream().map(AggregateCrudMapping::toEventWithMetadata).collect(Collectors.toList()); List<Event> events = eventsWithIds.stream().map(EventWithMetadata::getEvent).collect(Collectors.toList()); return new EntityWithMetadata<T>( new EntityIdAndVersion(entityId, le.getEvents().isEmpty() ? le.getSnapshot().get().getEntityVersion() : le.getEvents().get(le.getEvents().size() - 1).getId()), le.getSnapshot().map(SerializedSnapshotWithVersion::getEntityVersion), eventsWithIds, le.getSnapshot().map(ss -> Aggregates.applyEventsToMutableAggregate((T) snapshotManager.recreateFromSnapshot(clasz, AggregateCrudMapping.toSnapshot(ss.getSerializedSnapshot()), missingApplyEventMethodStrategy), events, missingApplyEventMethodStrategy)) .orElseGet(() -> Aggregates.recreateAggregate(clasz, events, missingApplyEventMethodStrategy))); }); }
@Test public void getEntityResponseShouldConform() throws FileNotFoundException { shouldConformTo(new GetEntityResponse(Collections.singletonList(new EventIdTypeAndData(Int128.fromString("1-1"), "EventType", "EventData", Optional.empty()))), "get-response.json"); } }
Int128 updatedEntityVersion = last(eventsWithIds).getId(); event.getId().asString(), event.getEventType(), event.getEventData(), entityType, entityId, updateOptions.flatMap(AggregateCrudUpdateOptions::getTriggeringEvent).map(EventContext::getEventToken).orElse(null), event.getMetadata().orElse(null));
private EventIdTypeAndData toEventWithId(EventTypeAndData eventTypeAndData) { return new EventIdTypeAndData(idGenerator.genId(), eventTypeAndData.getEventType(), eventTypeAndData.getEventData(), eventTypeAndData.getMetadata()); }
@Test @PactVerification(fragment="find") public void shouldFind() throws URISyntaxException, ExecutionException, InterruptedException { LoadedEvents findResult = find().get(); assertEquals(Collections.singletonList(new EventIdTypeAndData(new Int128(8,9), RequestResponseJsonObjects.createdEvent, RequestResponseJsonObjects.eventData, null)), findResult.getEvents()); }
@Test @PactVerification(fragment="findWithTriggeringEvent") public void shouldFindWithTriggeringEvent() throws URISyntaxException, ExecutionException, InterruptedException { LoadedEvents findResult = findWithEventContext().get(); assertEquals(Collections.singletonList(new EventIdTypeAndData(new Int128(8,9), RequestResponseJsonObjects.createdEvent, RequestResponseJsonObjects.eventData, null)), findResult.getEvents()); }