Skip to content

Commit

Permalink
Added new Jandex based domain event registry
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-schnell committed Jan 21, 2024
1 parent def8694 commit d8553f7
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 1 deletion.
16 changes: 15 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
<dependency>
<groupId>org.fuin</groupId>
<artifactId>units4j</artifactId>
<version>0.11.0</version>
<version>0.12.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>

Expand Down Expand Up @@ -206,6 +206,20 @@
<artifactId>jacoco-maven-plugin</artifactId>
</plugin>

<plugin>
<groupId>io.smallrye</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>3.0.6</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>

</build>
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/org/fuin/cqrs4j/JandexDomainEventRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package org.fuin.cqrs4j;

import jakarta.validation.constraints.NotNull;
import org.fuin.ddd4j.ddd.DomainEvent;
import org.fuin.ddd4j.ddd.JandexEntityIdFactory;
import org.fuin.esc.api.*;
import org.fuin.utils4j.JandexIndexFileReader;
import org.fuin.utils4j.JandexUtils;
import org.jboss.jandex.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Modifier;
import java.util.*;

/**
* Registry that is built up by scanning for classes that are annotated with
* {@link HasSerializedDataTypeConstant} and implement {@link DomainEvent},
* but not {@link AggregateCommand}.
*/
public class JandexDomainEventRegistry implements SerializedDataTypeRegistry {

private static final Logger LOG = LoggerFactory.getLogger(JandexDomainEventRegistry.class);

private static final DotName COMMAND_INTERFACE = DotName.createSimple(Command.class);

private final SimpleSerializedDataTypeRegistry delegate;

private final List<File> classesDirs;

private final List<Class<?>> domainEventClasses;

/**
* Default constructor.
*/
public JandexDomainEventRegistry() {
this(new File("target/classes"));
}

/**
* Constructor with classes directories. Most likely only used in tests.
*
* @param classesDirs Directories with class files.
*/
public JandexDomainEventRegistry(final File... classesDirs) {
delegate = new SimpleSerializedDataTypeRegistry();
this.classesDirs = Arrays.asList(classesDirs);
domainEventClasses = scanForDomainEventClasses();
for (final Class<?> domainEventClass : domainEventClasses) {
delegate.add(serializedDataTypeConstant(domainEventClass), domainEventClass);
}
}

@Override
@NotNull
public Class<?> findClass(@NotNull SerializedDataType type) {
return delegate.findClass(type);
}

/**
* Returns a list of known {@link DomainEvent} classes.
*
* @return Domain event classes.
*/
public List<Class<?>> getDomainEventClasses() {
return Collections.unmodifiableList(domainEventClasses);
}

private List<Class<?>> scanForDomainEventClasses() {
final List<IndexView> indexes = new ArrayList<>();
indexes.add(new JandexIndexFileReader.Builder().addDefaultResource().build().loadR());
indexes.add(indexClassesDirs());
return findDomainEventClasses(CompositeIndex.create(indexes));
}

private IndexView indexClassesDirs() {
final Indexer indexer = new Indexer();
final List<File> knownClassFiles = new ArrayList<>();
for (final File classesDir : classesDirs) {
JandexUtils.indexDir(indexer, knownClassFiles, classesDir);
}
return indexer.complete();
}

private static List<Class<?>> findDomainEventClasses(final IndexView index) {
List<Class<?>> classes = new ArrayList<>();
final Collection<ClassInfo> classInfos = index.getAllKnownImplementors(DotName.createSimple(DomainEvent.class));
for (final ClassInfo classInfo : classInfos) {
if (!Modifier.isAbstract(classInfo.flags())
&& !Modifier.isInterface(classInfo.flags())) {

final Class<?> clasz = JandexUtils.loadClass(classInfo.name());
if (!AggregateCommand.class.isAssignableFrom(clasz)) {
boolean include = true;
if (clasz.getAnnotation(HasSerializedDataTypeConstant.class) == null) {
LOG.warn("Missing annotation @{} on {} class: {}", HasSerializedDataTypeConstant.class.getSimpleName(), DomainEvent.class.getSimpleName(), clasz.getName());
include = false;
}
if (include) {
classes.add(clasz);
LOG.info("Added {} class to {}: {}", DomainEvent.class.getSimpleName(), JandexEntityIdFactory.class.getSimpleName(), clasz.getName());
} else {
LOG.debug("Ignored {} class: {}", DomainEvent.class.getSimpleName(), clasz.getName());
}
}
}
}
return classes;
}

public SerializedDataType serializedDataTypeConstant(Class<?> domainEventClass) {
final HasSerializedDataTypeConstant annotation = domainEventClass.getAnnotation(HasSerializedDataTypeConstant.class);
return HasSerializedDataTypeConstantValidator.extractValue(domainEventClass, annotation.value());
}

}
56 changes: 56 additions & 0 deletions src/test/java/org/fuin/cqrs4j/ACreatedEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright (C) 2015 Michael Schnell. All rights reserved.
* http://www.fuin.org/
* <p>
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option) any
* later version.
* <p>
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
* <p>
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see http://www.gnu.org/licenses/.
*/
package org.fuin.cqrs4j;

import org.fuin.ddd4j.ddd.AbstractDomainEvent;
import org.fuin.ddd4j.ddd.EntityIdPath;
import org.fuin.ddd4j.ddd.EventType;
import org.fuin.esc.api.HasSerializedDataTypeConstant;
import org.fuin.esc.api.SerializedDataType;
import org.fuin.esc.api.TypeName;

@HasSerializedDataTypeConstant
public class ACreatedEvent extends AbstractDomainEvent<AId> {

private static final long serialVersionUID = 1L;

/** Unique name of the event. */
public static final TypeName TYPE = new TypeName("ACreatedEvent");

/** Unique name of the serialized event. */
public static final SerializedDataType SER_TYPE = new SerializedDataType(TYPE.asBaseType());

private static final EventType EVENT_TYPE = new EventType(TYPE.asBaseType());

private AId id;

public ACreatedEvent(final AId id) {
super(new EntityIdPath(id));
this.id = id;
}

public AId getId() {
return id;
}

@Override
public EventType getEventType() {
return EVENT_TYPE;
}

}
27 changes: 27 additions & 0 deletions src/test/java/org/fuin/cqrs4j/JandexDomainEventRegistryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.fuin.cqrs4j;

import org.junit.jupiter.api.Test;

import java.io.File;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Test for the {@link JandexDomainEventRegistry} class.
*/
public class JandexDomainEventRegistryTest {

@Test
public void testCreate() {
final JandexDomainEventRegistry testee = new JandexDomainEventRegistry(new File("target/test-classes"));
assertThat(testee.getDomainEventClasses()).containsOnly(ACreatedEvent.class);
}

@Test
public void testFind() {
final JandexDomainEventRegistry testee = new JandexDomainEventRegistry(new File("target/test-classes"));
assertThat(testee.findClass(ACreatedEvent.SER_TYPE)).isEqualTo(ACreatedEvent.class);

}

}

0 comments on commit d8553f7

Please sign in to comment.