Repository: serverlessworkflow/sdk-java (java)

Issue:
[BUG][7.0.0] Error when parsing listen task (one,all,any)
**What happened**:

During a debug session on a runtime implementation in Java one of our new tests failed due to error when reading and parsing the workflow YAML file.
After some time we found suppresed errors showing that the expected fields inside the `to` block of `listen` was expecting diferent keys than the ones specified on the spec.

![Captura de tela de 2024-08-21 11-09-17](https://github.com/user-attachments/assets/5767fe78-364e-442a-8383-8a849daff0c4)

I also replicated the failure with tests on the SDK project by adding a new test.

![Captura de tela de 2024-08-21 11-22-02](https://github.com/user-attachments/assets/522b7cbe-fe67-49a2-9cdd-f913a832ac68)


**What you expected to happen**:

For the YAML to be parsed correctly and expect the format complying with the SW specification.

**How to reproduce it**:
Add a `listen` task to any workflow using `with.to.any` or `with.to.one` or `with.to.all`

**Anything else we need to know?**:

**Environment**:
- Specification version used: 1.0.0-alpha2 

Pull request description:
Fix #412 Adding OneOf support to EventConsumptionStrategy
Fix https://github.com/serverlessworkflow/sdk-java/issues/412

Patch:
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 1e99f0f..c5496ed 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java
@@ -105,19 +105,41 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
       JDefinedClass definedClass, Optional<JType> refType, Collection<JType> unionTypes) {
     JType clazzClass = definedClass.owner()._ref(Object.class);
 
-    JFieldVar valueField =
-        definedClass.field(
-            JMod.PRIVATE,
-            clazzClass,
-            ruleFactory.getNameHelper().getPropertyName("value", null),
-            null);
+    Optional<JFieldVar> valueField;
+    if (!unionTypes.isEmpty()) {
+      valueField =
+          Optional.of(
+              definedClass.field(
+                  JMod.PRIVATE,
+                  clazzClass,
+                  ruleFactory.getNameHelper().getPropertyName("value", null),
+                  null));
+
+      definedClass._implements(
+          definedClass.owner().ref(GeneratorUtils.ONE_OF_VALUE_PROVIDER_INTERFACE_NAME));
 
-    definedClass._implements(
-        definedClass.owner().ref(GeneratorUtils.ONE_OF_VALUE_PROVIDER_INTERFACE_NAME));
+      GeneratorUtils.implementInterface(definedClass, valueField.orElseThrow());
 
-    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, unionTypes);
+        definedClass.annotate(JsonDeserialize.class).param("using", deserializer);
+      } catch (JClassAlreadyExistsException ex) {
+        // already deserialized aware
+      }
+      for (JType unionType : unionTypes) {
+        wrapIt(definedClass, valueField, unionType);
+      }
+    } else {
+      valueField = Optional.empty();
+    }
 
-    unionTypes.forEach(unionType -> wrapIt(definedClass, valueField, unionType));
     refType.ifPresent(
         type -> {
           if (type instanceof JClass) {
@@ -126,6 +148,7 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
             wrapIt(definedClass, valueField, type);
           }
         });
+
     if (definedClass.constructors().hasNext()
         && definedClass.getConstructor(new JType[0]) == null) {
       definedClass.constructor(JMod.PUBLIC);
@@ -133,7 +156,8 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
     return definedClass;
   }
 
