io.fluxcapacitor.javaclient.persisting.caching.CachingAggregateRepository Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-client Show documentation
Show all versions of java-client Show documentation
Default Java client library for interfacing with Flux Capacitor.
package io.fluxcapacitor.javaclient.persisting.caching;
import io.fluxcapacitor.common.MessageType;
import io.fluxcapacitor.common.api.SerializedMessage;
import io.fluxcapacitor.javaclient.common.Message;
import io.fluxcapacitor.javaclient.common.serialization.DeserializingMessage;
import io.fluxcapacitor.javaclient.common.serialization.Serializer;
import io.fluxcapacitor.javaclient.modeling.Aggregate;
import io.fluxcapacitor.javaclient.modeling.AggregateRepository;
import io.fluxcapacitor.javaclient.persisting.eventsourcing.EventSourcingHandler;
import io.fluxcapacitor.javaclient.persisting.eventsourcing.EventSourcingHandlerFactory;
import io.fluxcapacitor.javaclient.tracking.client.TrackingClient;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import static io.fluxcapacitor.common.MessageType.EVENT;
import static io.fluxcapacitor.javaclient.modeling.AggregateIdResolver.getAggregateId;
import static io.fluxcapacitor.javaclient.modeling.AggregateTypeResolver.getAggregateType;
import static io.fluxcapacitor.javaclient.tracking.client.TrackingUtils.start;
import static java.lang.String.format;
import static java.time.Instant.ofEpochMilli;
@RequiredArgsConstructor
@Slf4j
public class CachingAggregateRepository implements AggregateRepository {
private static final Function keyFunction = aggregateId ->
CachingAggregateRepository.class.getSimpleName() + ":" + aggregateId;
private final AggregateRepository delegate;
private final EventSourcingHandlerFactory handlerFactory;
private final Cache cache;
private final String clientName;
private final TrackingClient trackingClient;
private final Serializer serializer;
private final AtomicBoolean started = new AtomicBoolean();
private final AtomicLong lastEventIndex = new AtomicLong();
@Override
public Aggregate load(@NonNull String aggregateId, @NonNull Class aggregateType, boolean onlyCached) {
if (!delegate.cachingAllowed(aggregateType)) {
return delegate.load(aggregateId, aggregateType, onlyCached);
}
DeserializingMessage current = DeserializingMessage.getCurrent();
if (current != null && current.getMessageType() == MessageType.COMMAND) {
return delegate.load(aggregateId, aggregateType, onlyCached);
}
Aggregate result = delegate.load(aggregateId, aggregateType, true);
if (result == null) {
return Optional.>ofNullable(doLoad(aggregateId))
.filter(a -> Optional.ofNullable(a.get()).map(m -> aggregateType.isAssignableFrom(m.getClass()))
.orElse(true))
.orElseGet(() -> delegate.load(aggregateId, aggregateType, onlyCached));
}
return result;
}
private RefreshingAggregate doLoad(String aggregateId) {
if (started.compareAndSet(false, true)) {
log.info("Start tracking notifications");
start(format("%s_%s", clientName, CachingAggregateRepository.class.getSimpleName()),
trackingClient, this::handleEvents);
return null;
}
if (lastEventIndex.get() <= 0) {
return null;
}
DeserializingMessage current = DeserializingMessage.getCurrent();
if (current != null) {
switch (current.getMessageType()) {
case EVENT:
case NOTIFICATION:
Long eventIndex = current.getSerializedObject().getIndex();
if (eventIndex != null && lastEventIndex.get() < eventIndex) {
synchronized (cache) {
while (lastEventIndex.get() < eventIndex) {
try {
cache.wait(5_000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("Failed to load aggregate for event {}", current, e);
return null;
}
}
}
}
break;
}
}
return cache.getIfPresent(keyFunction.apply(aggregateId));
}
protected void handleEvents(List messages) {
serializer.deserializeMessages(messages.stream(), false, EVENT)
.forEach(m -> {
String aggregateId = getAggregateId(m);
Class> aggregateType = getAggregateType(m);
if (aggregateId != null && aggregateType != null && delegate.cachingAllowed(aggregateType)) {
try {
handleEvent(m, aggregateId, aggregateType);
} catch (Exception e) {
log.error("Failed to handle event for aggregate with id {} of type {}", aggregateId,
aggregateType, e);
}
}
});
}
protected void handleEvent(DeserializingMessage event, String aggregateId, Class> type) {
try {
EventSourcingHandler