Repository: serverlessworkflow/sdk-java
Pull request #238 (2023-07-06 12:33:00)

Removing gson and everit dependencies
**Many thanks for submitting your Pull Request :heart:!**

**What this PR does / why we need it**:
It removed org.json and everit validation dependencies and add json-schema-validator for validation.
See https://github.com/everit-org/json-schema#when-to-use-this-library
Since we are using Jackson for serialization/deserialization of the workflow, we should use https://github.com/networknt/json-schema-validator instead of everit, and hence avoid having dependencies to two different json parsers. 

**Special notes for reviewers**:
This remove unneeded dependencies for poms and slightly change the validator interface (it now returns a Collection of ValidationErrors rather than a List, which easily remove duplicated validation messages) 

**Additional information (if needed):**

ricardozanini: Although this is very important, we need to handle the CVEs while they don't release a new version:

https://mvnrepository.com/artifact/com.github.java-json-tools/json-schema-validator/2.2.14

1. Upgrade Guava - [CVE-2023-2976](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-2976) and [CVE-2020-8908](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8908)
2. Upgrade testng-core - [CVE-2022-4065](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-4065)


Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 06541a8..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-# To get started with Dependabot version updates, you'll need to specify which
-# package ecosystems to update and where the package manifests are located.
-# Please see the documentation for all configuration options:
-# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
-
-version: 2
-updates:
-  - package-ecosystem: "maven" # See documentation for possible values
-    directory: "/" # Location of package manifests
-    schedule:
-      interval: "weekly"
-    assignees:
-      - ricardozanini
-      - tsurdilo
diff --git a/api/pom.xml b/api/pom.xml
index e3e6838..e984453 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -11,6 +11,7 @@
 
     <artifactId>serverlessworkflow-api</artifactId>
     <name>Serverless Workflow :: API</name>
+    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
     <description>Java SDK for Serverless Workflow Specification</description>
 
@@ -26,31 +27,19 @@
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-core</artifactId>
-            <version>[2.13.0,)</version>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
-            <version>[2.13.0,)</version>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.dataformat</groupId>
             <artifactId>jackson-dataformat-yaml</artifactId>
-            <version>[2.13.0,)</version>
         </dependency>
         <dependency>
             <groupId>javax.validation</groupId>
             <artifactId>validation-api</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.json</groupId>