-  private JDefinedClass generateSerializer(JDefinedClass relatedClass) {
+  private JDefinedClass generateSerializer(JDefinedClass relatedClass)
+      throws JClassAlreadyExistsException {
     JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass);
     GeneratorUtils.fillSerializer(
         definedClass,
@@ -150,7 +174,8 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
   }
 
   private JDefinedClass generateDeserializer(
-      JDefinedClass relatedClass, Collection<JType> unionTypes) {
+      JDefinedClass relatedClass, Collection<JType> unionTypes)
+      throws JClassAlreadyExistsException {
     JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass);
     GeneratorUtils.fillDeserializer(
         definedClass,
@@ -173,22 +198,18 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
 
   private JDefinedClass createUnionClass(
       String nodeName, JPackage container, Optional<JType> refType, Collection<JType> unionTypes) {
-    final String className =
-        ruleFactory.getNameHelper().getUniqueClassName(nodeName, null, container);
     try {
-      JDefinedClass definedClass = container._class(className);
-      definedClass.annotate(JsonSerialize.class).param("using", generateSerializer(definedClass));
-      definedClass
-          .annotate(JsonDeserialize.class)
-          .param("using", generateDeserializer(definedClass, unionTypes));
-
-      return populateClass(definedClass, refType, unionTypes);
+      return populateClass(
+          container._class(
+              ruleFactory.getNameHelper().getUniqueClassName(nodeName, null, container)),
+          refType,
+          unionTypes);
     } catch (JClassAlreadyExistsException e) {
       throw new IllegalArgumentException(e);
     }
   }
 
-  private void wrapIt(JDefinedClass definedClass, JFieldVar valueField, JType unionType) {
+  private void wrapIt(JDefinedClass definedClass, Optional<JFieldVar> valueField, JType unionType) {
     final String name = unionType.name();
     JFieldVar instanceField =
         definedClass.field(
@@ -196,10 +217,9 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
     GeneratorUtils.buildMethod(definedClass, instanceField, ruleFactory.getNameHelper(), name);
     JMethod constructor = definedClass.constructor(JMod.PUBLIC);
     JVar instanceParam = constructor.param(unionType, instanceField.name());
-    constructor
-        .body()
-        .assign(JExpr._this().ref(valueField), instanceParam)
-        .assign(JExpr._this().ref(instanceField), instanceParam);
+    JBlock body = constructor.body();
+    valueField.ifPresent(v -> body.assign(JExpr._this().ref(v), instanceParam));
+    body.assign(JExpr._this().ref(instanceField), instanceParam);
   }
 
   private void unionType(
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 ebf1c50..ce3badc 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java
@@ -49,11 +49,13 @@ public class GeneratorUtils {
     void accept(JMethod method, JVar parserParam);
   }
 
-  public static JDefinedClass serializerClass(JDefinedClass relatedClass) {
+  public static JDefinedClass serializerClass(JDefinedClass relatedClass)
+      throws JClassAlreadyExistsException {
     return createClass(relatedClass, JsonSerializer.class, "Serializer");
   }
 
-  public static JDefinedClass deserializerClass(JDefinedClass relatedClass) {
+  public static JDefinedClass deserializerClass(JDefinedClass relatedClass)
+      throws JClassAlreadyExistsException {
     return createClass(relatedClass, JsonDeserializer.class, "Deserializer");
   }
 
@@ -97,15 +99,12 @@ public class GeneratorUtils {
   }
 
   private static JDefinedClass createClass(
-      JDefinedClass relatedClass, Class<?> serializerClass, String suffix) {
-    try {
-      JDefinedClass definedClass =
-          relatedClass._package()._class(JMod.NONE, relatedClass.name() + suffix);
-      definedClass._extends(definedClass.owner().ref(serializerClass).narrow(relatedClass));
-      return definedClass;
-    } catch (JClassAlreadyExistsException ex) {
-      throw new IllegalArgumentException(ex);
-    }
+      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 25eacd1..0e93765 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java
@@ -20,6 +20,7 @@ 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;
 import com.sun.codemodel.JFieldVar;
@@ -52,14 +53,19 @@ public class UnevaluatedPropertiesRule extends AdditionalPropertiesRule
     } else if (node != null
         && checkIntValue(parent, "maxProperties", 1)
         && checkIntValue(parent, "minProperties", 1)) {
-      return addKeyValueFields(jclass, node, parent, nodeName, schema);
+      try {
+        return addKeyValueFields(jclass, node, parent, nodeName, schema);
+      } catch (JClassAlreadyExistsException e) {
+        throw new IllegalArgumentException(e);
+      }
     } else {
       return super.apply(nodeName, node, parent, jclass, schema);
     }
   }
 
   private JDefinedClass addKeyValueFields(
-      JDefinedClass jclass, JsonNode node, JsonNode parent, String nodeName, Schema schema) {
+      JDefinedClass jclass, JsonNode node, JsonNode parent, String nodeName, Schema schema)
+      throws JClassAlreadyExistsException {
     NameHelper nameHelper = ruleFactory.getNameHelper();
     JType stringClass = jclass.owner()._ref(String.class);
     JFieldVar nameField =
@@ -107,7 +113,8 @@ public class UnevaluatedPropertiesRule extends AdditionalPropertiesRule
     return jclass;
   }
 
-  private JDefinedClass generateDeserializer(JDefinedClass relatedClass, JType propertyType) {
+  private JDefinedClass generateDeserializer(JDefinedClass relatedClass, JType propertyType)
+      throws JClassAlreadyExistsException {
     JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass);
     GeneratorUtils.fillDeserializer(
         definedClass,
@@ -127,7 +134,8 @@ public class UnevaluatedPropertiesRule extends AdditionalPropertiesRule
   }
 
   private JDefinedClass generateSerializer(
-      JDefinedClass relatedClass, JMethod nameMethod, JMethod valueMethod) {
+      JDefinedClass relatedClass, JMethod nameMethod, JMethod valueMethod)
+      throws JClassAlreadyExistsException {
     JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass);
     GeneratorUtils.fillSerializer(
         definedClass,


Test patch:
diff --git a/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java b/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java
index de2591a..8e77673 100644
--- a/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java
+++ b/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java
@@ -42,7 +42,8 @@ public class FeaturesTest {
         "features/raise.yaml",
         "features/set.yaml",
         "features/switch.yaml",
-        "features/try.yaml"
+        "features/try.yaml",
+        "features/listen.yaml"
       })
   public void testSpecFeaturesParsing(String workflowLocation) throws IOException {
     Workflow workflow = readWorkflowFromClasspath(workflowLocation);
diff --git a/api/src/test/resources/features/listen.yaml b/api/src/test/resources/features/listen.yaml
new file mode 100644
index 0000000..393355d
--- /dev/null
+++ b/api/src/test/resources/features/listen.yaml
@@ -0,0 +1,12 @@
+document:
+  dsl: 1.0.0-alpha1
+  namespace: default
+  name: listen-task
+do:
+  - listenToSomething:
+      listen:
+        to:
+          any:
+            - with: 
+                source: pepe
+                type: pepe
\ No newline at end of file