/**
* Copyright (c) 2016, All Contributors (see CONTRIBUTORS file)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.eventsourcing.repository;
import com.eventsourcing.Entity;
import com.eventsourcing.EntityHandle;
import com.eventsourcing.EntitySubscriber;
import com.eventsourcing.Repository;
import com.eventsourcing.index.IndexEngine;
import com.eventsourcing.index.IndexLoader;
import com.eventsourcing.inmem.MemoryIndexEngine;
import com.googlecode.cqengine.IndexedCollection;
import com.googlecode.cqengine.index.Index;
import com.googlecode.cqengine.query.Query;
import com.googlecode.cqengine.resultset.ResultSet;
import lombok.SneakyThrows;
import lombok.Value;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class QuerySubscriber implements EntitySubscriber<Entity> {
private Repository repository;
private final Set<IndexLoader> indexLoaders = new LinkedHashSet<>();
@Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE)
public void bindIndexLoader(IndexLoader loader) {
indexLoaders.add(loader);
}
public void unbindIndexLoader(IndexLoader loader) {
indexLoaders.remove(loader);
}
@Reference
public void setRepository(Repository repository) {
this.repository = repository;
}
public void unsetRepository(Repository repository) {
this.repository = null;
}
public QuerySubscriber() {
}
public QuerySubscriber(Repository repository) {
this.repository = repository;
}
@Value
private static class QueryHolder<E extends Entity> {
private Class<E> klass;
private Query<EntityHandle<E>> query;
public int hashCode() {
return query.hashCode();
}
}
private Map<QueryHolder, Runnable> queries = new HashMap<>();
public <E extends Entity> void addQuery(Class<E> klass, Query<EntityHandle<E>> query,
Consumer<Query<EntityHandle<E>>> consumer) {
queries.put(new QueryHolder<>(klass, query), () -> consumer.accept(query));
}
public <E extends Entity> void removeQuery(Class<E> klass, Query<EntityHandle<E>> query) {
queries.remove(new QueryHolder<>(klass, query));
}
@SneakyThrows
@Override public void accept(Repository repository, Stream<EntityHandle<Entity>> entityStream) {
MemoryIndexEngine indexEngine = new MemoryIndexEngine();
indexEngine.setJournal(repository.getJournal());
indexEngine.setRepository(repository);
for (Class<? extends Entity> klass : repository.getCommands()) {
configureIndices(indexEngine, klass);
}
for (Class<? extends Entity> klass : repository.getEvents()) {
configureIndices(indexEngine, klass);
}
indexEngine.startAsync().awaitRunning();
entityStream.forEachOrdered(h -> {
Entity entity = h.get();
IndexedCollection<EntityHandle<Entity>> indexedCollection =
indexEngine.getIndexedCollection((Class<Entity>)entity.getClass());
indexedCollection.add(h);
});
//
queries.forEach((query, runnable) -> {
IndexedCollection collection = indexEngine.getIndexedCollection(query.getKlass());
try (ResultSet resultSet = collection.retrieve(query.getQuery())) {
if (resultSet.isNotEmpty()) {
runnable.run();
}
}
});
//
indexEngine.stopAsync().awaitTerminated();
}
private void configureIndices(IndexEngine engine, Class<? extends Entity> klass) throws IndexEngine.IndexNotSupported {
for (IndexLoader indexLoader : indexLoaders) {
Iterable<Index> indices = indexLoader.load(engine, klass);
for (Index i : indices) {
IndexedCollection<? extends EntityHandle<? extends Entity>> collection =
engine.getIndexedCollection(klass);
boolean hasIndex = StreamSupport.stream(collection.getIndexes().spliterator(), false)
.anyMatch(index -> index.equals(i));
if (!hasIndex) {
collection.addIndex(i);
}
}
}
}
}