From 96bda0871f123dbf39c423662587f5b174a74359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:15:00 +0000 Subject: [PATCH 1/6] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.7 to 3.2.8 Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.7 to 3.2.8. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.7...maven-gpg-plugin-3.2.8) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-version: 3.2.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1f1e72c1..d86d9b6d 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ 3.5.0 3.5.3 2.27 - 3.2.7 + 3.2.8 3.4.2 ${java.version} 1.2.2 From ac3bc6786653b7c1b94add36dd294954ed4c322d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:49:45 +0000 Subject: [PATCH 2/6] Bump org.apache.maven.plugins:maven-enforcer-plugin from 3.5.0 to 3.6.0 Bumps [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.5.0...enforcer-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-version: 3.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d86d9b6d..5d42e813 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ 3.6.0 3.14.0 3.1.4 - 3.5.0 + 3.6.0 3.5.3 2.27 3.2.8 From 2403d1adf0ecfc2a7fc6435ecfdc14e5d64da41d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:25:07 +0000 Subject: [PATCH 3/6] Bump version.org.junit.jupiter from 5.13.2 to 5.13.3 Bumps `version.org.junit.jupiter` from 5.13.2 to 5.13.3. Updates `org.junit.jupiter:junit-jupiter-api` from 5.13.2 to 5.13.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.2...r5.13.3) Updates `org.junit.jupiter:junit-jupiter-engine` from 5.13.2 to 5.13.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.2...r5.13.3) Updates `org.junit.jupiter:junit-jupiter-params` from 5.13.2 to 5.13.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.2...r5.13.3) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5d42e813..a1d961ad 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ 3.1.1 1.5.2 3.27.3 - 5.13.2 + 5.13.3 5.18.0 2.0.17 9.0.1.Final From f6caef49ec3cefbd9bf251cfe32e46f46325c5a8 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 10 Jul 2025 18:31:14 +0200 Subject: [PATCH 4/6] [Fix #634] Refactoring modules Signed-off-by: fjtirado --- api/pom.xml | 70 ++----------------- custom-generator/pom.xml | 6 ++ .../generator/AllAnyOneOfSchemaRule.java | 17 +++-- .../generator/GeneratorUtils.java | 9 --- .../generator/UnevaluatedPropertiesRule.java | 3 +- pom.xml | 2 + serverlessworkflow-annotations/pom.xml | 20 ++++++ .../annotations}/OneOfSetter.java | 2 +- .../annotations}/OneOfValueProvider.java | 2 +- .../serialization/DeserializeHelper.java | 1 + .../serialization/SerializeHelper.java | 4 +- serverlessworkflow-types/pom.xml | 66 +++++++++++++++++ .../src/main/resources/schema/workflow.yaml | 0 13 files changed, 116 insertions(+), 86 deletions(-) create mode 100644 serverlessworkflow-annotations/pom.xml rename {api/src/main/java/io/serverlessworkflow/serialization => serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations}/OneOfSetter.java (95%) rename {api/src/main/java/io/serverlessworkflow/api => serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations}/OneOfValueProvider.java (94%) rename {api => serverlessworkflow-annotations}/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java (98%) rename {api => serverlessworkflow-annotations}/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java (91%) create mode 100644 serverlessworkflow-types/pom.xml rename {api => serverlessworkflow-types}/src/main/resources/schema/workflow.yaml (100%) diff --git a/api/pom.xml b/api/pom.xml index 2b08c827..e2bf61fa 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -14,29 +14,23 @@ - org.slf4j - slf4j-api + io.serverlessworkflow + serverlessworkflow-types + ${project.version} - com.fasterxml.jackson.core - jackson-core + org.slf4j + slf4j-api com.networknt json-schema-validator - - com.fasterxml.jackson.core - jackson-databind - com.fasterxml.jackson.dataformat jackson-dataformat-yaml - - jakarta.validation - jakarta.validation-api - + org.hibernate.validator hibernate-validator @@ -79,54 +73,4 @@ test - - - - - - org.jsonschema2pojo - jsonschema2pojo-maven-plugin - - ${basedir}/src/main/resources/schema - - - yamlschema - io.serverlessworkflow.api.types - ${project.build.directory}/generated-sources/src/main/java - true - true - true - true - false - false - true - true - true - true - ${java.version} - true - true - io.serverlessworkflow.generator.UnreferencedFactory - io.serverlessworkflow.generator.ConstAnnotator - - - - io.serverlessworkflow - serverless-workflow-custom-generator - ${project.version} - - - - - - generate - - generate-sources - - - - - - + diff --git a/custom-generator/pom.xml b/custom-generator/pom.xml index 40bf6b11..55b2215b 100644 --- a/custom-generator/pom.xml +++ b/custom-generator/pom.xml @@ -12,6 +12,12 @@ org.jsonschema2pojo jsonschema2pojo-core + + io.serverlessworkflow + serverlessworkflow-annotations + ${project.version} + + diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java index 28a611cb..6221a560 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java @@ -33,6 +33,10 @@ import com.sun.codemodel.JPackage; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; +import io.serverlessworkflow.annotations.OneOfSetter; +import io.serverlessworkflow.annotations.OneOfValueProvider; +import io.serverlessworkflow.serialization.DeserializeHelper; +import io.serverlessworkflow.serialization.SerializeHelper; import jakarta.validation.ConstraintViolationException; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -318,10 +322,7 @@ private JDefinedClass populateOneOf( null); definedClass._implements( - definedClass - .owner() - .ref(GeneratorUtils.ONE_OF_VALUE_PROVIDER_INTERFACE_NAME) - .narrow(valueField.type())); + definedClass.owner().ref(OneOfValueProvider.class).narrow(valueField.type())); GeneratorUtils.implementInterface(definedClass, valueField); try { JDefinedClass serializer = generateSerializer(definedClass); @@ -397,9 +398,7 @@ private JDefinedClass generateSerializer(JDefinedClass relatedClass) (method, valueParam, genParam) -> method .body() - .staticInvoke( - definedClass.owner().ref(GeneratorUtils.SERIALIZE_HELPER_NAME), - "serializeOneOf") + .staticInvoke(definedClass.owner().ref(SerializeHelper.class), "serializeOneOf") .arg(genParam) .arg(valueParam)); return definedClass; @@ -418,7 +417,7 @@ private JDefinedClass generateDeserializer( body._return( definedClass .owner() - .ref(GeneratorUtils.DESERIALIZE_HELPER_NAME) + .ref(DeserializeHelper.class) .staticInvoke(methodName) .arg(parserParam) .arg(relatedClass.dotclass()) @@ -460,7 +459,7 @@ private JVar setupMethod( v -> { method.body().assign(JExpr._this().ref(v), methodParam); method - .annotate(definedClass.owner().ref(GeneratorUtils.SETTER_ANNOTATION_NAME)) + .annotate(definedClass.owner().ref(OneOfSetter.class)) .param("value", instanceField.type()); }); return methodParam; diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java index e7af60d5..b98bd7be 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java @@ -32,15 +32,6 @@ public class GeneratorUtils { - public static final String SERIALIZE_HELPER_NAME = - "io.serverlessworkflow.serialization.SerializeHelper"; - public static final String DESERIALIZE_HELPER_NAME = - "io.serverlessworkflow.serialization.DeserializeHelper"; - public static final String ONE_OF_VALUE_PROVIDER_INTERFACE_NAME = - "io.serverlessworkflow.api.OneOfValueProvider"; - public static final String SETTER_ANNOTATION_NAME = - "io.serverlessworkflow.serialization.OneOfSetter"; - @FunctionalInterface public interface SerializerFiller { void accept(JMethod method, JVar valueParam, JVar genParam); diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java index 18bdbba6..533db957 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java @@ -27,6 +27,7 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JType; +import io.serverlessworkflow.serialization.DeserializeHelper; import org.jsonschema2pojo.Schema; import org.jsonschema2pojo.rules.AdditionalPropertiesRule; import org.jsonschema2pojo.rules.Rule; @@ -125,7 +126,7 @@ private JDefinedClass generateDeserializer(JDefinedClass relatedClass, JType pro ._return( definedClass .owner() - .ref(GeneratorUtils.DESERIALIZE_HELPER_NAME) + .ref(DeserializeHelper.class) .staticInvoke("deserializeItem") .arg(parserParam) .arg(relatedClass.dotclass()) diff --git a/pom.xml b/pom.xml index a1d961ad..271e10da 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,8 @@ api custom-generator impl + serverlessworkflow-types + serverlessworkflow-annotations diff --git a/serverlessworkflow-annotations/pom.xml b/serverlessworkflow-annotations/pom.xml new file mode 100644 index 00000000..e37aa671 --- /dev/null +++ b/serverlessworkflow-annotations/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + Serverless Workflow :: Annotations + serverlessworkflow-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + jakarta.validation + jakarta.validation-api + + + \ No newline at end of file diff --git a/api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java similarity index 95% rename from api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java index 098df425..d67f0292 100644 --- a/api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.serialization; +package io.serverlessworkflow.annotations; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java similarity index 94% rename from api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java index 9d17b872..d275ff9d 100644 --- a/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api; +package io.serverlessworkflow.annotations; public interface OneOfValueProvider { T get(); diff --git a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java similarity index 98% rename from api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java index cfbd54ca..041474d9 100644 --- a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.JsonMappingException; +import io.serverlessworkflow.annotations.OneOfSetter; import jakarta.validation.ConstraintViolationException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; diff --git a/api/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java similarity index 91% rename from api/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java index e074a656..47ab1a5e 100644 --- a/api/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java @@ -16,11 +16,11 @@ package io.serverlessworkflow.serialization; import com.fasterxml.jackson.core.JsonGenerator; -import io.serverlessworkflow.api.OneOfValueProvider; +import io.serverlessworkflow.annotations.OneOfValueProvider; import java.io.IOException; public class SerializeHelper { - public static void serializeOneOf(JsonGenerator jgen, OneOfValueProvider item) + public static void serializeOneOf(JsonGenerator jgen, OneOfValueProvider item) throws IOException { jgen.writeObject(item.get()); } diff --git a/serverlessworkflow-types/pom.xml b/serverlessworkflow-types/pom.xml new file mode 100644 index 00000000..4fe9f9d2 --- /dev/null +++ b/serverlessworkflow-types/pom.xml @@ -0,0 +1,66 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + Serverless Workflow :: Types + serverlessworkflow-types + + + io.serverlessworkflow + serverlessworkflow-annotations + ${project.version} + + + + + + org.jsonschema2pojo + jsonschema2pojo-maven-plugin + + ${basedir}/src/main/resources/schema + + + yamlschema + io.serverlessworkflow.api.types + ${project.build.directory}/generated-sources/src/main/java + true + true + true + true + false + false + true + true + true + true + ${java.version} + true + jackson2 + true + io.serverlessworkflow.generator.UnreferencedFactory + io.serverlessworkflow.generator.ConstAnnotator + + + + io.serverlessworkflow + serverless-workflow-custom-generator + ${project.version} + + + + + + generate + + generate-sources + + + + + + \ No newline at end of file diff --git a/api/src/main/resources/schema/workflow.yaml b/serverlessworkflow-types/src/main/resources/schema/workflow.yaml similarity index 100% rename from api/src/main/resources/schema/workflow.yaml rename to serverlessworkflow-types/src/main/resources/schema/workflow.yaml From a7efd235b10f6dec537d450f4bb64cc86f790d21 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 10 Jul 2025 20:01:27 +0200 Subject: [PATCH 5/6] [Fix #634] Setting up maven plugin Signed-off-by: fjtirado --- api/pom.xml | 47 ++++ .../api/ObjectMapperFactory.java | 6 +- .../generator/AllAnyOneOfSchemaRule.java | 64 +---- ...nstAnnotator.java => CustomAnnotator.java} | 10 +- .../generator/GeneratorUtils.java | 61 +---- .../generator/UnevaluatedPropertiesRule.java | 56 +--- jackson-generator/pom.xml | 80 ++++++ .../generator/jackson/GeneratorUtils.java | 172 +++++++++++++ .../generator/jackson/JacksonMixInPojo.java | 243 ++++++++++++++++++ pom.xml | 2 + serverlessworkflow-annotations/pom.xml | 10 - .../annotations/AdditionalProperties.java | 26 ++ .../annotations/GetterMethod.java | 26 ++ .../serverlessworkflow/annotations/Item.java | 26 ++ .../annotations/ItemKey.java | 26 ++ .../annotations/ItemValue.java | 26 ++ .../serverlessworkflow/annotations/Union.java | 26 ++ serverlessworkflow-serialization/pom.xml | 24 ++ .../serialization/DeserializeHelper.java | 0 .../serialization/SerializeHelper.java | 0 serverlessworkflow-types/pom.xml | 8 +- 21 files changed, 754 insertions(+), 185 deletions(-) rename custom-generator/src/main/java/io/serverlessworkflow/generator/{ConstAnnotator.java => CustomAnnotator.java} (78%) create mode 100644 jackson-generator/pom.xml create mode 100644 jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java create mode 100644 jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java create mode 100644 serverlessworkflow-serialization/pom.xml rename {serverlessworkflow-annotations => serverlessworkflow-serialization}/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java (100%) rename {serverlessworkflow-annotations => serverlessworkflow-serialization}/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java (100%) diff --git a/api/pom.xml b/api/pom.xml index e2bf61fa..19414b85 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -18,6 +18,11 @@ serverlessworkflow-types ${project.version} + + io.serverlessworkflow + serverlessworkflow-serialization + ${project.version} + org.slf4j slf4j-api @@ -73,4 +78,46 @@ test + + + + + io.serverlessworkflow + jackson-generator + ${project.version} + + + io.serverlessworkflow.api.types + + + + + generate + + generate-sources + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.3.0 + + + add-mixin + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/jacksonmixinpojo + + + + + + + diff --git a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java index c8211586..78b1e24e 100644 --- a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java +++ b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java @@ -15,11 +15,13 @@ */ package io.serverlessworkflow.api; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature; +import io.serverlessworkflow.api.types.JacksonMixInModule; import io.serverlessworkflow.serialization.BeanDeserializerModifierWithValidation; import io.serverlessworkflow.serialization.URIDeserializer; import io.serverlessworkflow.serialization.URISerializer; @@ -47,10 +49,12 @@ private static ObjectMapper configure(ObjectMapper mapper) { validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation()); return mapper + .setSerializationInclusion(Include.NON_NULL) .configure(SerializationFeature.INDENT_OUTPUT, true) .configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false) .configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false) - .registerModule(validationModule); + .registerModule(validationModule) + .registerModule(new JacksonMixInModule()); } private ObjectMapperFactory() {} diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java index 6221a560..32dca0cd 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java @@ -16,8 +16,6 @@ package io.serverlessworkflow.generator; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.node.ArrayNode; import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; @@ -35,8 +33,7 @@ import com.sun.codemodel.JVar; import io.serverlessworkflow.annotations.OneOfSetter; import io.serverlessworkflow.annotations.OneOfValueProvider; -import io.serverlessworkflow.serialization.DeserializeHelper; -import io.serverlessworkflow.serialization.SerializeHelper; +import io.serverlessworkflow.annotations.Union; import jakarta.validation.ConstraintViolationException; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -324,21 +321,7 @@ private JDefinedClass populateOneOf( definedClass._implements( definedClass.owner().ref(OneOfValueProvider.class).narrow(valueField.type())); GeneratorUtils.implementInterface(definedClass, valueField); - try { - JDefinedClass serializer = generateSerializer(definedClass); - definedClass.annotate(JsonSerialize.class).param("using", serializer); - } catch (JClassAlreadyExistsException ex) { - // already serialized aware - } - - try { - JDefinedClass deserializer = - generateDeserializer(definedClass, oneOfTypes, "deserializeOneOf"); - definedClass.annotate(JsonDeserialize.class).param("using", deserializer); - } catch (JClassAlreadyExistsException ex) { - // already deserialized aware - } - + definedClass.annotate(Union.class); return wrapAll(parentSchema, definedClass, commonType, oneOfTypes, Optional.of(valueField)); } @@ -389,49 +372,6 @@ private static boolean isStringType(JType type) { return type.name().equals("String"); } - private JDefinedClass generateSerializer(JDefinedClass relatedClass) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); - GeneratorUtils.fillSerializer( - definedClass, - relatedClass, - (method, valueParam, genParam) -> - method - .body() - .staticInvoke(definedClass.owner().ref(SerializeHelper.class), "serializeOneOf") - .arg(genParam) - .arg(valueParam)); - return definedClass; - } - - private JDefinedClass generateDeserializer( - JDefinedClass relatedClass, Collection oneOfTypes, String methodName) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); - GeneratorUtils.fillDeserializer( - definedClass, - relatedClass, - (method, parserParam) -> { - JBlock body = method.body(); - - body._return( - definedClass - .owner() - .ref(DeserializeHelper.class) - .staticInvoke(methodName) - .arg(parserParam) - .arg(relatedClass.dotclass()) - .arg(list(definedClass, oneOfTypes))); - }); - return definedClass; - } - - private JInvocation list(JDefinedClass definedClass, Collection list) { - JInvocation result = definedClass.owner().ref(List.class).staticInvoke("of"); - list.forEach(c -> result.arg(((JClass) c.getType()).dotclass())); - return result; - } - private void wrapIt( Schema parentSchema, JDefinedClass definedClass, diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java similarity index 78% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java rename to custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java index a893cfb5..657ba3ce 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java @@ -18,18 +18,24 @@ import com.fasterxml.jackson.databind.JsonNode; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; +import io.serverlessworkflow.annotations.AdditionalProperties; import jakarta.validation.constraints.Pattern; import org.jsonschema2pojo.AbstractAnnotator; import org.jsonschema2pojo.GenerationConfig; -public class ConstAnnotator extends AbstractAnnotator { +public class CustomAnnotator extends AbstractAnnotator { private static final String CONST = "const"; - public ConstAnnotator(GenerationConfig generationConfig) { + public CustomAnnotator(GenerationConfig generationConfig) { super(generationConfig); } + @Override + public void additionalPropertiesField(JFieldVar field, JDefinedClass clazz, String propertyName) { + clazz.annotate(AdditionalProperties.class); + } + @Override public void propertyField( JFieldVar field, JDefinedClass clazz, String propertyName, JsonNode propertyNode) { diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java index b98bd7be..d330321b 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java @@ -15,43 +15,15 @@ */ package io.serverlessworkflow.generator; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; -import com.sun.codemodel.JVar; -import java.io.IOException; +import io.serverlessworkflow.annotations.GetterMethod; import org.jsonschema2pojo.util.NameHelper; public class GeneratorUtils { - @FunctionalInterface - public interface SerializerFiller { - void accept(JMethod method, JVar valueParam, JVar genParam); - } - - @FunctionalInterface - public interface DeserializerFiller { - void accept(JMethod method, JVar parserParam); - } - - public static JDefinedClass serializerClass(JDefinedClass relatedClass) - throws JClassAlreadyExistsException { - return createClass(relatedClass, JsonSerializer.class, "Serializer"); - } - - public static JDefinedClass deserializerClass(JDefinedClass relatedClass) - throws JClassAlreadyExistsException { - return createClass(relatedClass, JsonDeserializer.class, "Deserializer"); - } - public static JMethod implementInterface(JDefinedClass definedClass, JFieldVar valueField) { JMethod method = definedClass.method(JMod.PUBLIC, valueField.type(), "get"); method.annotate(Override.class); @@ -67,38 +39,9 @@ public static JMethod getterMethod( instanceField.type(), nameHelper.getGetterName(name, instanceField.type(), null)); method.body()._return(instanceField); + method.annotate(GetterMethod.class); return method; } - public static void fillSerializer( - JDefinedClass definedClass, JDefinedClass relatedClass, SerializerFiller filler) { - JMethod method = definedClass.method(JMod.PUBLIC, void.class, "serialize"); - method.annotate(Override.class); - method._throws(IOException.class); - JVar valueParam = method.param(relatedClass, "value"); - JVar genParam = method.param(JsonGenerator.class, "gen"); - method.param(SerializerProvider.class, "serializers"); - filler.accept(method, valueParam, genParam); - } - - public static void fillDeserializer( - JDefinedClass definedClass, JDefinedClass relatedClass, DeserializerFiller filler) { - JMethod method = definedClass.method(JMod.PUBLIC, relatedClass, "deserialize"); - method.annotate(Override.class); - method._throws(IOException.class); - JVar parserParam = method.param(JsonParser.class, "parser"); - method.param(DeserializationContext.class, "dctx"); - filler.accept(method, parserParam); - } - - private static JDefinedClass createClass( - JDefinedClass relatedClass, Class serializerClass, String suffix) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = - relatedClass._package()._class(JMod.NONE, relatedClass.name() + suffix); - definedClass._extends(definedClass.owner().ref(serializerClass).narrow(relatedClass)); - return definedClass; - } - private GeneratorUtils() {} } diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java index 533db957..5388a503 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java @@ -16,10 +16,6 @@ package io.serverlessworkflow.generator; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.sun.codemodel.JBlock; -import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; @@ -27,7 +23,9 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JType; -import io.serverlessworkflow.serialization.DeserializeHelper; +import io.serverlessworkflow.annotations.Item; +import io.serverlessworkflow.annotations.ItemKey; +import io.serverlessworkflow.annotations.ItemValue; import org.jsonschema2pojo.Schema; import org.jsonschema2pojo.rules.AdditionalPropertiesRule; import org.jsonschema2pojo.rules.Rule; @@ -100,12 +98,10 @@ private JDefinedClass addKeyValueFields( JMod.PRIVATE, propertyType, nameHelper.getPropertyName(propertyType.name(), null)); JMethod valueMethod = GeneratorUtils.getterMethod(jclass, valueField, nameHelper, propertyType.name()); - jclass - .annotate(JsonSerialize.class) - .param("using", generateSerializer(jclass, nameMethod, valueMethod)); - jclass - .annotate(JsonDeserialize.class) - .param("using", generateDeserializer(jclass, propertyType)); + + jclass.annotate(Item.class); + nameMethod.annotate(ItemKey.class); + valueMethod.annotate(ItemValue.class); JMethod constructor = jclass.constructor(JMod.PUBLIC); constructor .body() @@ -114,44 +110,6 @@ private JDefinedClass addKeyValueFields( return jclass; } - private JDefinedClass generateDeserializer(JDefinedClass relatedClass, JType propertyType) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); - GeneratorUtils.fillDeserializer( - definedClass, - relatedClass, - (method, parserParam) -> - method - .body() - ._return( - definedClass - .owner() - .ref(DeserializeHelper.class) - .staticInvoke("deserializeItem") - .arg(parserParam) - .arg(relatedClass.dotclass()) - .arg(((JClass) propertyType).dotclass()))); - return definedClass; - } - - private JDefinedClass generateSerializer( - JDefinedClass relatedClass, JMethod nameMethod, JMethod valueMethod) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); - GeneratorUtils.fillSerializer( - definedClass, - relatedClass, - (method, valueParam, genParam) -> { - JBlock body = method.body(); - body.invoke(genParam, "writeStartObject"); - body.invoke(genParam, "writeObjectField") - .arg(valueParam.invoke(nameMethod)) - .arg(valueParam.invoke(valueMethod)); - body.invoke(genParam, "writeEndObject"); - }); - return definedClass; - } - private boolean checkIntValue(JsonNode node, String propName, int value) { return node.has(propName) && node.get(propName).asInt() == value; } diff --git a/jackson-generator/pom.xml b/jackson-generator/pom.xml new file mode 100644 index 00000000..55d2e03c --- /dev/null +++ b/jackson-generator/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + maven-plugin + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + jackson-generator + + 3.15.1 + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${maven-plugin-tools.version} + + jackson-generator + + + + help-mojo + + + helpmojo + + + + + + + + + + + + org.apache.maven.plugins + maven-plugin-report-plugin + ${maven-plugin-tools.version} + + + + + + org.apache.maven + maven-plugin-api + ${version.maven} + provided + + + com.sun.codemodel + codemodel + 2.6 + + + io.github.classgraph + classgraph + 4.8.180 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven-plugin-tools.version} + provided + + + io.serverlessworkflow + serverlessworkflow-types + ${project.version} + + + io.serverlessworkflow + serverlessworkflow-serialization + ${project.version} + + + \ No newline at end of file diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java new file mode 100644 index 00000000..9463106f --- /dev/null +++ b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java @@ -0,0 +1,172 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.generator.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JType; +import com.sun.codemodel.JVar; +import io.serverlessworkflow.serialization.DeserializeHelper; +import io.serverlessworkflow.serialization.SerializeHelper; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +public class GeneratorUtils { + + @FunctionalInterface + public interface SerializerFiller { + void accept(JMethod method, JVar valueParam, JVar genParam); + } + + @FunctionalInterface + public interface DeserializerFiller { + void accept(JMethod method, JVar parserParam); + } + + public static JDefinedClass serializerClass(JClass relatedClass) + throws JClassAlreadyExistsException { + return createClass(relatedClass, JsonSerializer.class, "Serializer"); + } + + public static JDefinedClass deserializerClass(JClass relatedClass) + throws JClassAlreadyExistsException { + return createClass(relatedClass, JsonDeserializer.class, "Deserializer"); + } + + public static void fillSerializer( + JDefinedClass definedClass, JClass relatedClass, SerializerFiller filler) { + JMethod method = definedClass.method(JMod.PUBLIC, void.class, "serialize"); + method.annotate(Override.class); + method._throws(IOException.class); + JVar valueParam = method.param(relatedClass, "value"); + JVar genParam = method.param(JsonGenerator.class, "gen"); + method.param(SerializerProvider.class, "serializers"); + filler.accept(method, valueParam, genParam); + } + + public static void fillDeserializer( + JDefinedClass definedClass, JClass relatedClass, DeserializerFiller filler) { + JMethod method = definedClass.method(JMod.PUBLIC, relatedClass, "deserialize"); + method.annotate(Override.class); + method._throws(IOException.class); + JVar parserParam = method.param(JsonParser.class, "parser"); + method.param(DeserializationContext.class, "dctx"); + filler.accept(method, parserParam); + } + + private static JDefinedClass createClass( + JClass relatedClass, Class serializerClass, String suffix) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = + relatedClass._package()._class(JMod.NONE, relatedClass.name() + suffix); + definedClass._extends(definedClass.owner().ref(serializerClass).narrow(relatedClass)); + return definedClass; + } + + public static JDefinedClass generateSerializer(JClass relatedClass) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); + GeneratorUtils.fillSerializer( + definedClass, + relatedClass, + (method, valueParam, genParam) -> + method + .body() + .staticInvoke(definedClass.owner().ref(SerializeHelper.class), "serializeOneOf") + .arg(genParam) + .arg(valueParam)); + return definedClass; + } + + public static JDefinedClass generateDeserializer( + JClass relatedClass, Collection oneOfTypes) throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); + GeneratorUtils.fillDeserializer( + definedClass, + relatedClass, + (method, parserParam) -> { + JBlock body = method.body(); + + body._return( + definedClass + .owner() + .ref(DeserializeHelper.class) + .staticInvoke("deserializeOneOf") + .arg(parserParam) + .arg(relatedClass.dotclass()) + .arg(list(definedClass, oneOfTypes))); + }); + return definedClass; + } + + public static JDefinedClass generateDeserializer(JClass relatedClass, JType propertyType) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); + GeneratorUtils.fillDeserializer( + definedClass, + relatedClass, + (method, parserParam) -> + method + .body() + ._return( + definedClass + .owner() + .ref(DeserializeHelper.class) + .staticInvoke("deserializeItem") + .arg(parserParam) + .arg(relatedClass.dotclass()) + .arg(((JClass) propertyType).dotclass()))); + return definedClass; + } + + public static JDefinedClass generateSerializer( + JClass relatedClass, String keyMethod, String valueMethod) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); + GeneratorUtils.fillSerializer( + definedClass, + relatedClass, + (method, valueParam, genParam) -> { + JBlock body = method.body(); + body.invoke(genParam, "writeStartObject"); + body.invoke(genParam, "writeObjectField") + .arg(valueParam.invoke(keyMethod)) + .arg(valueParam.invoke(valueMethod)); + body.invoke(genParam, "writeEndObject"); + }); + return definedClass; + } + + private static JInvocation list(JDefinedClass definedClass, Collection list) { + JInvocation result = definedClass.owner().ref(List.class).staticInvoke("of"); + list.forEach(c -> result.arg(c.dotclass())); + return result; + } + + private GeneratorUtils() {} +} diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java new file mode 100644 index 00000000..16df2a6a --- /dev/null +++ b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java @@ -0,0 +1,243 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.generator.jackson; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.Module.SetupContext; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JExpression; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JPackage; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassRefTypeSignature; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; +import io.github.classgraph.TypeArgument; +import io.github.classgraph.TypeSignature; +import io.serverlessworkflow.annotations.AdditionalProperties; +import io.serverlessworkflow.annotations.GetterMethod; +import io.serverlessworkflow.annotations.Item; +import io.serverlessworkflow.annotations.ItemKey; +import io.serverlessworkflow.annotations.ItemValue; +import io.serverlessworkflow.annotations.Union; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.annotation.Annotation; +import java.nio.file.Files; +import java.util.Collection; +import java.util.LinkedHashSet; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +@Mojo( + name = "generate", + defaultPhase = LifecyclePhase.GENERATE_SOURCES, + requiresDependencyResolution = ResolutionScope.COMPILE, + threadSafe = true) +public class JacksonMixInPojo extends AbstractMojo { + + @Parameter( + property = "jacksonmixinpojo.outputDirectory", + defaultValue = "${project.build.directory}/generated-sources/jacksonmixinpojo") + private File outputDirectory; + + /** + * Package name used for generated Java classes (for types where a fully qualified name has not + * been supplied in the schema using the 'javaType' property). + * + * @since 0.1.0 + */ + @Parameter(property = "jsonschema2pojo.targetPackage") + private String targetPackage = ""; + + private static final String MIXIN_METHOD = "setMixInAnnotation"; + private static final String ADD_PROPERTIES_METHOD = "getAdditionalProperties"; + private static final String SETUP_METHOD = "setupModule"; + private JCodeModel codeModel; + private JPackage rootPackage; + private JMethod setupMethod; + + @FunctionalInterface + interface AnnotationProcessor { + void accept(ClassInfo classInfo, JDefinedClass definedClass) + throws JClassAlreadyExistsException; + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + try (ScanResult result = + new ClassGraph() + .enableAnnotationInfo() + .enableMethodInfo() + .acceptPackages(targetPackage) + .scan()) { + codeModel = new JCodeModel(); + rootPackage = codeModel._package(targetPackage); + setupMethod = + rootPackage + ._class("JacksonMixInModule") + ._extends(SimpleModule.class) + .method(JMod.PUBLIC, codeModel.VOID, SETUP_METHOD); + processAnnotatedClasses(result, Union.class, this::buildUnionMixIn); + processAnnotatedClasses(result, AdditionalProperties.class, this::buildAdditionalPropsMixIn); + processAnnotatedClasses(result, Item.class, this::buildItemMixIn); + processAnnotatedClasses(result.getAllEnums(), this::buildEnumMixIn); + setupMethod + .body() + .invoke(JExpr._super(), SETUP_METHOD) + .arg(setupMethod.param(SetupContext.class, "context")); + Files.createDirectories(outputDirectory.toPath()); + codeModel.build(outputDirectory, (PrintStream) null); + } catch (JClassAlreadyExistsException | IOException e) { + getLog().error(e); + } + } + + private void processAnnotatedClasses( + Iterable classesInfo, AnnotationProcessor processor) + throws JClassAlreadyExistsException { + for (ClassInfo classInfo : classesInfo) { + setupMethod + .body() + .invoke(JExpr._super(), MIXIN_METHOD) + .arg(JExpr.dotclass(codeModel.ref(classInfo.getName()))) + .arg(processAnnotatedClass(classInfo, processor)); + } + } + + private void processAnnotatedClasses( + ScanResult result, Class annotation, AnnotationProcessor processor) + throws JClassAlreadyExistsException { + processAnnotatedClasses(result.getClassesWithAnnotation(annotation), processor); + } + + private JExpression processAnnotatedClass(ClassInfo classInfo, AnnotationProcessor processor) + throws JClassAlreadyExistsException { + JDefinedClass result = createMixInClass(classInfo); + processor.accept(classInfo, result); + return JExpr.dotclass(result); + } + + private void buildAdditionalPropsMixIn( + ClassInfo addPropsClassInfo, JDefinedClass addPropsMixClass) { + JClass mapStringObject = + getReturnType(addPropsClassInfo.getMethodInfo().getSingleMethod(ADD_PROPERTIES_METHOD)); + JClass objectType = mapStringObject.getTypeParameters().get(1); + addPropsMixClass + .method(JMod.ABSTRACT, mapStringObject, ADD_PROPERTIES_METHOD) + .annotate(JsonAnyGetter.class); + addPropsMixClass + .field(JMod.NONE, mapStringObject, "additionalProperties") + .annotate(JsonIgnore.class); + JMethod setter = + addPropsMixClass.method(JMod.ABSTRACT, codeModel.VOID, "setAdditionalProperty"); + setter.param(String.class, "name"); + setter.param(objectType, "value"); + setter.annotate(JsonAnySetter.class); + } + + private void buildItemMixIn(ClassInfo classInfo, JDefinedClass mixClass) + throws JClassAlreadyExistsException { + JClass relClass = codeModel.ref(classInfo.getName()); + MethodInfo keyMethod = + classInfo.getMethodInfo().filter(m -> m.hasAnnotation(ItemKey.class)).get(0); + MethodInfo valueMethod = + classInfo.getMethodInfo().filter(m -> m.hasAnnotation(ItemValue.class)).get(0); + mixClass + .annotate(JsonSerialize.class) + .param( + "using", + GeneratorUtils.generateSerializer( + relClass, keyMethod.getName(), valueMethod.getName())); + mixClass + .annotate(JsonDeserialize.class) + .param("using", GeneratorUtils.generateDeserializer(relClass, getReturnType(valueMethod))); + } + + private void buildUnionMixIn(ClassInfo unionClassInfo, JDefinedClass unionMixClass) + throws JClassAlreadyExistsException { + JClass unionClass = codeModel.ref(unionClassInfo.getName()); + unionMixClass + .annotate(JsonSerialize.class) + .param("using", GeneratorUtils.generateSerializer(unionClass)); + unionMixClass + .annotate(JsonDeserialize.class) + .param( + "using", + GeneratorUtils.generateDeserializer(unionClass, getUnionClasses(unionClassInfo))); + } + + private void buildEnumMixIn(ClassInfo classInfo, JDefinedClass mixClass) + throws JClassAlreadyExistsException { + mixClass.method(JMod.ABSTRACT, String.class, "value").annotate(JsonValue.class); + + JMethod staticMethod = + mixClass.method(JMod.STATIC, codeModel.ref(classInfo.getName()), "fromValue"); + staticMethod.param(String.class, "value"); + staticMethod.annotate(JsonCreator.class); + staticMethod.body()._return(JExpr._null()); + } + + private JDefinedClass createMixInClass(ClassInfo classInfo) throws JClassAlreadyExistsException { + return rootPackage._class(JMod.ABSTRACT, classInfo.getSimpleName() + "MixIn"); + } + + private Collection getUnionClasses(ClassInfo unionClassInfo) { + Collection result = new LinkedHashSet(); + unionClassInfo + .getMethodInfo() + .filter(f -> f.hasAnnotation(GetterMethod.class)) + .forEach(m -> result.add(getReturnType(m))); + return result; + } + + private JClass getReturnType(MethodInfo info) { + return getReturnType(info.getTypeSignatureOrTypeDescriptor().getResultType()); + } + + private JClass getReturnType(ClassRefTypeSignature refTypeSignature) { + JClass result = codeModel.ref(refTypeSignature.getFullyQualifiedClassName()); + for (TypeArgument t : refTypeSignature.getTypeArguments()) + result = result.narrow(getReturnType(t.getTypeSignature())); + return result; + } + + private JClass getReturnType(TypeSignature t) { + return t instanceof ClassRefTypeSignature refTypeSignature + ? getReturnType(refTypeSignature) + : codeModel.ref(t.toString()); + } +} diff --git a/pom.xml b/pom.xml index 271e10da..a187fbfe 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,8 @@ impl serverlessworkflow-types serverlessworkflow-annotations + jackson-generator + serverlessworkflow-serialization diff --git a/serverlessworkflow-annotations/pom.xml b/serverlessworkflow-annotations/pom.xml index e37aa671..6e583ab9 100644 --- a/serverlessworkflow-annotations/pom.xml +++ b/serverlessworkflow-annotations/pom.xml @@ -7,14 +7,4 @@ Serverless Workflow :: Annotations serverlessworkflow-annotations - - - com.fasterxml.jackson.core - jackson-databind - - - jakarta.validation - jakarta.validation-api - - \ No newline at end of file diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java new file mode 100644 index 00000000..69a3b023 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(TYPE) +public @interface AdditionalProperties {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java new file mode 100644 index 00000000..1906f20b --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.annotations; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface GetterMethod {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java new file mode 100644 index 00000000..f42a86d5 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(TYPE) +public @interface Item {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java new file mode 100644 index 00000000..ea75094d --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.annotations; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface ItemKey {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java new file mode 100644 index 00000000..2aaf09e6 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.annotations; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface ItemValue {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java new file mode 100644 index 00000000..e9d3f2a8 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java @@ -0,0 +1,26 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(ElementType.TYPE) +public @interface Union {} diff --git a/serverlessworkflow-serialization/pom.xml b/serverlessworkflow-serialization/pom.xml new file mode 100644 index 00000000..55787c00 --- /dev/null +++ b/serverlessworkflow-serialization/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + serverlessworkflow-serialization + + + io.serverlessworkflow + serverlessworkflow-annotations + ${project.version} + + + com.fasterxml.jackson.core + jackson-databind + + + jakarta.validation + jakarta.validation-api + + + \ No newline at end of file diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java rename to serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java b/serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java rename to serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java diff --git a/serverlessworkflow-types/pom.xml b/serverlessworkflow-types/pom.xml index 4fe9f9d2..90aa8996 100644 --- a/serverlessworkflow-types/pom.xml +++ b/serverlessworkflow-types/pom.xml @@ -13,6 +13,10 @@ serverlessworkflow-annotations ${project.version} + + jakarta.validation + jakarta.validation-api + @@ -40,10 +44,10 @@ true ${java.version} true - jackson2 + none true io.serverlessworkflow.generator.UnreferencedFactory - io.serverlessworkflow.generator.ConstAnnotator + io.serverlessworkflow.generator.CustomAnnotator From 7f62ffd880a8aa8628264b476c4f0ef86747c6d3 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Mon, 14 Jul 2025 11:26:54 +0200 Subject: [PATCH 6/6] [Fix #634] Preserving union class order Signed-off-by: fjtirado --- .../generator/AllAnyOneOfSchemaRule.java | 5 ++-- .../generator/GeneratorUtils.java | 2 -- .../generator/jackson/JacksonMixInPojo.java | 18 +++++++------ .../annotations/GetterMethod.java | 26 ------------------- .../serverlessworkflow/annotations/Union.java | 4 ++- 5 files changed, 16 insertions(+), 39 deletions(-) delete mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java index 32dca0cd..a9823ce6 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.sun.codemodel.JAnnotationArrayMember; import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; @@ -317,11 +318,11 @@ private JDefinedClass populateOneOf( commonType.orElse(definedClass.owner().ref(Object.class)), ruleFactory.getNameHelper().getPropertyName("value", null), null); - definedClass._implements( definedClass.owner().ref(OneOfValueProvider.class).narrow(valueField.type())); GeneratorUtils.implementInterface(definedClass, valueField); - definedClass.annotate(Union.class); + JAnnotationArrayMember unionAnnotation = definedClass.annotate(Union.class).paramArray("value"); + oneOfTypes.forEach(t -> unionAnnotation.param(t.getType())); return wrapAll(parentSchema, definedClass, commonType, oneOfTypes, Optional.of(valueField)); } diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java index d330321b..abcf40eb 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java @@ -19,7 +19,6 @@ import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; -import io.serverlessworkflow.annotations.GetterMethod; import org.jsonschema2pojo.util.NameHelper; public class GeneratorUtils { @@ -39,7 +38,6 @@ public static JMethod getterMethod( instanceField.type(), nameHelper.getGetterName(name, instanceField.type(), null)); method.body()._return(instanceField); - method.annotate(GetterMethod.class); return method; } diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java index 16df2a6a..b80238ed 100644 --- a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java +++ b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java @@ -33,6 +33,8 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JPackage; +import io.github.classgraph.AnnotationClassRef; +import io.github.classgraph.AnnotationInfo; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; import io.github.classgraph.ClassRefTypeSignature; @@ -41,7 +43,6 @@ import io.github.classgraph.TypeArgument; import io.github.classgraph.TypeSignature; import io.serverlessworkflow.annotations.AdditionalProperties; -import io.serverlessworkflow.annotations.GetterMethod; import io.serverlessworkflow.annotations.Item; import io.serverlessworkflow.annotations.ItemKey; import io.serverlessworkflow.annotations.ItemValue; @@ -52,7 +53,8 @@ import java.lang.annotation.Annotation; import java.nio.file.Files; import java.util.Collection; -import java.util.LinkedHashSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -216,12 +218,12 @@ private JDefinedClass createMixInClass(ClassInfo classInfo) throws JClassAlready } private Collection getUnionClasses(ClassInfo unionClassInfo) { - Collection result = new LinkedHashSet(); - unionClassInfo - .getMethodInfo() - .filter(f -> f.hasAnnotation(GetterMethod.class)) - .forEach(m -> result.add(getReturnType(m))); - return result; + AnnotationInfo info = unionClassInfo.getAnnotationInfoRepeatable(Union.class).get(0); + Object[] unionClasses = (Object[]) info.getParameterValues().getValue("value"); + return Stream.of(unionClasses) + .map(AnnotationClassRef.class::cast) + .map(ref -> codeModel.ref(ref.getName())) + .collect(Collectors.toList()); } private JClass getReturnType(MethodInfo info) { diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java deleted file mode 100644 index 1906f20b..00000000 --- a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2020-Present The Serverless Workflow Specification Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.serverlessworkflow.annotations; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Retention(RUNTIME) -@Target(METHOD) -public @interface GetterMethod {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java index e9d3f2a8..e6cc4ecb 100644 --- a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java @@ -23,4 +23,6 @@ @Retention(RUNTIME) @Target(ElementType.TYPE) -public @interface Union {} +public @interface Union { + Class[] value(); +}