@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, target = "(!(service.pid=com.eventsourcing.index.CascadingIndexEngine))") public void addIndexEngine(IndexEngine indexEngine) { indexEngine.setJournal(journal); indexEngine.setRepository(repository); indexEngines.add(indexEngine); sortIndexEngines(); }
@Override public <O extends Entity, A> Index<O> getIndexOnAttribute(Attribute<O, A> attribute, IndexFeature... features) throws IndexNotSupported { for (IndexEngine engine : indexEngines) { try { Index<O> index = engine.getIndexOnAttribute(attribute, features); decisions.put(Joiner.on(", ").join(features) + " on " + attribute.toString(), engine); return index; } catch (IndexNotSupported e) { } } throw new IndexNotSupported(new Attribute[]{attribute}, features, this); }
@Override public <O extends Entity, A> Index<O> getIndexOnAttributes(Attribute<O, A>[] attributes, IndexFeature... features) throws IndexNotSupported { for (IndexEngine engine : indexEngines) { try { Index<O> index = engine.getIndexOnAttributes(attributes, features); for (Attribute attribute : attributes) { decisions.put(Joiner.on(", ").join(features) + " on " + attribute.toString(), engine); } return index; } catch (IndexNotSupported e) { } } throw new IndexNotSupported(attributes, features, this); }
@Override public Stream<T> getCollectionStream(Repository repository) { IndexedCollection<EntityHandle<Deleted>> deletedCollection = repository.getIndexEngine().getIndexedCollection(Deleted.class); IndexedCollection<EntityHandle<Undeleted>> undeletedCollection = repository.getIndexEngine().getIndexedCollection(Undeleted.class); ResultSet<EntityHandle<E>> resultSet = repository .query(klass, not(existsIn(deletedCollection, idAttribute, Deleted.REFERENCE_ID, not(existsIn(undeletedCollection, Deleted.ID, Undeleted.DELETED_ID))))); return streamOf(resultSet) .map(h -> loader.load(repository, h.get().uuid()).get()) .onClose(resultSet::close); }
@Override public void setJournal(Journal journal) throws IllegalStateException { this.journal = journal; indexEngines.forEach(indexEngine -> indexEngine.setJournal(journal)); }
@Override public void setRepository(Repository repository) throws IllegalStateException { this.repository = repository; indexEngines.forEach(indexEngine -> indexEngine.setRepository(repository)); }
protected void sortIndexEngines() { if (configuredIndexEngines != null) { indexEngines = Arrays.asList(configuredIndexEngines).stream(). map(name -> indexEngines.stream().filter(e -> e.getType().contentEquals(name)) .findFirst()). filter(Optional::isPresent). map(Optional::get). collect(Collectors.toList()); } }
@Override public Stream<T> getCollectionStream(Repository repository) { IndexedCollection<EntityHandle<Deleted>> deletedCollection = repository.getIndexEngine().getIndexedCollection(Deleted.class); IndexedCollection<EntityHandle<Undeleted>> undeletedCollection = repository.getIndexEngine().getIndexedCollection(Undeleted.class); ResultSet<EntityHandle<E>> resultSet = repository .query(klass, not(existsIn(deletedCollection, idAttribute, Deleted.REFERENCE_ID, not(existsIn(undeletedCollection, Deleted.ID, Undeleted.DELETED_ID))))); return streamOf(resultSet) .map(h -> loader.load(repository, h.get().uuid()).get()) .onClose(resultSet::close); }
@SneakyThrows @Override public TabularData getCascadingDecisions() { CompositeType type = new CompositeType("Decision", "Cascading decision", new String[]{"Index", "Engine"}, new String[]{"Index", "Index Engine"}, new OpenType[]{SimpleType.STRING, SimpleType.STRING}); TabularDataSupport tab = new TabularDataSupport( new TabularType("Decisions", "Cascading decisions", type, new String[]{"Index"})); for (Map.Entry<String, IndexEngine> entry : decisions.entrySet()) { tab.put(new CompositeDataSupport(type, new String[]{"Index", "Engine"}, new Object[]{entry.getKey(), entry.getValue().getType()})); } return tab; } }
default Optional<Deleted> deleted() { Not<EntityHandle<Deleted>> additionalQuery = not(existsIn(getRepository().getIndexEngine() .getIndexedCollection(Undeleted.class), Deleted.ID, Undeleted.DELETED_ID)); return latestAssociatedEntity(Deleted.class, Deleted.REFERENCE_ID, Deleted.TIMESTAMP, additionalQuery); }
@SneakyThrows public IndexEngineTest(T indexEngine) { this.indexEngine = indexEngine; repository = new StandardRepository(); journal = new MemoryJournal(); journal.setRepository(repository); repository.setJournal(journal); repository.addCommandSetProvider(new PackageCommandSetProvider(new Package[]{getClass().getPackage()})); repository.addEventSetProvider(new PackageEventSetProvider(new Package[]{getClass().getPackage()})); repository.setIndexEngine(indexEngine); repository.setLockProvider(new LocalLockProvider()); timeProvider = new NTPServerTimeProvider(new String[]{"localhost"}); repository.setPhysicalTimeProvider(timeProvider); indexEngine.setJournal(journal); indexEngine.setRepository(repository); }
indices.add(engine.getIndexOnAttribute(attribute, features));
@Override public Stream<T> getCollectionStream(Repository repository) { IndexedCollection<EntityHandle<Deleted>> deletedCollection = repository.getIndexEngine() .getIndexedCollection(Deleted.class); IndexedCollection<EntityHandle<Undeleted>> undeletedCollection = repository.getIndexEngine() .getIndexedCollection( Undeleted.class); Query<EntityHandle<Deleted>> query = and(not(existsIn(undeletedCollection, Deleted.ID, Undeleted.DELETED_ID)), isLatestEntity(deletedCollection, new DeletedQueryFunction(), Deleted.TIMESTAMP)); ResultSet<EntityHandle<Deleted>> resultSet = repository.query(Deleted.class, query); return streamOf(resultSet) .map(h -> loader.load(repository, h.get().reference()).get()) .onClose(resultSet::close); }
default Optional<Deleted> deleted() { Not<EntityHandle<Deleted>> additionalQuery = not(existsIn(getRepository().getIndexEngine() .getIndexedCollection(Undeleted.class), Deleted.ID, Undeleted.DELETED_ID)); return latestAssociatedEntity(Deleted.class, Deleted.REFERENCE_ID, Deleted.TIMESTAMP, additionalQuery); }
@Override public Stream<T> getCollectionStream(Repository repository) { IndexedCollection<EntityHandle<Deleted>> deletedCollection = repository.getIndexEngine() .getIndexedCollection(Deleted.class); IndexedCollection<EntityHandle<Undeleted>> undeletedCollection = repository.getIndexEngine() .getIndexedCollection( Undeleted.class); Query<EntityHandle<Deleted>> query = and(not(existsIn(undeletedCollection, Deleted.ID, Undeleted.DELETED_ID)), isLatestEntity(deletedCollection, new DeletedQueryFunction(), Deleted.TIMESTAMP)); ResultSet<EntityHandle<Deleted>> resultSet = repository.query(Deleted.class, query); return streamOf(resultSet) .map(h -> loader.load(repository, h.get().reference()).get()) .onClose(resultSet::close); }
/** * Shortcut method for accessing index querying. * <p> * <p>Example:</p> * <p> * {@code * repository.query(UserCreated.class, equal(UserCreated.EMAIL, email), noQueryOptions()) * } * * @param klass * @param query * @param queryOptions * @param <E> * @return */ default <E extends Entity> ResultSet<EntityHandle<E>> query(Class<E> klass, Query<EntityHandle<E>> query, QueryOptions queryOptions) { IndexedCollection<EntityHandle<E>> collection = getIndexEngine().getIndexedCollection(klass); queryOptions.put(Iterable.class, collection); queryOptions.put(IndexedCollection.class, collection); return collection.retrieve(query, queryOptions); }
/** * Figure out if the command has terminated exceptionally by testing the presence of an associated * {@link CommandTerminatedExceptionally} event. * @param repository * @return <code>true</code> if the command has terminated exceptionally */ default boolean hasTerminatedExceptionally(Repository repository) { try (ResultSet<EntityHandle<CommandTerminatedExceptionally>> resultSet = repository .query(CommandTerminatedExceptionally.class, and(all(CommandTerminatedExceptionally.class), existsIn( repository.getIndexEngine().getIndexedCollection(EventCausalityEstablished.class), CommandTerminatedExceptionally.ID, EventCausalityEstablished.EVENT)))) { return resultSet.isNotEmpty(); } }
/** * Figure out the cause of command termination by searching for {@link JavaExceptionOccurred} events, associated * with the commend. * @param repository * @return an instance of {@link JavaExceptionOccurred} or <code>null</code> if none found */ default JavaExceptionOccurred exceptionalTerminationCause(Repository repository) { try (ResultSet<EntityHandle<JavaExceptionOccurred>> resultSet = repository .query(JavaExceptionOccurred.class, and(all(JavaExceptionOccurred.class), existsIn( repository.getIndexEngine().getIndexedCollection(EventCausalityEstablished.class), JavaExceptionOccurred.ID, EventCausalityEstablished.EVENT)))) { if (resultSet.isEmpty()) { return null; } return resultSet.uniqueResult().get(); } } }