-            <artifactId>json</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.github.erosb</groupId>
-            <artifactId>everit-json-schema</artifactId>
-        </dependency>
-
         <!-- test -->
         <dependency>
             <groupId>org.junit.jupiter</groupId>
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/AuthDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/AuthDeserializer.java
index abdb058..aa078cb 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/AuthDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/AuthDeserializer.java
@@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.serverlessworkflow.api.auth.AuthDefinition;
 import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
 import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@ import io.serverlessworkflow.api.workflow.Auth;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,21 +67,8 @@ public class AuthDeserializer extends StdDeserializer<Auth> {
     } else {
       String authFileDef = node.asText();
       String authFileSrc = Utils.getResourceFileAsString(authFileDef);
-      JsonNode authRefNode;
-      ObjectMapper jsonWriter = new ObjectMapper();
       if (authFileSrc != null && authFileSrc.trim().length() > 0) {
-        // if its a yaml def convert to json first
-        if (!authFileSrc.trim().startsWith("{")) {
-          // convert yaml to json to validate
-          ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-          Object obj = yamlReader.readValue(authFileSrc, Object.class);
-
-          authRefNode =
-              jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
-        } else {
-          authRefNode = jsonWriter.readTree(new JSONObject(authFileSrc).toString());
-        }
-
+        JsonNode authRefNode = Utils.getNode(authFileSrc);
         JsonNode refAuth = authRefNode.get("auth");
         if (refAuth != null) {
           for (final JsonNode nodeEle : refAuth) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java
index 71b6f45..1d95216 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java
@@ -18,14 +18,11 @@ package io.serverlessworkflow.api.deserializers;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
 import io.serverlessworkflow.api.utils.Utils;
 import io.serverlessworkflow.api.workflow.Constants;
 import java.io.IOException;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,21 +61,8 @@ public class ConstantsDeserializer extends StdDeserializer<Constants> {
       String constantsFileDef = node.asText();
       constants.setRefValue(constantsFileDef);
       String constantsFileSrc = Utils.getResourceFileAsString(constantsFileDef);
-      JsonNode constantsRefNode;
-      ObjectMapper jsonWriter = new ObjectMapper();
       if (constantsFileSrc != null && constantsFileSrc.trim().length() > 0) {
-        // if its a yaml def convert to json first
-        if (!constantsFileSrc.trim().startsWith("{")) {
-          // convert yaml to json to validate
-          ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-          Object obj = yamlReader.readValue(constantsFileSrc, Object.class);
-
-          constantsRefNode =
-              jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
-        } else {
-          constantsRefNode = jsonWriter.readTree(new JSONObject(constantsFileSrc).toString());
-        }
-
+        JsonNode constantsRefNode = Utils.getNode(constantsFileSrc);
         JsonNode refConstants = constantsRefNode.get("constants");
         if (refConstants != null) {
           constantsDefinition = refConstants;
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java
index beedc7d..6fe366e 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java
@@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.serverlessworkflow.api.error.ErrorDefinition;
 import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
 import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@ import io.serverlessworkflow.api.workflow.Errors;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,21 +67,8 @@ public class ErrorsDeserializer extends StdDeserializer<Errors> {
     } else {
       String errorsFileDef = node.asText();
       String errorsFileSrc = Utils.getResourceFileAsString(errorsFileDef);
-      JsonNode errorsRefNode;
-      ObjectMapper jsonWriter = new ObjectMapper();
       if (errorsFileSrc != null && errorsFileSrc.trim().length() > 0) {
-        // if its a yaml def convert to json first
-        if (!errorsFileSrc.trim().startsWith("{")) {
-          // convert yaml to json to validate
-          ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-          Object obj = yamlReader.readValue(errorsFileSrc, Object.class);
-
-          errorsRefNode =
-              jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
-        } else {
-          errorsRefNode = jsonWriter.readTree(new JSONObject(errorsFileSrc).toString());
-        }
-
+        JsonNode errorsRefNode = Utils.getNode(errorsFileSrc);
         JsonNode refErrors = errorsRefNode.get("errors");
         if (refErrors != null) {
           for (final JsonNode nodeEle : refErrors) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java
index cfa207d..a02fdf4 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java
@@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.serverlessworkflow.api.events.EventDefinition;
 import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
 import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@ import io.serverlessworkflow.api.workflow.Events;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,21 +67,9 @@ public class EventsDeserializer extends StdDeserializer<Events> {
     } else {
       String eventsFileDef = node.asText();
       String eventsFileSrc = Utils.getResourceFileAsString(eventsFileDef);
-      JsonNode eventsRefNode;
-      ObjectMapper jsonWriter = new ObjectMapper();
       if (eventsFileSrc != null && eventsFileSrc.trim().length() > 0) {
         // if its a yaml def convert to json first
-        if (!eventsFileSrc.trim().startsWith("{")) {
-          // convert yaml to json to validate
-          ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-          Object obj = yamlReader.readValue(eventsFileSrc, Object.class);
-
-          eventsRefNode =
-              jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
-        } else {
-          eventsRefNode = jsonWriter.readTree(new JSONObject(eventsFileSrc).toString());
-        }
-
+        JsonNode eventsRefNode = Utils.getNode(eventsFileSrc);
         JsonNode refEvents = eventsRefNode.get("events");
         if (refEvents != null) {
           for (final JsonNode nodeEle : refEvents) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java
index c27e2c4..b706b2d 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java
@@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.serverlessworkflow.api.functions.FunctionDefinition;
 import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
 import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@ import io.serverlessworkflow.api.workflow.Functions;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,20 +67,9 @@ public class FunctionsDeserializer extends StdDeserializer<Functions> {
       String functionsFileDef = node.asText();
       String functionsFileSrc = Utils.getResourceFileAsString(functionsFileDef);
       JsonNode functionsRefNode;
-      ObjectMapper jsonWriter = new ObjectMapper();
       if (functionsFileSrc != null && functionsFileSrc.trim().length() > 0) {
         // if its a yaml def convert to json first
-        if (!functionsFileSrc.trim().startsWith("{")) {
-          // convert yaml to json to validate
-          ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-          Object obj = yamlReader.readValue(functionsFileSrc, Object.class);
-
-          functionsRefNode =
-              jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
-        } else {
-          functionsRefNode = jsonWriter.readTree(new JSONObject(functionsFileSrc).toString());
-        }
-
+        functionsRefNode = Utils.getNode(functionsFileSrc);
         JsonNode refFunctions = functionsRefNode.get("functions");
         if (refFunctions != null) {
           for (final JsonNode nodeEle : refFunctions) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java
index ff2fe44..9eb47b5 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java
@@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
 import io.serverlessworkflow.api.retry.RetryDefinition;
 import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@ import io.serverlessworkflow.api.workflow.Retries;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,21 +67,8 @@ public class RetriesDeserializer extends StdDeserializer<Retries> {
     } else {
       String retriesFileDef = node.asText();
       String retriesFileSrc = Utils.getResourceFileAsString(retriesFileDef);
-      JsonNode retriesRefNode;
-      ObjectMapper jsonWriter = new ObjectMapper();
       if (retriesFileSrc != null && retriesFileSrc.trim().length() > 0) {
-        // if its a yaml def convert to json first
-        if (!retriesFileSrc.trim().startsWith("{")) {
-          // convert yaml to json to validate
-          ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-          Object obj = yamlReader.readValue(retriesFileSrc, Object.class);
-
-          retriesRefNode =
-              jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
-        } else {
-          retriesRefNode = jsonWriter.readTree(new JSONObject(retriesFileSrc).toString());
-        }
-
+        JsonNode retriesRefNode = Utils.getNode(retriesFileSrc);
         JsonNode refRetries = retriesRefNode.get("retries");
         if (refRetries != null) {
           for (final JsonNode nodeEle : refRetries) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java
index e9ec05e..60cc2a8 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java
@@ -18,16 +18,13 @@ package io.serverlessworkflow.api.deserializers;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
 import io.serverlessworkflow.api.utils.Utils;
 import io.serverlessworkflow.api.workflow.Secrets;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,21 +63,9 @@ public class SecretsDeserializer extends StdDeserializer<Secrets> {
     } else {
       String secretsFileDef = node.asText();
       String secretsFileSrc = Utils.getResourceFileAsString(secretsFileDef);
-      JsonNode secretsRefNode;
-      ObjectMapper jsonWriter = new ObjectMapper();
       if (secretsFileSrc != null && secretsFileSrc.trim().length() > 0) {
         // if its a yaml def convert to json first
-        if (!secretsFileSrc.trim().startsWith("{")) {
-          // convert yaml to json to validate
-          ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-          Object obj = yamlReader.readValue(secretsFileSrc, Object.class);
-
-          secretsRefNode =
-              jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
-        } else {
-          secretsRefNode = jsonWriter.readTree(new JSONObject(secretsFileSrc).toString());
-        }
-
+        JsonNode secretsRefNode = Utils.getNode(secretsFileSrc);
         JsonNode refSecrets = secretsRefNode.get("secrets");
         if (refSecrets != null) {
           for (final JsonNode nodeEle : refSecrets) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/mapper/JsonObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/mapper/JsonObjectMapperFactory.java
new file mode 100644
index 0000000..eb34b0e
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/api/mapper/JsonObjectMapperFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.api.mapper;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonObjectMapperFactory {
+
+  private static final ObjectMapper instance = new JsonObjectMapper();
+
+  public static final ObjectMapper mapper() {
+    return instance;
+  }
+}
diff --git a/api/src/main/java/io/serverlessworkflow/api/mapper/YamlObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/mapper/YamlObjectMapperFactory.java
new file mode 100644
index 0000000..04371db
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/api/mapper/YamlObjectMapperFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.api.mapper;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class YamlObjectMapperFactory {
+
+  private static final ObjectMapper instance = new YamlObjectMapper();
+
+  public static final ObjectMapper mapper() {
+    return instance;
+  }
+}
diff --git a/api/src/main/java/io/serverlessworkflow/api/schemaclient/ResourceSchemaClient.java b/api/src/main/java/io/serverlessworkflow/api/schemaclient/ResourceSchemaClient.java
deleted file mode 100644
index a4da238..0000000
--- a/api/src/main/java/io/serverlessworkflow/api/schemaclient/ResourceSchemaClient.java
+++ /dev/null
@@ -1,38 +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.api.schemaclient;
-
-import java.io.InputStream;
-import java.util.Objects;
-import org.everit.json.schema.loader.SchemaClient;
-
-public class ResourceSchemaClient implements SchemaClient {
-
-  @SuppressWarnings("unused")
-  private final SchemaClient fallbackClient;
-
-  private final String baseResourcePath = "/schema/";
-
-  public ResourceSchemaClient(SchemaClient fallbackClient) {
-    this.fallbackClient = Objects.requireNonNull(fallbackClient, "fallbackClient cannot be null");
-  }
-
-  @Override
-  public InputStream get(String path) {
-    path = path.substring("https://wg-serverless.org/".length());
-    return this.getClass().getResourceAsStream(baseResourcePath + path);
-  }
-}
diff --git a/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java b/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java
index 3e4b427..9bdce41 100644
--- a/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java
+++ b/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java
@@ -15,6 +15,11 @@
  */
 package io.serverlessworkflow.api.utils;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.serverlessworkflow.api.mapper.JsonObjectMapperFactory;
+import io.serverlessworkflow.api.mapper.YamlObjectMapperFactory;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
@@ -34,4 +39,14 @@ public class Utils {
       }
     }
   }
+
+  public static ObjectMapper getObjectMapper(String source) {
+    return !source.trim().startsWith("{")
+        ? YamlObjectMapperFactory.mapper()
+        : JsonObjectMapperFactory.mapper();
+  }
+
+  public static JsonNode getNode(String source) throws JsonProcessingException {
+    return getObjectMapper(source).readTree(source);
+  }
 }
diff --git a/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java b/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java
index 830bb50..847380f 100644
--- a/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java
+++ b/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java
@@ -15,26 +15,23 @@
  */
 package io.serverlessworkflow.api.validation;
 
-import io.serverlessworkflow.api.schemaclient.ResourceSchemaClient;
-import org.everit.json.schema.Schema;
-import org.everit.json.schema.loader.SchemaLoader;
-import org.everit.json.schema.loader.internal.DefaultSchemaClient;
-import org.json.JSONObject;
-import org.json.JSONTokener;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.io.UncheckedIOException;
 
 public class WorkflowSchemaLoader {
-  private static final JSONObject workflowSchema =
-      new JSONObject(
-          new JSONTokener(WorkflowSchemaLoader.class.getResourceAsStream("/schema/workflow.json")));
 
-  public static Schema getWorkflowSchema() {
-    SchemaLoader schemaLoader =
-        SchemaLoader.builder()
-            .schemaClient(new ResourceSchemaClient(new DefaultSchemaClient()))
-            .schemaJson(workflowSchema)
-            .resolutionScope("classpath:schema")
-            .draftV7Support()
-            .build();
-    return schemaLoader.load().build();
+  public static JsonNode getWorkflowSchema() {
+    try {
+      return ObjectMapperHolder.objectMapper.readTree(
+          WorkflowSchemaLoader.class.getResourceAsStream("/schema/workflow.json"));
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  private static class ObjectMapperHolder {
+    public static final ObjectMapper objectMapper = new ObjectMapper();
   }
 }
diff --git a/api/src/main/resources/schema/workflow.json b/api/src/main/resources/schema/workflow.json
index 9ea8d6b..dbb462e 100644
--- a/api/src/main/resources/schema/workflow.json
+++ b/api/src/main/resources/schema/workflow.json
@@ -1,5 +1,5 @@
 {
-  "$id": "https://wg-serverless.org/workflow.schema.json",
+  "$id": "classpath:schema/workflow.schema.json",
   "$schema": "http://json-schema.org/draft-07/schema#",
   "description": "Serverless Workflow is a vendor-neutral specification for defining the model of workflows responsible for orchestrating event-driven serverless applications.",
   "type": "object",
diff --git a/diagram/pom.xml b/diagram/pom.xml
index 77f1342..30ceb33 100644
--- a/diagram/pom.xml
+++ b/diagram/pom.xml
@@ -11,6 +11,7 @@
 
     <artifactId>serverlessworkflow-diagram</artifactId>
     <name>Serverless Workflow :: Diagram</name>
+    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
     <description>Diagram Generation</description>
 
diff --git a/pom.xml b/pom.xml
index 5ad9e56..a8d6ded 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,9 +45,9 @@
         <version.failsafe.plugin>2.22.0</version.failsafe.plugin>
         <version.checkstyle.plugin>3.1.1</version.checkstyle.plugin>
         <version.deploy.plugin>2.8.2</version.deploy.plugin>
-
+        <version.com.networknt>1.0.86</version.com.networknt>
         <version.org.slf4j>1.7.25</version.org.slf4j>
-        <version.com.fasterxml.jackson>2.10.3</version.com.fasterxml.jackson>
+        <version.com.fasterxml.jackson>2.15.2</version.com.fasterxml.jackson>
         <version.javax.validation>2.0.1.Final</version.javax.validation>
         <version.org.junit.minor>6.0</version.org.junit.minor>
         <version.org.junit>5.${version.org.junit.minor}</version.org.junit>
@@ -60,7 +60,6 @@
         <hamcrest.version>1.3</hamcrest.version>
         <jsonassert.version>1.5.0</jsonassert.version>
         <json.schema.validation.version>1.14.1</json.schema.validation.version>
-        <version.json>20230618</version.json>
         <version.thymeleaf>3.0.11.RELEASE</version.thymeleaf>
         <version.plantuml>8059</version.plantuml>
         <version.graphviz>0.17.0</version.graphviz>
@@ -111,6 +110,17 @@
                 <groupId>com.fasterxml.jackson.core</groupId>
                 <artifactId>jackson-databind</artifactId>
                 <version>${version.com.fasterxml.jackson}</version>
+            </dependency>
+             <dependency>
+               <groupId>com.networknt</groupId>
+               <artifactId>json-schema-validator</artifactId>
+               <version>${version.com.networknt}</version>
+               <exclusions>
+                 <exclusion>
+                   <groupId>org.apache.commons</groupId>
+                   <artifactId>commons-lang3</artifactId>
+                 </exclusion>
+              </exclusions>
             </dependency>
             <dependency>
                 <groupId>com.fasterxml.jackson.dataformat</groupId>
@@ -127,22 +137,6 @@
                 <artifactId>commons-lang3</artifactId>
                 <version>${commons.lang.version}</version>
             </dependency>
-            <dependency>
-                <groupId>com.github.erosb</groupId>
-                <artifactId>everit-json-schema</artifactId>
-                <version>${json.schema.validation.version}</version>
-                <exclusions>
-                    <exclusion>
-                        <groupId>commons-logging</groupId>
-                        <artifactId>commons-logging</artifactId>
-                    </exclusion>
-                </exclusions>
-            </dependency>
-            <dependency>
-                <groupId>org.json</groupId>
-                <artifactId>json</artifactId>
-                <version>${version.json}</version>
-            </dependency>
             <dependency>
                 <groupId>org.thymeleaf</groupId>
                 <artifactId>thymeleaf</artifactId>
diff --git a/spi/pom.xml b/spi/pom.xml
index 5846e0c..2c61c88 100644
--- a/spi/pom.xml
+++ b/spi/pom.xml
@@ -11,6 +11,7 @@
 
     <artifactId>serverlessworkflow-spi</artifactId>
     <name>Serverless Workflow :: SPI</name>
+    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
     <description>Java SDK for Serverless Workflow Specification</description>
 
diff --git a/utils/pom.xml b/utils/pom.xml
index 9a4f414..c56695f 100644
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -11,6 +11,7 @@
 
     <artifactId>serverlessworkflow-util</artifactId>
     <name>Serverless Workflow :: Utils</name>
+    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
     <description>Java SDK for Serverless Workflow Specification</description>
 
diff --git a/validation/pom.xml b/validation/pom.xml
index a581979..750947e 100644
--- a/validation/pom.xml
+++ b/validation/pom.xml
@@ -11,6 +11,7 @@
 
     <artifactId>serverlessworkflow-validation</artifactId>
     <name>Serverless Workflow :: Validation</name>
+    <version>${project.parent.version}</version>
     <packaging>jar</packaging>
     <description>Java SDK for Serverless Workflow Specification</description>
 
@@ -34,12 +35,10 @@
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>com.github.erosb</groupId>
-            <artifactId>everit-json-schema</artifactId>
+          <groupId>com.networknt</groupId>
+          <artifactId>json-schema-validator</artifactId>
         </dependency>
-
         <!-- test -->
         <dependency>
             <groupId>org.junit.jupiter</groupId>
diff --git a/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java b/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java
index abef046..84dbb43 100644
--- a/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java
+++ b/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java
@@ -15,8 +15,9 @@
  */
 package io.serverlessworkflow.validation;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SpecVersion.VersionFlag;
 import io.serverlessworkflow.api.Workflow;
 import io.serverlessworkflow.api.actions.Action;
 import io.serverlessworkflow.api.events.EventDefinition;
@@ -27,15 +28,14 @@ import io.serverlessworkflow.api.interfaces.WorkflowValidator;
 import io.serverlessworkflow.api.states.*;
 import io.serverlessworkflow.api.switchconditions.DataCondition;
 import io.serverlessworkflow.api.switchconditions.EventCondition;
+import io.serverlessworkflow.api.utils.Utils;
 import io.serverlessworkflow.api.validation.ValidationError;
 import io.serverlessworkflow.api.validation.WorkflowSchemaLoader;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import org.everit.json.schema.Schema;
-import org.everit.json.schema.ValidationException;
-import org.json.JSONObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,7 +44,7 @@ public class WorkflowValidatorImpl implements WorkflowValidator {
   private static final Logger logger = LoggerFactory.getLogger(WorkflowValidatorImpl.class);
   private boolean schemaValidationEnabled = true;
   private List<ValidationError> validationErrors = new ArrayList<>();
-  private Schema workflowSchema = WorkflowSchemaLoader.getWorkflowSchema();
+  private JsonNode workflowSchema = WorkflowSchemaLoader.getWorkflowSchema();
   private String source;
   private Workflow workflow;
 
@@ -66,34 +66,13 @@ public class WorkflowValidatorImpl implements WorkflowValidator {
     if (workflow == null) {
       try {
         if (schemaValidationEnabled && source != null) {
-          try {
-            if (!source.trim().startsWith("{")) {
-              // convert yaml to json to validate
-              ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
-              Object obj = yamlReader.readValue(source, Object.class);
-
-              ObjectMapper jsonWriter = new ObjectMapper();
-
-              workflowSchema.validate(new JSONObject(jsonWriter.writeValueAsString(obj)));
-            } else {
-              workflowSchema.validate(new JSONObject(source));
-            }
-          } catch (ValidationException e) {
-            e.getCausingExceptions().stream()
-                .map(ValidationException::getMessage)
-                .forEach(
-                    m -> {
-                      if ((!m.equals("#/functions: expected type: JSONObject, found: JSONArray")
-                          && !m.equals("#/events: expected type: JSONObject, found: JSONArray")
-                          && !m.equals("#/start: expected type: JSONObject, found: String")
-                          && !m.equals("#/retries: expected type: JSONObject, found: JSONArray"))) {
-                        addValidationError(m, ValidationError.SCHEMA_VALIDATION);
-                      }
-                    });
-          }
+          JsonSchemaFactory.getInstance(VersionFlag.V7)
+              .getSchema(workflowSchema)
+              .validate(Utils.getNode(source))
+              .forEach(m -> addValidationError(m.getMessage(), ValidationError.SCHEMA_VALIDATION));
         }
-      } catch (Exception e) {
-        logger.error("Schema validation exception: " + e.getMessage());
+      } catch (IOException e) {
+        logger.error("Unexpected error during validation", e);
       }
     }
 
@@ -101,258 +80,244 @@ public class WorkflowValidatorImpl implements WorkflowValidator {
     // there is no point of doing the workflow validation
     if (validationErrors.size() > 0) {
       return validationErrors;
-    } else {
-      if (workflow == null) {
-        workflow = Workflow.fromSource(source);
-      }
+    } else if (workflow == null) {
+      workflow = Workflow.fromSource(source);
+    }
 
-      List<FunctionDefinition> functions =
-          workflow.getFunctions() != null ? workflow.getFunctions().getFunctionDefs() : null;
+    List<FunctionDefinition> functions =
+        workflow.getFunctions() != null ? workflow.getFunctions().getFunctionDefs() : null;
 
-      List<EventDefinition> events =
-          workflow.getEvents() != null ? workflow.getEvents().getEventDefs() : null;
+    List<EventDefinition> events =
+        workflow.getEvents() != null ? workflow.getEvents().getEventDefs() : null;
 
-      if ((workflow.getId() == null || workflow.getId().trim().isEmpty())
-          && (workflow.getKey() == null || workflow.getKey().trim().isEmpty())) {
-        addValidationError(
-            "Workflow id or key should not be empty", ValidationError.WORKFLOW_VALIDATION);
-      }
+    if ((workflow.getId() == null || workflow.getId().trim().isEmpty())
+        && (workflow.getKey() == null || workflow.getKey().trim().isEmpty())) {
+      addValidationError(
+          "Workflow id or key should not be empty", ValidationError.WORKFLOW_VALIDATION);
+    }
 
-      if (workflow.getVersion() == null || workflow.getVersion().trim().isEmpty()) {
-        addValidationError(
-            "Workflow version should not be empty", ValidationError.WORKFLOW_VALIDATION);
-      }
+    if (workflow.getVersion() == null || workflow.getVersion().trim().isEmpty()) {
+      addValidationError(
+          "Workflow version should not be empty", ValidationError.WORKFLOW_VALIDATION);
+    }
 
-      if (workflow.getStates() == null || workflow.getStates().isEmpty()) {
-        addValidationError("No states found", ValidationError.WORKFLOW_VALIDATION);
-      }
+    if (workflow.getStates() == null || workflow.getStates().isEmpty()) {
+      addValidationError("No states found", ValidationError.WORKFLOW_VALIDATION);
+    }
 
-      if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
-        boolean existingStateWithStartProperty = false;
-        if (workflow.getStart() != null) {
-          String startProperty = workflow.getStart().getStateName();
-          for (State s : workflow.getStates()) {
-            if (s.getName().equals(startProperty)) {
-              existingStateWithStartProperty = true;
-              break;
-            }
+    if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
+      boolean existingStateWithStartProperty = false;
+      if (workflow.getStart() != null) {
+        String startProperty = workflow.getStart().getStateName();
+        for (State s : workflow.getStates()) {
+          if (s.getName().equals(startProperty)) {
+            existingStateWithStartProperty = true;
+            break;
           }
-        } else {
-          existingStateWithStartProperty = true;
-        }
-        if (!existingStateWithStartProperty) {
-          addValidationError(
-              "No state name found that matches the workflow start definition",
-              ValidationError.WORKFLOW_VALIDATION);
         }
+      } else {
+        existingStateWithStartProperty = true;
       }
+      if (!existingStateWithStartProperty) {
+        addValidationError(
+            "No state name found that matches the workflow start definition",
+            ValidationError.WORKFLOW_VALIDATION);
+      }
+    }
 
-      Validation validation = new Validation();
-      if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
-        workflow
-            .getStates()
-            .forEach(
-                s -> {
-                  if (s.getName() != null && s.getName().trim().isEmpty()) {
-                    addValidationError(
-                        "State name should not be empty", ValidationError.WORKFLOW_VALIDATION);
-                  } else {
-                    validation.addState(s.getName());
-                  }
-
-                  if (s.getEnd() != null) {
-                    validation.addEndState();
-                  }
-
-                  if (s instanceof OperationState) {
-                    OperationState operationState = (OperationState) s;
-
-                    List<Action> actions = operationState.getActions();
-                    for (Action action : actions) {
-                      if (action.getFunctionRef() != null) {
-                        if (action.getFunctionRef().getRefName().isEmpty()) {
-                          addValidationError(
-                              "Operation State action functionRef should not be null or empty",
-                              ValidationError.WORKFLOW_VALIDATION);
-                        }
-
-                        if (!haveFunctionDefinition(
-                            action.getFunctionRef().getRefName(), functions)) {
-                          addValidationError(
-                              "Operation State action functionRef does not reference an existing workflow function definition",
-                              ValidationError.WORKFLOW_VALIDATION);
-                        }
+    Validation validation = new Validation();
+    if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
+      workflow
+          .getStates()
+          .forEach(
+              s -> {
+                if (s.getName() != null && s.getName().trim().isEmpty()) {
+                  addValidationError(
+                      "State name should not be empty", ValidationError.WORKFLOW_VALIDATION);
+                } else {
+                  validation.addState(s.getName());
+                }
+
+                if (s.getEnd() != null) {
+                  validation.addEndState();
+                }
+
+                if (s instanceof OperationState) {
+                  OperationState operationState = (OperationState) s;
+
+                  List<Action> actions = operationState.getActions();
+                  for (Action action : actions) {
+                    if (action.getFunctionRef() != null) {
+                      if (action.getFunctionRef().getRefName().isEmpty()) {
+                        addValidationError(
+                            "Operation State action functionRef should not be null or empty",
+                            ValidationError.WORKFLOW_VALIDATION);
                       }
 
-                      if (action.getEventRef() != null) {
-                        if (action.getEventRef().getTriggerEventRef().isEmpty()) {
-                          addValidationError(
-                              "Operation State action trigger eventRef does not reference an existing workflow event definition",
-                              ValidationError.WORKFLOW_VALIDATION);
-                        }
-
-                        if (action.getEventRef().getResultEventRef().isEmpty()) {
-                          addValidationError(
-                              "Operation State action results eventRef does not reference an existing workflow event definition",
-                              ValidationError.WORKFLOW_VALIDATION);
-                        }
-
-                        if (!haveEventsDefinition(
-                            action.getEventRef().getTriggerEventRef(), events)) {
-                          addValidationError(
-                              "Operation State action trigger event def does not reference an existing workflow event definition",
-                              ValidationError.WORKFLOW_VALIDATION);
-                        }
-
-                        if (!haveEventsDefinition(
-                            action.getEventRef().getResultEventRef(), events)) {
-                          addValidationError(
-                              "Operation State action results event def does not reference an existing workflow event definition",
-                              ValidationError.WORKFLOW_VALIDATION);
-                        }
+                      if (!haveFunctionDefinition(
+                          action.getFunctionRef().getRefName(), functions)) {
+                        addValidationError(
+                            "Operation State action functionRef does not reference an existing workflow function definition",
+                            ValidationError.WORKFLOW_VALIDATION);
                       }
                     }
-                  }
 
-                  if (s instanceof EventState) {
-                    EventState eventState = (EventState) s;
-                    if (eventState.getOnEvents() == null || eventState.getOnEvents().size() < 1) {
-                      addValidationError(
-                          "Event State has no eventActions defined",
-                          ValidationError.WORKFLOW_VALIDATION);
-                    }
-                    List<OnEvents> eventsActionsList = eventState.getOnEvents();
-                    for (OnEvents onEvents : eventsActionsList) {
+                    if (action.getEventRef() != null) {
 
-                      List<String> eventRefs = onEvents.getEventRefs();
-                      if (eventRefs == null || eventRefs.size() < 1) {
+                      if (!haveEventsDefinition(
+                          action.getEventRef().getTriggerEventRef(), events)) {
                         addValidationError(
-                            "Event State eventsActions has no event refs",
+                            "Operation State action trigger event def does not reference an existing workflow event definition",
+                            ValidationError.WORKFLOW_VALIDATION);
+                      }
+
+                      if (!haveEventsDefinition(action.getEventRef().getResultEventRef(), events)) {
+                        addValidationError(
+                            "Operation State action results event def does not reference an existing workflow event definition",
                             ValidationError.WORKFLOW_VALIDATION);
-                      } else {
-                        for (String eventRef : eventRefs) {
-                          if (!haveEventsDefinition(eventRef, events)) {
-                            addValidationError(
-                                "Event State eventsActions eventRef does not match a declared workflow event definition",
-                                ValidationError.WORKFLOW_VALIDATION);
-                          }
-                        }
                       }
                     }
                   }
+                }
 
-                  if (s instanceof SwitchState) {
-                    SwitchState switchState = (SwitchState) s;
-                    if ((switchState.getDataConditions() == null
-                            || switchState.getDataConditions().size() < 1)
-                        && (switchState.getEventConditions() == null
-                            || switchState.getEventConditions().size() < 1)) {
-                      addValidationError(
-                          "Switch state should define either data or event conditions",
-                          ValidationError.WORKFLOW_VALIDATION);
-                    }
+                if (s instanceof EventState) {
+                  EventState eventState = (EventState) s;
+                  if (eventState.getOnEvents() == null || eventState.getOnEvents().size() < 1) {
+                    addValidationError(
+                        "Event State has no eventActions defined",
+                        ValidationError.WORKFLOW_VALIDATION);
+                  }
+                  List<OnEvents> eventsActionsList = eventState.getOnEvents();
+                  for (OnEvents onEvents : eventsActionsList) {
 
-                    if (switchState.getDefaultCondition() == null) {
+                    List<String> eventRefs = onEvents.getEventRefs();
+                    if (eventRefs == null || eventRefs.size() < 1) {
                       addValidationError(
-                          "Switch state should define a default transition",
+                          "Event State eventsActions has no event refs",
                           ValidationError.WORKFLOW_VALIDATION);
-                    }
-
-                    if (switchState.getEventConditions() != null
-                        && switchState.getEventConditions().size() > 0) {
-                      List<EventCondition> eventConditions = switchState.getEventConditions();
-                      for (EventCondition ec : eventConditions) {
-                        if (!haveEventsDefinition(ec.getEventRef(), events)) {
+                    } else {
+                      for (String eventRef : eventRefs) {
+                        if (!haveEventsDefinition(eventRef, events)) {
                           addValidationError(
-                              "Switch state event condition eventRef does not reference a defined workflow event",
+                              "Event State eventsActions eventRef does not match a declared workflow event definition",
                               ValidationError.WORKFLOW_VALIDATION);
                         }
-                        if (ec.getEnd() != null) {
-                          validation.addEndState();
-                        }
                       }
                     }
+                  }
+                }
+
+                if (s instanceof SwitchState) {
+                  SwitchState switchState = (SwitchState) s;
+                  if ((switchState.getDataConditions() == null
+                          || switchState.getDataConditions().size() < 1)
+                      && (switchState.getEventConditions() == null
+                          || switchState.getEventConditions().size() < 1)) {
+                    addValidationError(
+                        "Switch state should define either data or event conditions",
+                        ValidationError.WORKFLOW_VALIDATION);
+                  }
 
-                    if (switchState.getDataConditions() != null
-                        && switchState.getDataConditions().size() > 0) {
-                      List<DataCondition> dataConditions = switchState.getDataConditions();
-                      for (DataCondition dc : dataConditions) {
-                        if (dc.getEnd() != null) {
-                          validation.addEndState();
-                        }
+                  if (switchState.getDefaultCondition() == null) {
+                    addValidationError(
+                        "Switch state should define a default transition",
+                        ValidationError.WORKFLOW_VALIDATION);
+                  }
+
+                  if (switchState.getEventConditions() != null
+                      && switchState.getEventConditions().size() > 0) {
+                    List<EventCondition> eventConditions = switchState.getEventConditions();
+                    for (EventCondition ec : eventConditions) {
+                      if (!haveEventsDefinition(ec.getEventRef(), events)) {
+                        addValidationError(
+                            "Switch state event condition eventRef does not reference a defined workflow event",
+                            ValidationError.WORKFLOW_VALIDATION);
+                      }
+                      if (ec.getEnd() != null) {
+                        validation.addEndState();
                       }
                     }
                   }
 
-                  if (s instanceof SleepState) {
-                    SleepState sleepState = (SleepState) s;
-                    if (sleepState.getDuration() == null || sleepState.getDuration().length() < 1) {
-                      addValidationError(
-                          "Sleep state should have a non-empty time delay",
-                          ValidationError.WORKFLOW_VALIDATION);
+                  if (switchState.getDataConditions() != null
+                      && switchState.getDataConditions().size() > 0) {
+                    List<DataCondition> dataConditions = switchState.getDataConditions();
+                    for (DataCondition dc : dataConditions) {
+                      if (dc.getEnd() != null) {
+                        validation.addEndState();
+                      }
                     }
                   }
+                }
+
+                if (s instanceof SleepState) {
+                  SleepState sleepState = (SleepState) s;
+                  if (sleepState.getDuration() == null || sleepState.getDuration().length() < 1) {
+                    addValidationError(
+                        "Sleep state should have a non-empty time delay",
+                        ValidationError.WORKFLOW_VALIDATION);
+                  }
+                }
 
-                  if (s instanceof ParallelState) {
-                    ParallelState parallelState = (ParallelState) s;
+                if (s instanceof ParallelState) {
+                  ParallelState parallelState = (ParallelState) s;
 
-                    if (parallelState.getBranches() == null
-                        || parallelState.getBranches().size() < 2) {
-                      addValidationError(
-                          "Parallel state should have at lest two branches",
-                          ValidationError.WORKFLOW_VALIDATION);
-                    }
+                  if (parallelState.getBranches() == null
+                      || parallelState.getBranches().size() < 2) {
+                    addValidationError(
+                        "Parallel state should have at lest two branches",
+                        ValidationError.WORKFLOW_VALIDATION);
                   }
+                }
 
-                  if (s instanceof InjectState) {
-                    InjectState injectState = (InjectState) s;
-                    if (injectState.getData() == null || injectState.getData().isEmpty()) {
-                      addValidationError(
-                          "InjectState should have non-null data",
-                          ValidationError.WORKFLOW_VALIDATION);
-                    }
+                if (s instanceof InjectState) {
+                  InjectState injectState = (InjectState) s;
+                  if (injectState.getData() == null || injectState.getData().isEmpty()) {
+                    addValidationError(
+                        "InjectState should have non-null data",
+                        ValidationError.WORKFLOW_VALIDATION);
                   }
+                }
 
-                  if (s instanceof ForEachState) {
-                    ForEachState forEachState = (ForEachState) s;
-                    if (forEachState.getInputCollection() == null
-                        || forEachState.getInputCollection().isEmpty()) {
-                      addValidationError(
-                          "ForEach state should have a valid inputCollection",
-                          ValidationError.WORKFLOW_VALIDATION);
-                    }
+                if (s instanceof ForEachState) {
+                  ForEachState forEachState = (ForEachState) s;
+                  if (forEachState.getInputCollection() == null
+                      || forEachState.getInputCollection().isEmpty()) {
+                    addValidationError(
+                        "ForEach state should have a valid inputCollection",
+                        ValidationError.WORKFLOW_VALIDATION);
                   }
+                }
 
-                  if (s instanceof CallbackState) {
-                    CallbackState callbackState = (CallbackState) s;
+                if (s instanceof CallbackState) {
+                  CallbackState callbackState = (CallbackState) s;
 
-                    if (!haveEventsDefinition(callbackState.getEventRef(), events)) {
-                      addValidationError(
-                          "CallbackState event ref does not reference a defined workflow event definition",
-                          ValidationError.WORKFLOW_VALIDATION);
-                    }
+                  if (!haveEventsDefinition(callbackState.getEventRef(), events)) {
+                    addValidationError(
+                        "CallbackState event ref does not reference a defined workflow event definition",
+                        ValidationError.WORKFLOW_VALIDATION);
+                  }
 
-                    if (!haveFunctionDefinition(
-                        callbackState.getAction().getFunctionRef().getRefName(), functions)) {
-                      addValidationError(
-                          "CallbackState action function ref does not reference a defined workflow function definition",
-                          ValidationError.WORKFLOW_VALIDATION);
-                    }
+                  if (!haveFunctionDefinition(
+                      callbackState.getAction().getFunctionRef().getRefName(), functions)) {
+                    addValidationError(
+                        "CallbackState action function ref does not reference a defined workflow function definition",
+                        ValidationError.WORKFLOW_VALIDATION);
                   }
-                });
+                }
+              });
 
-        if (validation.endStates == 0) {
-          addValidationError("No end state found.", ValidationError.WORKFLOW_VALIDATION);
-        }
+      if (validation.endStates == 0) {
+        addValidationError("No end state found.", ValidationError.WORKFLOW_VALIDATION);
       }
-
-      return validationErrors;
     }
+
+    return validationErrors;
   }
 
   @Override
   public boolean isValid() {
-    return validate().size() < 1;
+    return validate().isEmpty();
   }
 
   @Override
@@ -381,17 +346,25 @@ public class WorkflowValidatorImpl implements WorkflowValidator {
   }
 
   private boolean haveEventsDefinition(String eventName, List<EventDefinition> events) {
+    if (eventName == null) {
+      return true;
+    }
     if (events != null) {
       EventDefinition eve =
           events.stream().filter(e -> e.getName().equals(eventName)).findFirst().orElse(null);
-
       return eve == null ? false : true;
     } else {
       return false;
     }
   }
 
+  private static final Set<String> skipMessages =
+      Set.of("$.start: string found, object expected", "$.functions: array found, object expected");
+
   private void addValidationError(String message, String type) {
+    if (skipMessages.contains(message)) {
+      return;
+    }
     ValidationError mainError = new ValidationError();
     mainError.setMessage(message);
     mainError.setType(type);
@@ -399,30 +372,9 @@ public class WorkflowValidatorImpl implements WorkflowValidator {
   }
 
   private class Validation {
-
-    final Set<String> events = new HashSet<>();
-    final Set<String> functions = new HashSet<>();
     final Set<String> states = new HashSet<>();
     Integer endStates = 0;
 
-    void addFunction(String name) {
-      if (functions.contains(name)) {
-        addValidationError(
-            "Function does not have an unique name: " + name, ValidationError.WORKFLOW_VALIDATION);
-      } else {
-        functions.add(name);
-      }
-    }
-
-    void addEvent(String name) {
-      if (events.contains(name)) {
-        addValidationError(
-            "Event does not have an unique name: " + name, ValidationError.WORKFLOW_VALIDATION);
-      } else {
-        events.add(name);
-      }
-    }
-
     void addState(String name) {
       if (states.contains(name)) {
         addValidationError(


Test patch:
diff --git a/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java b/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java
index a81e14f..e600e25 100644
--- a/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java
+++ b/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java
@@ -15,14 +15,26 @@
  */
 package io.serverlessworkflow.validation.test;
 
+import static io.serverlessworkflow.api.states.DefaultState.Type.OPERATION;
 import static io.serverlessworkflow.api.states.DefaultState.Type.SLEEP;
 
 import io.serverlessworkflow.api.Workflow;
+import io.serverlessworkflow.api.actions.Action;
 import io.serverlessworkflow.api.end.End;
+import io.serverlessworkflow.api.events.EventDefinition;
+import io.serverlessworkflow.api.events.EventRef;
+import io.serverlessworkflow.api.functions.FunctionDefinition;
+import io.serverlessworkflow.api.functions.FunctionDefinition.Type;
+import io.serverlessworkflow.api.functions.FunctionRef;
 import io.serverlessworkflow.api.interfaces.WorkflowValidator;
+import io.serverlessworkflow.api.retry.RetryDefinition;
 import io.serverlessworkflow.api.start.Start;
+import io.serverlessworkflow.api.states.OperationState;
 import io.serverlessworkflow.api.states.SleepState;
 import io.serverlessworkflow.api.validation.ValidationError;
+import io.serverlessworkflow.api.workflow.Events;
+import io.serverlessworkflow.api.workflow.Functions;
+import io.serverlessworkflow.api.workflow.Retries;
 import io.serverlessworkflow.validation.WorkflowValidatorImpl;
 import java.util.Arrays;
 import java.util.List;
@@ -46,7 +58,6 @@ public class WorkflowValidationTest {
     List<ValidationError> validationErrors =
         workflowValidator.setSource("---\n" + "key: abc\n").validate();
     Assertions.assertNotNull(validationErrors);
-    System.out.println(validationErrors);
     Assertions.assertEquals(3, validationErrors.size());
   }
 
@@ -162,7 +173,6 @@ public class WorkflowValidationTest {
     Assertions.assertNotNull(validationErrors);
     Assertions.assertEquals(1, validationErrors.size());
 
-    // validationErrors.stream().forEach(v -> System.out.println(v.toString()));
     Assertions.assertEquals(
         "Operation State action functionRef does not reference an existing workflow function definition",
         validationErrors.get(0).getMessage());
@@ -280,4 +290,48 @@ public class WorkflowValidationTest {
         "CallbackState action function ref does not reference a defined workflow function definition",
         validationErrors.get(1).getMessage());
   }
+
+  @Test
+  void testFunctionCall() {
+    Workflow workflow =
+        new Workflow()
+            .withId("test-workflow")
+            .withVersion("1.0")
+            .withStart(new Start().withStateName("start"))
+            .withFunctions(
+                new Functions(
+                    Arrays.asList(new FunctionDefinition("expression").withType(Type.EXPRESSION))))
+            .withStates(
+                Arrays.asList(
+                    new OperationState()
+                        .withName("start")
+                        .withType(OPERATION)
+                        .withActions(
+                            Arrays.asList(
+                                new Action().withFunctionRef(new FunctionRef("expression"))))
+                        .withEnd(new End())));
+    Assertions.assertTrue(new WorkflowValidatorImpl().setWorkflow(workflow).validate().isEmpty());
+  }
+
+  @Test
+  void testEventCall() {
+    Workflow workflow =
+        new Workflow()
+            .withId("test-workflow")
+            .withVersion("1.0")
+            .withStart(new Start().withStateName("start"))
+            .withEvents(new Events(Arrays.asList(new EventDefinition().withName("event"))))
+            .withRetries(new Retries(Arrays.asList(new RetryDefinition("start", "PT1S"))))
+            .withStates(
+                Arrays.asList(
+                    new OperationState()
+                        .withName("start")
+                        .withType(OPERATION)
+                        .withActions(
+                            Arrays.asList(
+                                new Action()
+                                    .withEventRef(new EventRef().withTriggerEventRef("event"))))
+                        .withEnd(new End())));
+    Assertions.assertTrue(new WorkflowValidatorImpl().setWorkflow(workflow).validate().isEmpty());
+  }
 }