diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 5b20da4b..f0810616 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -2,7 +2,7 @@ name: Java Maven Build on: push: - branches: + branches: - '**' paths-ignore: - '**/README.md' @@ -18,46 +18,46 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout source - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up JDK - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'zulu' - cache: maven - - - name: Show versions - run: java -version && ./mvnw -version && gpg --version - - - name: Cache SonarCloud packages - uses: actions/cache@v3 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - - name: Cache Maven packages - uses: actions/cache@v3 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5 - with: - gpg_private_key: ${{ secrets.OSS_SONATYPE_GPG_PRIVATE_KEY }} - passphrase: ${{ secrets.OSS_SONATYPE_GPG_PASSPHRASE }} - - - name: Build with Maven - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OSS_SONATYPE_USERNAME: michael-schnell - OSS_SONATYPE_TOKEN: ${{ secrets.OSS_SONATYPE_TOKEN }} - OSS_SONATYPE_GPG_PASSPHRASE: ${{ secrets.OSS_SONATYPE_GPG_PASSPHRASE }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: ./mvnw clean deploy jacoco:report sonar:sonar -U -B -P sonatype-oss-release --file pom.xml -s settings.xml + - name: Checkout source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'zulu' + cache: maven + + - name: Show versions + run: java -version && ./mvnw -version && gpg --version + + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Cache Maven packages + uses: actions/cache@v4 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.OSS_SONATYPE_GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.OSS_SONATYPE_GPG_PASSPHRASE }} + + - name: Build with Maven + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OSS_SONATYPE_USERNAME: michael-schnell + OSS_SONATYPE_TOKEN: ${{ secrets.OSS_SONATYPE_TOKEN }} + OSS_SONATYPE_GPG_PASSPHRASE: ${{ secrets.OSS_SONATYPE_GPG_PASSPHRASE }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: ./mvnw clean deploy jacoco:report sonar:sonar -U -B -P sonatype-oss-release --file pom.xml -s settings.xml diff --git a/api/src/main/java/org/fuin/esc/api/GenerateSerializedDataTypesRegistrationRequest.java b/api/src/main/java/org/fuin/esc/api/GenerateSerializedDataTypesRegistrationRequest.java new file mode 100644 index 00000000..2b9dbe03 --- /dev/null +++ b/api/src/main/java/org/fuin/esc/api/GenerateSerializedDataTypesRegistrationRequest.java @@ -0,0 +1,31 @@ +package org.fuin.esc.api; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Request to generate a class that implements {@link SerializedDataTypesRegistrationRequest}. + * Should only be placed on an interface as it will be used for the implementation with "implements". + */ +@Documented +@Target(ElementType.TYPE) +@Inherited +@Retention(RetentionPolicy.SOURCE) +public @interface GenerateSerializedDataTypesRegistrationRequest { + + /** + * Name of the class to generate. In case it's not set, the class will be named + * like annotated interface with an "Impl" extension. + * + * @return Simple class name. + */ + String name() default ""; + +} diff --git a/api/src/main/java/org/fuin/esc/api/SerializedDataTypesRegistrationRequest.java b/api/src/main/java/org/fuin/esc/api/SerializedDataTypesRegistrationRequest.java new file mode 100644 index 00000000..c4b04513 --- /dev/null +++ b/api/src/main/java/org/fuin/esc/api/SerializedDataTypesRegistrationRequest.java @@ -0,0 +1,18 @@ +package org.fuin.esc.api; + +import java.util.Set; + +/** + * Returns classes that are annotated with {@link HasSerializedDataTypeConstant} and should be + * included in the applications {@link SerializedDataTypeRegistry}. + */ +public interface SerializedDataTypesRegistrationRequest { + + /** + * Returns the class to type mappings that should be registered. + * + * @return Set of mappings. + */ + Set getMappingsToRegister(); + +} diff --git a/apt/pom.xml b/apt/pom.xml new file mode 100644 index 00000000..8cff0a1e --- /dev/null +++ b/apt/pom.xml @@ -0,0 +1,145 @@ + + + + 4.0.0 + + + org.fuin.esc + esc-parent + 0.8.0-SNAPSHOT + ../pom.xml + + + esc-apt + jar + Generation of source code with APT (Annotation Processing Tool) + + + + + + + com.google.auto.service + auto-service-annotations + + + + com.google.auto.service + auto-service + 1.1.1 + true + + + + + + org.fuin.esc + esc-api + + + + org.junit.jupiter + junit-jupiter + test + + + + io.toolisticon.cute + cute + 1.4.0 + test + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-source-plugin + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + org.jacoco + jacoco-maven-plugin + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.2 + + + package + + shade + + + true + + + com.google.auto.common + org.fuin.esc.apt.auto.common + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + **/* + + + + org.fuin.esc.apt + + + + + + + org.apache.maven.plugins + maven-jdeps-plugin + + + + org.apache.maven.plugins + maven-dependency-plugin + + + com.tngtech.archunit:archunit-junit5 + org.junit.jupiter:junit-jupiter + com.google.auto.service:auto-service + org.fuin.esc:esc-api + + + com.tngtech.archunit:archunit-junit5-api + org.junit.jupiter:junit-jupiter-api + + + + + + + + + diff --git a/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypeProcessor.java b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypeProcessor.java new file mode 100644 index 00000000..a2c10bf2 --- /dev/null +++ b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypeProcessor.java @@ -0,0 +1,169 @@ +package org.fuin.esc.apt; + +import com.google.auto.service.AutoService; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Creates a "SerializedDataTypesRegistrationRequest" containing all types with a "HasSerializedDataTypeConstant" annotation. + */ +@AutoService(Processor.class) +@SupportedSourceVersion(SourceVersion.RELEASE_17) +@SupportedAnnotationTypes({ + SerializedDataTypeProcessor.HAS_SERIALIZED_DATA_TYPE_CONSTANT, + SerializedDataTypeProcessor.GENERATE_SERIALIZED_DATA_TYPES_REGISTRATION_REQUEST +}) +public class SerializedDataTypeProcessor extends AbstractProcessor { + + static final String HAS_SERIALIZED_DATA_TYPE_CONSTANT = "org.fuin.esc.api.HasSerializedDataTypeConstant"; + + private static final String GENERATOR_ANNOTATION = "GenerateSerializedDataTypesRegistrationRequest"; + + static final String GENERATE_SERIALIZED_DATA_TYPES_REGISTRATION_REQUEST = "org.fuin.esc.api." + GENERATOR_ANNOTATION; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!annotations.isEmpty()) { + Set targets = null; + Set results = null; + for (final TypeElement annotation : annotations) { + final Set annotatedElements = roundEnv.getElementsAnnotatedWith(annotation); + final String fqnAnnotationName = annotation.getQualifiedName().toString(); + if (fqnAnnotationName.equals(HAS_SERIALIZED_DATA_TYPE_CONSTANT)) { + ensureHasNotBeenSetBefore("results", results); + results = processHasSerializedDataTypeConstant(annotatedElements); + } else if (fqnAnnotationName.equals(GENERATE_SERIALIZED_DATA_TYPES_REGISTRATION_REQUEST)) { + ensureHasNotBeenSetBefore("targets", targets); + targets = processGenerateSerializedDataTypesRegistrationRequest(annotatedElements); + } + } + SerializedDataTypesRegistrationRequestWriter.write(processingEnv, targets, results); + } + return true; + } + + private void ensureHasNotBeenSetBefore(String name, Object value) { + if (value != null) { + throw new IllegalStateException(name + " has already been set before: " + value); + } + } + + private Set processHasSerializedDataTypeConstant(Set annotatedElements) { + final Set results = new HashSet<>(); + for (final Element element : annotatedElements) { + logInfo("Process: " + element); + final AnnotationMirror annotationMirror = findAnnotationMirror(element, HAS_SERIALIZED_DATA_TYPE_CONSTANT); + if (annotationMirror == null) { + logError("Failed to find annotation: " + HAS_SERIALIZED_DATA_TYPE_CONSTANT); + } else { + final AnnotationValue annotationValue = findAnnotationValue(annotationMirror, "value"); + if (annotationValue == null) { + logError("Failed to find annotation 'value()': " + HAS_SERIALIZED_DATA_TYPE_CONSTANT); + } else { + final String fieldName = (String) annotationValue.getValue(); + PackageElement pkg = processingEnv.getElementUtils().getPackageOf(element); + final String packageName = pkg.getQualifiedName().toString(); + final String className = element.getSimpleName().toString(); + if (pkg.isUnnamed()) { + throw new IllegalStateException("The default package is not allowed for generated classes: " + className); + } + results.add(new SerializedDataTypeResult(packageName, className, fieldName)); + } + } + } + return results; + } + + private Set processGenerateSerializedDataTypesRegistrationRequest(final Set annotatedElements) { + final Set results = new HashSet<>(); + for (final Element element : annotatedElements) { + logInfo("Process: " + element); + if (element.getKind().isInterface()) { + final AnnotationMirror annotationMirror = findAnnotationMirror(element, GENERATE_SERIALIZED_DATA_TYPES_REGISTRATION_REQUEST); + if (annotationMirror == null) { + logError("Failed to find annotation: " + GENERATE_SERIALIZED_DATA_TYPES_REGISTRATION_REQUEST); + } else { + final String simpleInterfaceName = element.getSimpleName().toString(); + final AnnotationValue annotationValue = findAnnotationValue(annotationMirror, "name"); + if (annotationValue == null) { + logError("Failed to find annotation 'name()': " + GENERATE_SERIALIZED_DATA_TYPES_REGISTRATION_REQUEST); + } else { + results.add(createAnnotation(element, annotationValue, simpleInterfaceName)); + } + } + } else { + logError("Expected type annotated with '@" + GENERATOR_ANNOTATION + "' to be an interface, but was not: " + element); + } + } + return results; + } + + private SerializedDataTypesRegistrationRequestTarget createAnnotation(Element element, AnnotationValue annotationValue, String simpleInterfaceName) { + final String simpleClassName = simpleClassName(annotationValue, simpleInterfaceName); + final String packageName = packageName(element); + return new SerializedDataTypesRegistrationRequestTarget(packageName, simpleClassName, simpleInterfaceName); + } + + private String packageName(Element element) { + final PackageElement pkg = processingEnv.getElementUtils().getPackageOf(element); + if (pkg.isUnnamed()) { + throw new IllegalStateException("The default package is not allowed for generated classes: " + element); + } + return pkg.getQualifiedName().toString(); + } + + private static String simpleClassName(AnnotationValue annotationValue, String simpleInterfaceName) { + final String nameStr = (String) annotationValue.getValue(); + if (nameStr.isEmpty()) { + return simpleInterfaceName + "Impl"; + } + return (String) annotationValue.getValue(); + } + + private AnnotationValue findAnnotationValue(AnnotationMirror annotationMirror, String methodName) { + for (final Map.Entry entry : getElementValuesWithDefaults(annotationMirror).entrySet()) { + if (entry.getKey().getSimpleName().toString().equals(methodName)) { + return entry.getValue(); + } + } + return null; + } + + private Map getElementValuesWithDefaults(AnnotationMirror annotationMirror) { + return processingEnv.getElementUtils().getElementValuesWithDefaults(annotationMirror); + } + + private AnnotationMirror findAnnotationMirror(final Element element, final String annotationClassName) { + for (final AnnotationMirror mirror : element.getAnnotationMirrors()) { + if (mirror.getAnnotationType().toString().equals(annotationClassName)) { + return mirror; + } + } + return null; + } + + private void logInfo(String message) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message); + } + + private void logError(String message) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message); + } + + +} \ No newline at end of file diff --git a/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypeResult.java b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypeResult.java new file mode 100644 index 00000000..992e8ba6 --- /dev/null +++ b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypeResult.java @@ -0,0 +1,32 @@ +package org.fuin.esc.apt; + +/** + * Type found during annotation processing. + * + * @param packageName Name of the package where the class is located. + * @param simpleClassName Simple class name. + * @param fieldName Name of the static field. + */ +public record SerializedDataTypeResult(String packageName, String simpleClassName, String fieldName) { + + public SerializedDataTypeResult { + if (packageName == null || packageName.isEmpty()) { + throw new IllegalArgumentException("packageName cannot be null or empty: " + packageName); + } + if (simpleClassName == null || simpleClassName.isEmpty()) { + throw new IllegalArgumentException("simpleClassName cannot be null or empty: " + simpleClassName); + } + if (fieldName == null || fieldName.isEmpty()) { + throw new IllegalArgumentException("fieldName cannot be null or empty: " + fieldName); + } + } + + public String getFullClassName() { + return packageName + "." + simpleClassName; + } + + @Override + public String toString() { + return "new SerializedDataType2ClassMapping(" + simpleClassName + "." + fieldName + ", " + simpleClassName + ".class)"; + } +} diff --git a/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypesRegistrationRequestTarget.java b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypesRegistrationRequestTarget.java new file mode 100644 index 00000000..fef7f541 --- /dev/null +++ b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypesRegistrationRequestTarget.java @@ -0,0 +1,29 @@ +package org.fuin.esc.apt; + +/** + * Defines the data necessary to create a source file. + * + * @param packageName Name of the package where to create the class. + * @param simpleClassName Name of the class to create. + * @param simpleInterfaceName Interface the class should implement. + */ +public record SerializedDataTypesRegistrationRequestTarget(String packageName, String simpleClassName, String simpleInterfaceName) { + + public SerializedDataTypesRegistrationRequestTarget { + if (packageName == null || packageName.isEmpty()) { + throw new IllegalArgumentException("packageName cannot be null or empty: " + packageName); + } + if (simpleClassName == null || simpleClassName.isEmpty()) { + throw new IllegalArgumentException("simpleClassName cannot be null or empty: " + simpleClassName); + } + if (simpleInterfaceName == null || simpleInterfaceName.isEmpty()) { + throw new IllegalArgumentException("simpleInterfacefName cannot be null or empty: " + simpleInterfaceName); + } + } + + @Override + public String toString() { + return packageName + "." + simpleClassName; + } + +} diff --git a/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypesRegistrationRequestWriter.java b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypesRegistrationRequestWriter.java new file mode 100644 index 00000000..3eb2b11d --- /dev/null +++ b/apt/src/main/java/org/fuin/esc/apt/SerializedDataTypesRegistrationRequestWriter.java @@ -0,0 +1,79 @@ +package org.fuin.esc.apt; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.tools.JavaFileObject; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Creates the source code for + */ +public final class SerializedDataTypesRegistrationRequestWriter { + + private SerializedDataTypesRegistrationRequestWriter() { + } + + public static void write(final ProcessingEnvironment processingEnv, final Set targets, final Set results) { + if (targets == null || results == null) { + return; + } + for (final SerializedDataTypesRegistrationRequestTarget target : targets) { + write(processingEnv, target, results); + } + } + + public static void write(final ProcessingEnvironment processingEnv, final SerializedDataTypesRegistrationRequestTarget target, final Set results) { + try { + final JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(target.toString()); + try (final PrintWriter out = new PrintWriter(builderFile.openWriter())) { + out.write(createSource(target, results)); + } + } catch (final IOException ex) { + throw new IllegalStateException("Failed to write:" + target, ex); + } + } + + private static String createSource(final SerializedDataTypesRegistrationRequestTarget target, final Set results) { + final StringBuilder sb = new StringBuilder(); + if (target.packageName() != null) { + sb.append("package ").append(target.packageName()).append(";\n\n"); + } + results.forEach(result -> { + if (result.packageName() != null) { + sb.append("import ").append(result.getFullClassName()).append(";\n"); + } + }); + sb.append(""" + import com.google.auto.service.AutoService; + import org.fuin.esc.api.SerializedDataType2ClassMapping; + import org.fuin.esc.api.SerializedDataTypesRegistrationRequest; + import java.util.Set; + + /** + * Request to register {@link org.fuin.esc.api.SerializedDataType} to class mappings. + */ + @AutoService(SerializedDataTypesRegistrationRequest.class) + public class ${className} implements SerializedDataTypesRegistrationRequest, ${interfaceName} { + + @Override + public Set getMappingsToRegister() { + return Set.of(${entries}); + } + + } + """ + .replace("${className}", target.simpleClassName()) + .replace("${interfaceName}", target.simpleInterfaceName()) + .replace("${entries}", asString(results))); + return sb.toString(); + } + + private static String asString(final Set results) { + return results.stream() + .map(SerializedDataTypeResult::toString) + .collect(Collectors.joining(", ")); + } + +} diff --git a/apt/src/test/java/org/fuin/esc/apt/SerializedDataTypeProcessorTest.java b/apt/src/test/java/org/fuin/esc/apt/SerializedDataTypeProcessorTest.java new file mode 100644 index 00000000..615ecfbe --- /dev/null +++ b/apt/src/test/java/org/fuin/esc/apt/SerializedDataTypeProcessorTest.java @@ -0,0 +1,88 @@ +package org.fuin.esc.apt; + +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.CuteApi; +import io.toolisticon.cute.JavaFileObjectUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class SerializedDataTypeProcessorTest { + + private CuteApi.BlackBoxTestSourceFilesInterface compileTestBuilder; + + @BeforeEach + public void init() { + compileTestBuilder = Cute.blackBoxTest().given().processors(SerializedDataTypeProcessor.class); + } + + @Test + public void testYourProcessor() { + compileTestBuilder + .andSourceFiles("DefaultValueTestClass.java", + "DedicatedValueTestClass.java", + "DefaultNameGenerateTestClass.java", + "DedicatedNameGenerateTestClass.java") + .whenCompiled() + .thenExpectThat() + .compilationSucceeds() + .andThat() + .generatedSourceFile("org.fuin.esc.apt.gen.AnotherName").equals(JavaFileObjectUtils.readFromString(anotherNameSource())) + .andThat() + .generatedSourceFile("org.fuin.esc.apt.gen.DefaultNameGenerateTestClassImpl").equals(JavaFileObjectUtils.readFromString(defaultNameGenerateTestClassImplSource())) + .executeTest(); + } + + private static String anotherNameSource() { + return """ + package org.fuin.esc.apt.gen; + + import org.fuin.esc.apt.demo.DedicatedValueTestClass; + import org.fuin.esc.apt.demo2.DefaultValueTestClass; + import com.google.auto.service.AutoService; + import org.fuin.esc.api.SerializedDataType2ClassMapping; + import org.fuin.esc.api.SerializedDataTypesRegistrationRequest; + import java.util.Set; + + /** + * Request to register {@link org.fuin.esc.api.SerializedDataType} to class mappings. + */ + @AutoService(SerializedDataTypesRegistrationRequest.class) + public class AnotherName implements SerializedDataTypesRegistrationRequest, DedicatedNameGenerateTestClass { + + @Override + public Set getMappingsToRegister() { + return Set.of(new SerializedDataType2ClassMapping(DedicatedValueTestClass.THE_TYPE, DedicatedValueTestClass.class), new SerializedDataType2ClassMapping(DefaultValueTestClass.SER_TYPE, DefaultValueTestClass.class)); + } + + } + """; + } + + private static String defaultNameGenerateTestClassImplSource() { + return """ + package org.fuin.esc.apt.gen; + + import org.fuin.esc.apt.demo.DedicatedValueTestClass; + import org.fuin.esc.apt.demo2.DefaultValueTestClass; + import com.google.auto.service.AutoService; + import org.fuin.esc.api.SerializedDataType2ClassMapping; + import org.fuin.esc.api.SerializedDataTypesRegistrationRequest; + import java.util.Set; + + /** + * Request to register {@link org.fuin.esc.api.SerializedDataType} to class mappings. + */ + @AutoService(SerializedDataTypesRegistrationRequest.class) + public class DefaultNameGenerateTestClassImpl implements SerializedDataTypesRegistrationRequest, DefaultNameGenerateTestClass { + + @Override + public Set getMappingsToRegister() { + return Set.of(new SerializedDataType2ClassMapping(DedicatedValueTestClass.THE_TYPE, DedicatedValueTestClass.class), new SerializedDataType2ClassMapping(DefaultValueTestClass.SER_TYPE, DefaultValueTestClass.class)); + } + + } + """; + } + + +} diff --git a/apt/src/test/resources/DedicatedNameGenerateTestClass.java b/apt/src/test/resources/DedicatedNameGenerateTestClass.java new file mode 100644 index 00000000..47728d32 --- /dev/null +++ b/apt/src/test/resources/DedicatedNameGenerateTestClass.java @@ -0,0 +1,7 @@ +package org.fuin.esc.apt.gen; + +import org.fuin.esc.api.GenerateSerializedDataTypesRegistrationRequest; + +@GenerateSerializedDataTypesRegistrationRequest(name = "AnotherName") +public interface DedicatedNameGenerateTestClass { +} diff --git a/apt/src/test/resources/DedicatedValueTestClass.java b/apt/src/test/resources/DedicatedValueTestClass.java new file mode 100644 index 00000000..2ffe6d29 --- /dev/null +++ b/apt/src/test/resources/DedicatedValueTestClass.java @@ -0,0 +1,11 @@ +package org.fuin.esc.apt.demo; + +import org.fuin.esc.api.HasSerializedDataTypeConstant; +import org.fuin.esc.api.SerializedDataType; + +@HasSerializedDataTypeConstant("THE_TYPE") +public class DedicatedValueTestClass { + + public static final SerializedDataType THE_TYPE = new SerializedDataType("XYZ"); + +} diff --git a/apt/src/test/resources/DefaultNameGenerateTestClass.java b/apt/src/test/resources/DefaultNameGenerateTestClass.java new file mode 100644 index 00000000..5f7d1b5e --- /dev/null +++ b/apt/src/test/resources/DefaultNameGenerateTestClass.java @@ -0,0 +1,7 @@ +package org.fuin.esc.apt.gen; + +import org.fuin.esc.api.GenerateSerializedDataTypesRegistrationRequest; + +@GenerateSerializedDataTypesRegistrationRequest +public interface DefaultNameGenerateTestClass { +} diff --git a/apt/src/test/resources/DefaultValueTestClass.java b/apt/src/test/resources/DefaultValueTestClass.java new file mode 100644 index 00000000..b36a8631 --- /dev/null +++ b/apt/src/test/resources/DefaultValueTestClass.java @@ -0,0 +1,11 @@ +package org.fuin.esc.apt.demo2; + +import org.fuin.esc.api.HasSerializedDataTypeConstant; +import org.fuin.esc.api.SerializedDataType; + +@HasSerializedDataTypeConstant +public class DefaultValueTestClass { + + public static final SerializedDataType SER_TYPE = new SerializedDataType("XYZ"); + +} diff --git a/client/src/main/java/org/fuin/esc/client/JandexSerializedDataTypeRegistry.java b/client/src/main/java/org/fuin/esc/client/JandexSerializedDataTypeRegistry.java index 8e81cf91..d90aebfb 100644 --- a/client/src/main/java/org/fuin/esc/client/JandexSerializedDataTypeRegistry.java +++ b/client/src/main/java/org/fuin/esc/client/JandexSerializedDataTypeRegistry.java @@ -29,7 +29,7 @@ * Registry that is built up by scanning for classes that are annotated with {@link HasSerializedDataTypeConstant}. * Inner classes are ignored. */ -public class JandexSerializedDataTypeRegistry implements SerializedDataTypeRegistry { +public final class JandexSerializedDataTypeRegistry implements SerializedDataTypeRegistry { private static final Logger LOG = LoggerFactory.getLogger(JandexSerializedDataTypeRegistry.class); diff --git a/jacoco/pom.xml b/jacoco/pom.xml index 1b4d81de..980788bf 100644 --- a/jacoco/pom.xml +++ b/jacoco/pom.xml @@ -55,6 +55,11 @@ esc-esgrpc + + org.fuin.esc + esc-apt + + org.fuin.esc esc-test diff --git a/jaxb/pom.xml b/jaxb/pom.xml index d6e26998..be82613c 100644 --- a/jaxb/pom.xml +++ b/jaxb/pom.xml @@ -65,6 +65,17 @@ jakarta.activation-api + + com.google.auto.service + auto-service-annotations + + + + com.google.auto.service + auto-service + true + + @@ -185,6 +196,7 @@ ch.qos.logback:logback-classic com.tngtech.archunit:archunit-junit5 org.junit.jupiter:junit-jupiter + com.google.auto.service:auto-service com.tngtech.archunit:archunit-junit5-api diff --git a/jaxb/src/main/java/org/fuin/esc/jaxb/JaxbSerializedDataTypesRegistrationRequest.java b/jaxb/src/main/java/org/fuin/esc/jaxb/JaxbSerializedDataTypesRegistrationRequest.java new file mode 100644 index 00000000..d430146d --- /dev/null +++ b/jaxb/src/main/java/org/fuin/esc/jaxb/JaxbSerializedDataTypesRegistrationRequest.java @@ -0,0 +1,29 @@ +package org.fuin.esc.jaxb; + +import com.google.auto.service.AutoService; +import org.fuin.esc.api.IBase64Data; +import org.fuin.esc.api.IEscEvent; +import org.fuin.esc.api.IEscEvents; +import org.fuin.esc.api.IEscMeta; +import org.fuin.esc.api.SerializedDataType2ClassMapping; +import org.fuin.esc.api.SerializedDataTypesRegistrationRequest; + +import java.util.Set; + +/** + * Request to register the JAX-B related {@link org.fuin.esc.api.SerializedDataType} to class mappings. + */ +@AutoService(SerializedDataTypesRegistrationRequest.class) +public class JaxbSerializedDataTypesRegistrationRequest implements SerializedDataTypesRegistrationRequest { + + @Override + public Set getMappingsToRegister() { + return Set.of( + new SerializedDataType2ClassMapping(IBase64Data.SER_TYPE, Base64Data.class), + new SerializedDataType2ClassMapping(IEscEvent.SER_TYPE, EscEvent.class), + new SerializedDataType2ClassMapping(IEscEvents.SER_TYPE, EscEvents.class), + new SerializedDataType2ClassMapping(IEscMeta.SER_TYPE, EscMeta.class) + ); + } + +} diff --git a/jaxb/src/test/java/org/fuin/esc/jaxb/ArchitectureTest.java b/jaxb/src/test/java/org/fuin/esc/jaxb/ArchitectureTest.java index f0365b03..43bb2d8a 100644 --- a/jaxb/src/test/java/org/fuin/esc/jaxb/ArchitectureTest.java +++ b/jaxb/src/test/java/org/fuin/esc/jaxb/ArchitectureTest.java @@ -57,7 +57,8 @@ class ArchitectureTest { "org.fuin.objects4j.core..", "org.fuin.utils4j..", "org.slf4j..", - "org.w3c.dom.." + "org.w3c.dom..", + "com.google.auto.service.." ); diff --git a/jaxb/src/test/java/org/fuin/esc/jaxb/JaxbSerializedDataTypesRegistrationRequestTest.java b/jaxb/src/test/java/org/fuin/esc/jaxb/JaxbSerializedDataTypesRegistrationRequestTest.java new file mode 100644 index 00000000..bcf38641 --- /dev/null +++ b/jaxb/src/test/java/org/fuin/esc/jaxb/JaxbSerializedDataTypesRegistrationRequestTest.java @@ -0,0 +1,25 @@ +package org.fuin.esc.jaxb; + +import org.fuin.esc.api.SerializedDataTypesRegistrationRequest; +import org.junit.jupiter.api.Test; + +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for the {@link JaxbSerializedDataTypesRegistrationRequest} class. + */ +public class JaxbSerializedDataTypesRegistrationRequestTest { + + @Test + void testGetService() { + final ServiceLoader loader = ServiceLoader.load(SerializedDataTypesRegistrationRequest.class); + final Optional services = StreamSupport.stream(loader.spliterator(), false).findFirst(); + assertThat(services).isNotEmpty(); + assertThat(services.get()).isInstanceOf(JaxbSerializedDataTypesRegistrationRequest.class); + } + +} diff --git a/jsonb/pom.xml b/jsonb/pom.xml index b003a009..5d424bfe 100644 --- a/jsonb/pom.xml +++ b/jsonb/pom.xml @@ -65,6 +65,17 @@ jakarta.activation-api + + com.google.auto.service + auto-service-annotations + + + + com.google.auto.service + auto-service + true + + @@ -192,6 +203,7 @@ ch.qos.logback:logback-classic com.tngtech.archunit:archunit-junit5 org.junit.jupiter:junit-jupiter + com.google.auto.service:auto-service com.tngtech.archunit:archunit-junit5-api diff --git a/jsonb/src/main/java/org/fuin/esc/jsonb/JsonbSerializedDataTypesRegistrationRequest.java b/jsonb/src/main/java/org/fuin/esc/jsonb/JsonbSerializedDataTypesRegistrationRequest.java new file mode 100644 index 00000000..177f8228 --- /dev/null +++ b/jsonb/src/main/java/org/fuin/esc/jsonb/JsonbSerializedDataTypesRegistrationRequest.java @@ -0,0 +1,29 @@ +package org.fuin.esc.jsonb; + +import com.google.auto.service.AutoService; +import org.fuin.esc.api.IBase64Data; +import org.fuin.esc.api.IEscEvent; +import org.fuin.esc.api.IEscEvents; +import org.fuin.esc.api.IEscMeta; +import org.fuin.esc.api.SerializedDataType2ClassMapping; +import org.fuin.esc.api.SerializedDataTypesRegistrationRequest; + +import java.util.Set; + +/** + * Request to register the JSON-B related {@link org.fuin.esc.api.SerializedDataType} to class mappings. + */ +@AutoService(SerializedDataTypesRegistrationRequest.class) +public class JsonbSerializedDataTypesRegistrationRequest implements SerializedDataTypesRegistrationRequest { + + @Override + public Set getMappingsToRegister() { + return Set.of( + new SerializedDataType2ClassMapping(IBase64Data.SER_TYPE, Base64Data.class), + new SerializedDataType2ClassMapping(IEscEvent.SER_TYPE, EscEvent.class), + new SerializedDataType2ClassMapping(IEscEvents.SER_TYPE, EscEvents.class), + new SerializedDataType2ClassMapping(IEscMeta.SER_TYPE, EscMeta.class) + ); + } + +} diff --git a/jsonb/src/test/java/org/fuin/esc/jsonb/ArchitectureTest.java b/jsonb/src/test/java/org/fuin/esc/jsonb/ArchitectureTest.java index 35941842..782260ac 100644 --- a/jsonb/src/test/java/org/fuin/esc/jsonb/ArchitectureTest.java +++ b/jsonb/src/test/java/org/fuin/esc/jsonb/ArchitectureTest.java @@ -55,7 +55,8 @@ class ArchitectureTest { "org.slf4j..", "jakarta.json..", "org.apache.commons.lang3..", - "org.fuin.utils4j.." + "org.fuin.utils4j..", + "com.google.auto.service.." ); diff --git a/jsonb/src/test/java/org/fuin/esc/jsonb/JsonbSerializedDataTypesRegistrationRequestTest.java b/jsonb/src/test/java/org/fuin/esc/jsonb/JsonbSerializedDataTypesRegistrationRequestTest.java new file mode 100644 index 00000000..8930b443 --- /dev/null +++ b/jsonb/src/test/java/org/fuin/esc/jsonb/JsonbSerializedDataTypesRegistrationRequestTest.java @@ -0,0 +1,25 @@ +package org.fuin.esc.jsonb; + +import org.fuin.esc.api.SerializedDataTypesRegistrationRequest; +import org.junit.jupiter.api.Test; + +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for the {@link JsonbSerializedDataTypesRegistrationRequest} class. + */ +public class JsonbSerializedDataTypesRegistrationRequestTest { + + @Test + void testGetService() { + final ServiceLoader loader = ServiceLoader.load(SerializedDataTypesRegistrationRequest.class); + final Optional services = StreamSupport.stream(loader.spliterator(), false).findFirst(); + assertThat(services).isNotEmpty(); + assertThat(services.get()).isInstanceOf(JsonbSerializedDataTypesRegistrationRequest.class); + } + +} diff --git a/pom.xml b/pom.xml index ab4bf5cd..7a535c2e 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,12 @@ ${project.version} + + org.fuin.esc + esc-apt + ${project.version} + + org.fuin utils4j @@ -340,6 +346,18 @@ 2.1.2 + + com.google.auto.service + auto-service-annotations + 1.1.1 + + + + com.google.auto.service + auto-service + 1.1.1 + + @@ -354,6 +372,7 @@ jsonb esgrpc test + apt jacoco diff --git a/test/pom.xml b/test/pom.xml index 8c8e751c..390a8762 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -46,7 +46,6 @@ jakarta.annotation-api - org.fuin.objects4j objects4j-common