diff --git a/api/pom.xml b/api/pom.xml
index be94a16a..c8b2813c 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -57,14 +57,13 @@
jakarta.json-api
-
-
io.smallrye
jandex
- test
+
+
org.junit.jupiter
junit-jupiter
@@ -137,6 +136,12 @@
test
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
diff --git a/api/src/main/java/org/fuin/esc/api/HasSerializedDataTypeConstantValidator.java b/api/src/main/java/org/fuin/esc/api/HasSerializedDataTypeConstantValidator.java
index cb12403d..c050aa18 100644
--- a/api/src/main/java/org/fuin/esc/api/HasSerializedDataTypeConstantValidator.java
+++ b/api/src/main/java/org/fuin/esc/api/HasSerializedDataTypeConstantValidator.java
@@ -75,7 +75,7 @@ public static SerializedDataType extractValue(final Class> clasz, final String
if (result.message() == null) {
return (SerializedDataType) result.value();
}
- throw new IllegalArgumentException(result.message());
+ throw new IllegalArgumentException(result.message() + " (" + clasz + ")");
}
}
diff --git a/api/src/main/java/org/fuin/esc/api/JandexSerializedDataTypeRegistry.java b/api/src/main/java/org/fuin/esc/api/JandexSerializedDataTypeRegistry.java
new file mode 100644
index 00000000..71494e95
--- /dev/null
+++ b/api/src/main/java/org/fuin/esc/api/JandexSerializedDataTypeRegistry.java
@@ -0,0 +1,101 @@
+package org.fuin.esc.api;
+
+import jakarta.validation.constraints.NotNull;
+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}.
+ * Inner classes are ignored.
+ */
+public class JandexSerializedDataTypeRegistry implements SerializedDataTypeRegistry {
+
+ private static final Logger LOG = LoggerFactory.getLogger(JandexSerializedDataTypeRegistry.class);
+
+ private final SimpleSerializedDataTypeRegistry delegate;
+
+ private final List classesDirs;
+
+ private final List> classes;
+
+ /**
+ * Default constructor.
+ */
+ public JandexSerializedDataTypeRegistry() {
+ this(new File("target/classes"));
+ }
+
+ /**
+ * Constructor with classes directories. Most likely only used in tests.
+ *
+ * @param classesDirs Directories with class files.
+ */
+ public JandexSerializedDataTypeRegistry(final File... classesDirs) {
+ delegate = new SimpleSerializedDataTypeRegistry();
+ this.classesDirs = Arrays.asList(classesDirs);
+ classes = scanForClasses();
+ for (final Class> domainEventClass : classes) {
+ delegate.add(serializedDataTypeConstant(domainEventClass), domainEventClass);
+ }
+ }
+
+ @Override
+ @NotNull
+ public Class> findClass(@NotNull SerializedDataType type) {
+ return delegate.findClass(type);
+ }
+
+ /**
+ * Returns a list of known classes that can be serialized.
+ *
+ * @return SerializedDataType classes.
+ */
+ public List> getClasses() {
+ return Collections.unmodifiableList(classes);
+ }
+
+ private List> scanForClasses() {
+ final List indexes = new ArrayList<>();
+ indexes.add(new JandexIndexFileReader.Builder().addDefaultResource().build().loadR());
+ indexes.add(indexClassesDirs());
+ return findClasses(CompositeIndex.create(indexes));
+ }
+
+ private IndexView indexClassesDirs() {
+ final Indexer indexer = new Indexer();
+ final List knownClassFiles = new ArrayList<>();
+ for (final File classesDir : classesDirs) {
+ JandexUtils.indexDir(indexer, knownClassFiles, classesDir);
+ }
+ return indexer.complete();
+ }
+
+ private static List> findClasses(final IndexView index) {
+ List> classes = new ArrayList<>();
+ final Collection annotationInstances = index.getAnnotations(DotName.createSimple(HasSerializedDataTypeConstant.class));
+ for (final AnnotationInstance annotationInstance : annotationInstances) {
+ final ClassInfo classInfo = annotationInstance.target().asClass();
+ if (!Modifier.isAbstract(classInfo.flags())
+ && !Modifier.isInterface(classInfo.flags())
+ && !classInfo.name().toString().contains("$")) {
+ final Class> clasz = JandexUtils.loadClass(classInfo.name());
+ classes.add(clasz);
+ LOG.info("Added SerializedDataType to {}: {}", JandexSerializedDataTypeRegistry.class.getSimpleName(), clasz.getName());
+ }
+ }
+ return classes;
+ }
+
+ public SerializedDataType serializedDataTypeConstant(Class> domainEventClass) {
+ final HasSerializedDataTypeConstant annotation = domainEventClass.getAnnotation(HasSerializedDataTypeConstant.class);
+ return HasSerializedDataTypeConstantValidator.extractValue(domainEventClass, annotation.value());
+ }
+
+}
diff --git a/api/src/test/java/org/fuin/esc/api/JandexSerializedDataTypeRegistryTest.java b/api/src/test/java/org/fuin/esc/api/JandexSerializedDataTypeRegistryTest.java
new file mode 100644
index 00000000..1083f1e8
--- /dev/null
+++ b/api/src/test/java/org/fuin/esc/api/JandexSerializedDataTypeRegistryTest.java
@@ -0,0 +1,26 @@
+package org.fuin.esc.api;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for the {@link JandexSerializedDataTypeRegistry} class.
+ */
+public class JandexSerializedDataTypeRegistryTest {
+
+ @Test
+ public void testCreate() {
+ final JandexSerializedDataTypeRegistry testee = new JandexSerializedDataTypeRegistry(new File("target/test-classes"));
+ assertThat(testee.getClasses()).containsOnly(MyEvent.class);
+ }
+
+ @Test
+ public void testFind() {
+ final JandexSerializedDataTypeRegistry testee = new JandexSerializedDataTypeRegistry(new File("target/test-classes"));
+ assertThat(testee.findClass(MyEvent.SER_TYPE)).isEqualTo(MyEvent.class);
+ }
+
+}
diff --git a/api/src/test/java/org/fuin/esc/api/MyEvent.java b/api/src/test/java/org/fuin/esc/api/MyEvent.java
index e59579b7..21dc7b9c 100644
--- a/api/src/test/java/org/fuin/esc/api/MyEvent.java
+++ b/api/src/test/java/org/fuin/esc/api/MyEvent.java
@@ -4,8 +4,11 @@
/**
* Example event.
*/
+@HasSerializedDataTypeConstant
public class MyEvent {
+ public static final SerializedDataType SER_TYPE = new SerializedDataType("MyEvent");
+
private String name;
public MyEvent(final String name) {
diff --git a/api/src/test/resources/logback-test.xml b/api/src/test/resources/logback-test.xml
new file mode 100644
index 00000000..6062cc03
--- /dev/null
+++ b/api/src/test/resources/logback-test.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ %d{yyyy.dd.MM HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 619c7cf8..63c9649e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -325,6 +325,12 @@
4.0.1
+
+ io.smallrye
+ jandex
+ 3.1.2
+
+
diff --git a/spi/src/main/java/org/fuin/esc/spi/Base64Data.java b/spi/src/main/java/org/fuin/esc/spi/Base64Data.java
index 2e8ca4d2..3c0679f0 100644
--- a/spi/src/main/java/org/fuin/esc/spi/Base64Data.java
+++ b/spi/src/main/java/org/fuin/esc/spi/Base64Data.java
@@ -32,6 +32,7 @@
/**
* Contains some Base64 encoded data.
*/
+@HasSerializedDataTypeConstant
@XmlRootElement(name = Base64Data.EL_ROOT_NAME)
public final class Base64Data implements ToJsonCapable {
diff --git a/spi/src/main/java/org/fuin/esc/spi/EscEvent.java b/spi/src/main/java/org/fuin/esc/spi/EscEvent.java
index 6f99e5ea..18cedf2f 100644
--- a/spi/src/main/java/org/fuin/esc/spi/EscEvent.java
+++ b/spi/src/main/java/org/fuin/esc/spi/EscEvent.java
@@ -39,6 +39,7 @@
/**
* An event structure.
*/
+@HasSerializedDataTypeConstant
@XmlRootElement(name = EscEvent.EL_ROOT_NAME)
public final class EscEvent implements ToJsonCapable {
diff --git a/spi/src/main/java/org/fuin/esc/spi/EscEvents.java b/spi/src/main/java/org/fuin/esc/spi/EscEvents.java
index f73a9385..6db7917c 100644
--- a/spi/src/main/java/org/fuin/esc/spi/EscEvents.java
+++ b/spi/src/main/java/org/fuin/esc/spi/EscEvents.java
@@ -40,6 +40,7 @@
/**
* A list of events.
*/
+@HasSerializedDataTypeConstant
@XmlRootElement(name = EscEvents.EL_ROOT_NAME)
public final class EscEvents implements ToJsonCapable {
diff --git a/spi/src/main/java/org/fuin/esc/spi/EscMeta.java b/spi/src/main/java/org/fuin/esc/spi/EscMeta.java
index f60d5e86..fc3ba014 100644
--- a/spi/src/main/java/org/fuin/esc/spi/EscMeta.java
+++ b/spi/src/main/java/org/fuin/esc/spi/EscMeta.java
@@ -40,6 +40,7 @@
/**
* A structure that contains the user's meta data and the system's meta information.
*/
+@HasSerializedDataTypeConstant
@XmlRootElement(name = EscMeta.EL_ROOT_NAME)
public final class EscMeta implements ToJsonCapable {
diff --git a/spi/src/test/java/org/fuin/esc/spi/JandexSerializedDataTypeRegistryTest.java b/spi/src/test/java/org/fuin/esc/spi/JandexSerializedDataTypeRegistryTest.java
new file mode 100644
index 00000000..2a269603
--- /dev/null
+++ b/spi/src/test/java/org/fuin/esc/spi/JandexSerializedDataTypeRegistryTest.java
@@ -0,0 +1,26 @@
+package org.fuin.esc.spi;
+
+import org.fuin.esc.api.JandexSerializedDataTypeRegistry;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for the {@link JandexSerializedDataTypeRegistry} class.
+ */
+public class JandexSerializedDataTypeRegistryTest {
+
+ @Test
+ public void testBaseDataTypes() {
+ final JandexSerializedDataTypeRegistry testee = new JandexSerializedDataTypeRegistry();
+ assertThat(testee.getClasses()).containsOnly(
+ Base64Data.class,
+ EscEvent.class,
+ EscEvents.class,
+ EscMeta.class
+ );
+ }
+
+}